Utilisation d'une css différentes pour autoriser des customisation parfois nécessaires…
[ckeditor.git] / skins / ckeditor / _source / plugins / dialogui / plugin.js
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
5
6 /** @fileoverview The "dialogui" plugin. */
7
8 CKEDITOR.plugins.add( 'dialogui' );
9
10 (function()
11 {
12 var initPrivateObject = function( elementDefinition )
13 {
14 this._ || ( this._ = {} );
15 this._['default'] = this._.initValue = elementDefinition['default'] || '';
16 this._.required = elementDefinition[ 'required' ] || false;
17 var args = [ this._ ];
18 for ( var i = 1 ; i < arguments.length ; i++ )
19 args.push( arguments[i] );
20 args.push( true );
21 CKEDITOR.tools.extend.apply( CKEDITOR.tools, args );
22 return this._;
23 },
24 textBuilder =
25 {
26 build : function( dialog, elementDefinition, output )
27 {
28 return new CKEDITOR.ui.dialog.textInput( dialog, elementDefinition, output );
29 }
30 },
31 commonBuilder =
32 {
33 build : function( dialog, elementDefinition, output )
34 {
35 return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output );
36 }
37 },
38 containerBuilder =
39 {
40 build : function( dialog, elementDefinition, output )
41 {
42 var children = elementDefinition.children,
43 child,
44 childHtmlList = [],
45 childObjList = [];
46 for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ )
47 {
48 var childHtml = [];
49 childHtmlList.push( childHtml );
50 childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) );
51 }
52 return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition );
53 }
54 },
55 commonPrototype =
56 {
57 isChanged : function()
58 {
59 return this.getValue() != this.getInitValue();
60 },
61
62 reset : function( noChangeEvent )
63 {
64 this.setValue( this.getInitValue(), noChangeEvent );
65 },
66
67 setInitValue : function()
68 {
69 this._.initValue = this.getValue();
70 },
71
72 resetInitValue : function()
73 {
74 this._.initValue = this._['default'];
75 },
76
77 getInitValue : function()
78 {
79 return this._.initValue;
80 }
81 },
82 commonEventProcessors = CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
83 {
84 onChange : function( dialog, func )
85 {
86 if ( !this._.domOnChangeRegistered )
87 {
88 dialog.on( 'load', function()
89 {
90 this.getInputElement().on( 'change', function()
91 {
92 // Make sure 'onchange' doesn't get fired after dialog closed. (#5719)
93 if ( !dialog.parts.dialog.isVisible() )
94 return;
95
96 this.fire( 'change', { value : this.getValue() } );
97 }, this );
98 }, this );
99 this._.domOnChangeRegistered = true;
100 }
101
102 this.on( 'change', func );
103 }
104 }, true ),
105 eventRegex = /^on([A-Z]\w+)/,
106 cleanInnerDefinition = function( def )
107 {
108 // An inner UI element should not have the parent's type, title or events.
109 for ( var i in def )
110 {
111 if ( eventRegex.test( i ) || i == 'title' || i == 'type' )
112 delete def[i];
113 }
114 return def;
115 };
116
117 CKEDITOR.tools.extend( CKEDITOR.ui.dialog,
118 /** @lends CKEDITOR.ui.dialog */
119 {
120 /**
121 * Base class for all dialog elements with a textual label on the left.
122 * @constructor
123 * @example
124 * @extends CKEDITOR.ui.dialog.uiElement
125 * @param {CKEDITOR.dialog} dialog
126 * Parent dialog object.
127 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
128 * The element definition. Accepted fields:
129 * <ul>
130 * <li><strong>label</strong> (Required) The label string.</li>
131 * <li><strong>labelLayout</strong> (Optional) Put 'horizontal' here if the
132 * label element is to be layed out horizontally. Otherwise a vertical
133 * layout will be used.</li>
134 * <li><strong>widths</strong> (Optional) This applies only for horizontal
135 * layouts - an 2-element array of lengths to specify the widths of the
136 * label and the content element.</li>
137 * </ul>
138 * @param {Array} htmlList
139 * List of HTML code to output to.
140 * @param {Function} contentHtml
141 * A function returning the HTML code string to be added inside the content
142 * cell.
143 */
144 labeledElement : function( dialog, elementDefinition, htmlList, contentHtml )
145 {
146 if ( arguments.length < 4 )
147 return;
148
149 var _ = initPrivateObject.call( this, elementDefinition );
150 _.labelId = CKEDITOR.tools.getNextId() + '_label';
151 var children = this._.children = [];
152 /** @ignore */
153 var innerHTML = function()
154 {
155 var html = [],
156 requiredClass = elementDefinition.required ? ' cke_required' : '' ;
157 if ( elementDefinition.labelLayout != 'horizontal' )
158 html.push( '<label class="cke_dialog_ui_labeled_label' + requiredClass + '" ',
159 ' id="'+ _.labelId + '"',
160 ' for="' + _.inputId + '"',
161 ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>',
162 elementDefinition.label,
163 '</label>',
164 '<div class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + ' role="presentation">',
165 contentHtml.call( this, dialog, elementDefinition ),
166 '</div>' );
167 else
168 {
169 var hboxDefinition = {
170 type : 'hbox',
171 widths : elementDefinition.widths,
172 padding : 0,
173 children :
174 [
175 {
176 type : 'html',
177 html : '<label class="cke_dialog_ui_labeled_label' + requiredClass + '"' +
178 ' id="' + _.labelId + '"' +
179 ' for="' + _.inputId + '"' +
180 ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>' +
181 CKEDITOR.tools.htmlEncode( elementDefinition.label ) +
182 '</span>'
183 },
184 {
185 type : 'html',
186 html : '<span class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + '>' +
187 contentHtml.call( this, dialog, elementDefinition ) +
188 '</span>'
189 }
190 ]
191 };
192 CKEDITOR.dialog._.uiElementBuilders.hbox.build( dialog, hboxDefinition, html );
193 }
194 return html.join( '' );
195 };
196 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, { role : 'presentation' }, innerHTML );
197 },
198
199 /**
200 * A text input with a label. This UI element class represents both the
201 * single-line text inputs and password inputs in dialog boxes.
202 * @constructor
203 * @example
204 * @extends CKEDITOR.ui.dialog.labeledElement
205 * @param {CKEDITOR.dialog} dialog
206 * Parent dialog object.
207 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
208 * The element definition. Accepted fields:
209 * <ul>
210 * <li><strong>default</strong> (Optional) The default value.</li>
211 * <li><strong>validate</strong> (Optional) The validation function. </li>
212 * <li><strong>maxLength</strong> (Optional) The maximum length of text box
213 * contents.</li>
214 * <li><strong>size</strong> (Optional) The size of the text box. This is
215 * usually overridden by the size defined by the skin, however.</li>
216 * </ul>
217 * @param {Array} htmlList
218 * List of HTML code to output to.
219 */
220 textInput : function( dialog, elementDefinition, htmlList )
221 {
222 if ( arguments.length < 3 )
223 return;
224
225 initPrivateObject.call( this, elementDefinition );
226 var domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textInput',
227 attributes = { 'class' : 'cke_dialog_ui_input_' + elementDefinition.type, id : domId, type : 'text' },
228 i;
229
230 // Set the validator, if any.
231 if ( elementDefinition.validate )
232 this.validate = elementDefinition.validate;
233
234 // Set the max length and size.
235 if ( elementDefinition.maxLength )
236 attributes.maxlength = elementDefinition.maxLength;
237 if ( elementDefinition.size )
238 attributes.size = elementDefinition.size;
239
240 if ( elementDefinition.inputStyle )
241 attributes.style = elementDefinition.inputStyle;
242
243 // If user presses Enter in a text box, it implies clicking OK for the dialog.
244 var me = this, keyPressedOnMe = false;
245 dialog.on( 'load', function()
246 {
247 me.getInputElement().on( 'keydown', function( evt )
248 {
249 if ( evt.data.getKeystroke() == 13 )
250 keyPressedOnMe = true;
251 } );
252
253 // Lower the priority this 'keyup' since 'ok' will close the dialog.(#3749)
254 me.getInputElement().on( 'keyup', function( evt )
255 {
256 if ( evt.data.getKeystroke() == 13 && keyPressedOnMe )
257 {
258 dialog.getButton( 'ok' ) && setTimeout( function ()
259 {
260 dialog.getButton( 'ok' ).click();
261 }, 0 );
262 keyPressedOnMe = false;
263 }
264 }, null, null, 1000 );
265 } );
266
267 /** @ignore */
268 var innerHTML = function()
269 {
270 // IE BUG: Text input fields in IE at 100% would exceed a <td> or inline
271 // container's width, so need to wrap it inside a <div>.
272 var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '" role="presentation"' ];
273
274 if ( elementDefinition.width )
275 html.push( 'style="width:'+ elementDefinition.width +'" ' );
276
277 html.push( '><input ' );
278
279 attributes[ 'aria-labelledby' ] = this._.labelId;
280 this._.required && ( attributes[ 'aria-required' ] = this._.required );
281 for ( var i in attributes )
282 html.push( i + '="' + attributes[i] + '" ' );
283 html.push( ' /></div>' );
284 return html.join( '' );
285 };
286 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
287 },
288
289 /**
290 * A text area with a label on the top or left.
291 * @constructor
292 * @extends CKEDITOR.ui.dialog.labeledElement
293 * @example
294 * @param {CKEDITOR.dialog} dialog
295 * Parent dialog object.
296 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
297 * The element definition. Accepted fields:
298 * <ul>
299 * <li><strong>rows</strong> (Optional) The number of rows displayed.
300 * Defaults to 5 if not defined.</li>
301 * <li><strong>cols</strong> (Optional) The number of cols displayed.
302 * Defaults to 20 if not defined. Usually overridden by skins.</li>
303 * <li><strong>default</strong> (Optional) The default value.</li>
304 * <li><strong>validate</strong> (Optional) The validation function. </li>
305 * </ul>
306 * @param {Array} htmlList
307 * List of HTML code to output to.
308 */
309 textarea : function( dialog, elementDefinition, htmlList )
310 {
311 if ( arguments.length < 3 )
312 return;
313
314 initPrivateObject.call( this, elementDefinition );
315 var me = this,
316 domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textarea',
317 attributes = {};
318
319 if ( elementDefinition.validate )
320 this.validate = elementDefinition.validate;
321
322 // Generates the essential attributes for the textarea tag.
323 attributes.rows = elementDefinition.rows || 5;
324 attributes.cols = elementDefinition.cols || 20;
325
326 if ( typeof elementDefinition.inputStyle != 'undefined' )
327 attributes.style = elementDefinition.inputStyle;
328
329
330 /** @ignore */
331 var innerHTML = function()
332 {
333 attributes[ 'aria-labelledby' ] = this._.labelId;
334 this._.required && ( attributes[ 'aria-required' ] = this._.required );
335 var html = [ '<div class="cke_dialog_ui_input_textarea" role="presentation"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ];
336 for ( var i in attributes )
337 html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' );
338 html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' );
339 return html.join( '' );
340 };
341 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
342 },
343
344 /**
345 * A single checkbox with a label on the right.
346 * @constructor
347 * @extends CKEDITOR.ui.dialog.uiElement
348 * @example
349 * @param {CKEDITOR.dialog} dialog
350 * Parent dialog object.
351 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
352 * The element definition. Accepted fields:
353 * <ul>
354 * <li><strong>checked</strong> (Optional) Whether the checkbox is checked
355 * on instantiation. Defaults to false.</li>
356 * <li><strong>validate</strong> (Optional) The validation function.</li>
357 * <li><strong>label</strong> (Optional) The checkbox label.</li>
358 * </ul>
359 * @param {Array} htmlList
360 * List of HTML code to output to.
361 */
362 checkbox : function( dialog, elementDefinition, htmlList )
363 {
364 if ( arguments.length < 3 )
365 return;
366
367 var _ = initPrivateObject.call( this, elementDefinition, { 'default' : !!elementDefinition[ 'default' ] } );
368
369 if ( elementDefinition.validate )
370 this.validate = elementDefinition.validate;
371
372 /** @ignore */
373 var innerHTML = function()
374 {
375 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
376 {
377 id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextId() + '_checkbox'
378 }, true ),
379 html = [];
380
381 var labelId = CKEDITOR.tools.getNextId() + '_label';
382 var attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox', 'aria-labelledby' : labelId };
383 cleanInnerDefinition( myDefinition );
384 if ( elementDefinition[ 'default' ] )
385 attributes.checked = 'checked';
386
387 if ( typeof myDefinition.inputStyle != 'undefined' )
388 myDefinition.style = myDefinition.inputStyle;
389
390 _.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes );
391 html.push( ' <label id="', labelId, '" for="', attributes.id, '"' + ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) + '>',
392 CKEDITOR.tools.htmlEncode( elementDefinition.label ),
393 '</label>' );
394 return html.join( '' );
395 };
396
397 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'span', null, null, innerHTML );
398 },
399
400 /**
401 * A group of radio buttons.
402 * @constructor
403 * @example
404 * @extends CKEDITOR.ui.dialog.labeledElement
405 * @param {CKEDITOR.dialog} dialog
406 * Parent dialog object.
407 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
408 * The element definition. Accepted fields:
409 * <ul>
410 * <li><strong>default</strong> (Required) The default value.</li>
411 * <li><strong>validate</strong> (Optional) The validation function.</li>
412 * <li><strong>items</strong> (Required) An array of options. Each option
413 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value'
414 * is missing, then the value would be assumed to be the same as the
415 * description.</li>
416 * </ul>
417 * @param {Array} htmlList
418 * List of HTML code to output to.
419 */
420 radio : function( dialog, elementDefinition, htmlList )
421 {
422 if ( arguments.length < 3)
423 return;
424
425 initPrivateObject.call( this, elementDefinition );
426 if ( !this._['default'] )
427 this._['default'] = this._.initValue = elementDefinition.items[0][1];
428 if ( elementDefinition.validate )
429 this.validate = elementDefinition.valdiate;
430 var children = [], me = this;
431
432 /** @ignore */
433 var innerHTML = function()
434 {
435 var inputHtmlList = [], html = [],
436 commonAttributes = { 'class' : 'cke_dialog_ui_radio_item', 'aria-labelledby' : this._.labelId },
437 commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextId() + '_radio';
438 for ( var i = 0 ; i < elementDefinition.items.length ; i++ )
439 {
440 var item = elementDefinition.items[i],
441 title = item[2] !== undefined ? item[2] : item[0],
442 value = item[1] !== undefined ? item[1] : item[0],
443 inputId = CKEDITOR.tools.getNextId() + '_radio_input',
444 labelId = inputId + '_label',
445 inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
446 {
447 id : inputId,
448 title : null,
449 type : null
450 }, true ),
451 labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition,
452 {
453 title : title
454 }, true ),
455 inputAttributes =
456 {
457 type : 'radio',
458 'class' : 'cke_dialog_ui_radio_input',
459 name : commonName,
460 value : value,
461 'aria-labelledby' : labelId
462 },
463 inputHtml = [];
464 if ( me._['default'] == value )
465 inputAttributes.checked = 'checked';
466 cleanInnerDefinition( inputDefinition );
467 cleanInnerDefinition( labelDefinition );
468
469 if ( typeof inputDefinition.inputStyle != 'undefined' )
470 inputDefinition.style = inputDefinition.inputStyle;
471
472 children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) );
473 inputHtml.push( ' ' );
474 new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtml, 'label', null, { id : labelId, 'for' : inputAttributes.id },
475 item[0] );
476 inputHtmlList.push( inputHtml.join( '' ) );
477 }
478 new CKEDITOR.ui.dialog.hbox( dialog, [], inputHtmlList, html );
479 return html.join( '' );
480 };
481
482 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
483 this._.children = children;
484 },
485
486 /**
487 * A button with a label inside.
488 * @constructor
489 * @example
490 * @extends CKEDITOR.ui.dialog.uiElement
491 * @param {CKEDITOR.dialog} dialog
492 * Parent dialog object.
493 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
494 * The element definition. Accepted fields:
495 * <ul>
496 * <li><strong>label</strong> (Required) The button label.</li>
497 * <li><strong>disabled</strong> (Optional) Set to true if you want the
498 * button to appear in disabled state.</li>
499 * </ul>
500 * @param {Array} htmlList
501 * List of HTML code to output to.
502 */
503 button : function( dialog, elementDefinition, htmlList )
504 {
505 if ( !arguments.length )
506 return;
507
508 if ( typeof elementDefinition == 'function' )
509 elementDefinition = elementDefinition( dialog.getParentEditor() );
510
511 initPrivateObject.call( this, elementDefinition, { disabled : elementDefinition.disabled || false } );
512
513 // Add OnClick event to this input.
514 CKEDITOR.event.implementOn( this );
515
516 var me = this;
517
518 // Register an event handler for processing button clicks.
519 dialog.on( 'load', function( eventInfo )
520 {
521 var element = this.getElement();
522
523 (function()
524 {
525 element.on( 'click', function( evt )
526 {
527 me.fire( 'click', { dialog : me.getDialog() } );
528 evt.data.preventDefault();
529 } );
530
531 element.on( 'keydown', function( evt )
532 {
533 if ( evt.data.getKeystroke() in { 32:1 } )
534 {
535 me.click();
536 evt.data.preventDefault();
537 }
538 } );
539 })();
540
541 element.unselectable();
542 }, this );
543
544 var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition );
545 delete outerDefinition.style;
546
547 var labelId = CKEDITOR.tools.getNextId() + '_label';
548 CKEDITOR.ui.dialog.uiElement.call(
549 this,
550 dialog,
551 outerDefinition,
552 htmlList,
553 'a',
554 null,
555 {
556 style : elementDefinition.style,
557 href : 'javascript:void(0)',
558 title : elementDefinition.label,
559 hidefocus : 'true',
560 'class' : elementDefinition['class'],
561 role : 'button',
562 'aria-labelledby' : labelId
563 },
564 '<span id="' + labelId + '" class="cke_dialog_ui_button">' +
565 CKEDITOR.tools.htmlEncode( elementDefinition.label ) +
566 '</span>' );
567 },
568
569 /**
570 * A select box.
571 * @extends CKEDITOR.ui.dialog.uiElement
572 * @example
573 * @constructor
574 * @param {CKEDITOR.dialog} dialog
575 * Parent dialog object.
576 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
577 * The element definition. Accepted fields:
578 * <ul>
579 * <li><strong>default</strong> (Required) The default value.</li>
580 * <li><strong>validate</strong> (Optional) The validation function.</li>
581 * <li><strong>items</strong> (Required) An array of options. Each option
582 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value'
583 * is missing, then the value would be assumed to be the same as the
584 * description.</li>
585 * <li><strong>multiple</strong> (Optional) Set this to true if you'd like
586 * to have a multiple-choice select box.</li>
587 * <li><strong>size</strong> (Optional) The number of items to display in
588 * the select box.</li>
589 * </ul>
590 * @param {Array} htmlList
591 * List of HTML code to output to.
592 */
593 select : function( dialog, elementDefinition, htmlList )
594 {
595 if ( arguments.length < 3 )
596 return;
597
598 var _ = initPrivateObject.call( this, elementDefinition );
599
600 if ( elementDefinition.validate )
601 this.validate = elementDefinition.validate;
602
603 _.inputId = CKEDITOR.tools.getNextId() + '_select';
604 /** @ignore */
605 var innerHTML = function()
606 {
607 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
608 {
609 id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextId() + '_select'
610 }, true ),
611 html = [],
612 innerHTML = [],
613 attributes = { 'id' : _.inputId, 'class' : 'cke_dialog_ui_input_select', 'aria-labelledby' : this._.labelId };
614
615 // Add multiple and size attributes from element definition.
616 if ( elementDefinition.size != undefined )
617 attributes.size = elementDefinition.size;
618 if ( elementDefinition.multiple != undefined )
619 attributes.multiple = elementDefinition.multiple;
620
621 cleanInnerDefinition( myDefinition );
622 for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ )
623 {
624 innerHTML.push( '<option value="',
625 CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ).replace( /"/g, '&quot;' ), '" /> ',
626 CKEDITOR.tools.htmlEncode( item[0] ) );
627 }
628
629 if ( typeof myDefinition.inputStyle != 'undefined' )
630 myDefinition.style = myDefinition.inputStyle;
631
632 _.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) );
633 return html.join( '' );
634 };
635
636 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
637 },
638
639 /**
640 * A file upload input.
641 * @extends CKEDITOR.ui.dialog.labeledElement
642 * @example
643 * @constructor
644 * @param {CKEDITOR.dialog} dialog
645 * Parent dialog object.
646 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
647 * The element definition. Accepted fields:
648 * <ul>
649 * <li><strong>validate</strong> (Optional) The validation function.</li>
650 * </ul>
651 * @param {Array} htmlList
652 * List of HTML code to output to.
653 */
654 file : function( dialog, elementDefinition, htmlList )
655 {
656 if ( arguments.length < 3 )
657 return;
658
659 if ( elementDefinition['default'] === undefined )
660 elementDefinition['default'] = '';
661
662 var _ = CKEDITOR.tools.extend( initPrivateObject.call( this, elementDefinition ), { definition : elementDefinition, buttons : [] } );
663
664 if ( elementDefinition.validate )
665 this.validate = elementDefinition.validate;
666
667 /** @ignore */
668 var innerHTML = function()
669 {
670 _.frameId = CKEDITOR.tools.getNextId() + '_fileInput';
671
672 // Support for custom document.domain in IE.
673 var isCustomDomain = CKEDITOR.env.isCustomDomain();
674
675 var html = [
676 '<iframe' +
677 ' frameborder="0"' +
678 ' allowtransparency="0"' +
679 ' class="cke_dialog_ui_input_file"' +
680 ' id="', _.frameId, '"' +
681 ' title="', elementDefinition.label, '"' +
682 ' src="javascript:void(' ];
683
684 html.push(
685 isCustomDomain ?
686 '(function(){' +
687 'document.open();' +
688 'document.domain=\'' + document.domain + '\';' +
689 'document.close();' +
690 '})()'
691 :
692 '0' );
693
694 html.push(
695 ')">' +
696 '</iframe>' );
697
698 return html.join( '' );
699 };
700
701 // IE BUG: Parent container does not resize to contain the iframe automatically.
702 dialog.on( 'load', function()
703 {
704 var iframe = CKEDITOR.document.getById( _.frameId ),
705 contentDiv = iframe.getParent();
706 contentDiv.addClass( 'cke_dialog_ui_input_file' );
707 } );
708
709 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
710 },
711
712 /**
713 * A button for submitting the file in a file upload input.
714 * @extends CKEDITOR.ui.dialog.button
715 * @example
716 * @constructor
717 * @param {CKEDITOR.dialog} dialog
718 * Parent dialog object.
719 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
720 * The element definition. Accepted fields:
721 * <ul>
722 * <li><strong>for</strong> (Required) The file input's page and element Id
723 * to associate to, in a 2-item array format: [ 'page_id', 'element_id' ].
724 * </li>
725 * <li><strong>validate</strong> (Optional) The validation function.</li>
726 * </ul>
727 * @param {Array} htmlList
728 * List of HTML code to output to.
729 */
730 fileButton : function( dialog, elementDefinition, htmlList )
731 {
732 if ( arguments.length < 3 )
733 return;
734
735 var _ = initPrivateObject.call( this, elementDefinition ),
736 me = this;
737
738 if ( elementDefinition.validate )
739 this.validate = elementDefinition.validate;
740
741 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition );
742 var onClick = myDefinition.onClick;
743 myDefinition.className = ( myDefinition.className ? myDefinition.className + ' ' : '' ) + 'cke_dialog_ui_button';
744 myDefinition.onClick = function( evt )
745 {
746 var target = elementDefinition[ 'for' ]; // [ pageId, elementId ]
747 if ( !onClick || onClick.call( this, evt ) !== false )
748 {
749 dialog.getContentElement( target[0], target[1] ).submit();
750 this.disable();
751 }
752 };
753
754 dialog.on( 'load', function()
755 {
756 dialog.getContentElement( elementDefinition[ 'for' ][0], elementDefinition[ 'for' ][1] )._.buttons.push( me );
757 } );
758
759 CKEDITOR.ui.dialog.button.call( this, dialog, myDefinition, htmlList );
760 },
761
762 html : (function()
763 {
764 var myHtmlRe = /^\s*<[\w:]+\s+([^>]*)?>/,
765 theirHtmlRe = /^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/,
766 emptyTagRe = /\/$/;
767 /**
768 * A dialog element made from raw HTML code.
769 * @extends CKEDITOR.ui.dialog.uiElement
770 * @name CKEDITOR.ui.dialog.html
771 * @param {CKEDITOR.dialog} dialog Parent dialog object.
772 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element definition.
773 * Accepted fields:
774 * <ul>
775 * <li><strong>html</strong> (Required) HTML code of this element.</li>
776 * </ul>
777 * @param {Array} htmlList List of HTML code to be added to the dialog's content area.
778 * @example
779 * @constructor
780 */
781 return function( dialog, elementDefinition, htmlList )
782 {
783 if ( arguments.length < 3 )
784 return;
785
786 var myHtmlList = [],
787 myHtml,
788 theirHtml = elementDefinition.html,
789 myMatch, theirMatch;
790
791 // If the HTML input doesn't contain any tags at the beginning, add a <span> tag around it.
792 if ( theirHtml.charAt( 0 ) != '<' )
793 theirHtml = '<span>' + theirHtml + '</span>';
794
795 // Look for focus function in definition.
796 var focus = elementDefinition.focus;
797 if ( focus )
798 {
799 var oldFocus = this.focus;
800 this.focus = function()
801 {
802 oldFocus.call( this );
803 typeof focus == 'function' && focus.call( this );
804 this.fire( 'focus' );
805 };
806 if ( elementDefinition.isFocusable )
807 {
808 var oldIsFocusable = this.isFocusable;
809 this.isFocusable = oldIsFocusable;
810 }
811 this.keyboardFocusable = true;
812 }
813
814 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtmlList, 'span', null, null, '' );
815
816 // Append the attributes created by the uiElement call to the real HTML.
817 myHtml = myHtmlList.join( '' );
818 myMatch = myHtml.match( myHtmlRe );
819 theirMatch = theirHtml.match( theirHtmlRe ) || [ '', '', '' ];
820
821 if ( emptyTagRe.test( theirMatch[1] ) )
822 {
823 theirMatch[1] = theirMatch[1].slice( 0, -1 );
824 theirMatch[2] = '/' + theirMatch[2];
825 }
826
827 htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) );
828 };
829 })(),
830
831 /**
832 * Form fieldset for grouping dialog UI elements.
833 * @constructor
834 * @extends CKEDITOR.ui.dialog.uiElement
835 * @param {CKEDITOR.dialog} dialog Parent dialog object.
836 * @param {Array} childObjList
837 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
838 * container.
839 * @param {Array} childHtmlList
840 * Array of HTML code that correspond to the HTML output of all the
841 * objects in childObjList.
842 * @param {Array} htmlList
843 * Array of HTML code that this element will output to.
844 * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
845 * The element definition. Accepted fields:
846 * <ul>
847 * <li><strong>label</strong> (Optional) The legend of the this fieldset.</li>
848 * <li><strong>children</strong> (Required) An array of dialog field definitions which will be grouped inside this fieldset. </li>
849 * </ul>
850 */
851 fieldset : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
852 {
853 var legendLabel = elementDefinition.label;
854 /** @ignore */
855 var innerHTML = function()
856 {
857 var html = [];
858 legendLabel && html.push( '<legend>' + legendLabel + '</legend>' );
859 for ( var i = 0; i < childHtmlList.length; i++ )
860 html.push( childHtmlList[ i ] );
861 return html.join( '' );
862 };
863
864 this._ = { children : childObjList };
865 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'fieldset', null, null, innerHTML );
866 }
867
868 }, true );
869
870 CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement;
871
872 CKEDITOR.ui.dialog.labeledElement.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
873 /** @lends CKEDITOR.ui.dialog.labeledElement.prototype */
874 {
875 /**
876 * Sets the label text of the element.
877 * @param {String} label The new label text.
878 * @returns {CKEDITOR.ui.dialog.labeledElement} The current labeled element.
879 * @example
880 */
881 setLabel : function( label )
882 {
883 var node = CKEDITOR.document.getById( this._.labelId );
884 if ( node.getChildCount() < 1 )
885 ( new CKEDITOR.dom.text( label, CKEDITOR.document ) ).appendTo( node );
886 else
887 node.getChild( 0 ).$.nodeValue = label;
888 return this;
889 },
890
891 /**
892 * Retrieves the current label text of the elment.
893 * @returns {String} The current label text.
894 * @example
895 */
896 getLabel : function()
897 {
898 var node = CKEDITOR.document.getById( this._.labelId );
899 if ( !node || node.getChildCount() < 1 )
900 return '';
901 else
902 return node.getChild( 0 ).getText();
903 },
904
905 /**
906 * Defines the onChange event for UI element definitions.
907 * @field
908 * @type Object
909 * @example
910 */
911 eventProcessors : commonEventProcessors
912 }, true );
913
914 CKEDITOR.ui.dialog.button.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
915 /** @lends CKEDITOR.ui.dialog.button.prototype */
916 {
917 /**
918 * Simulates a click to the button.
919 * @example
920 * @returns {Object} Return value of the 'click' event.
921 */
922 click : function()
923 {
924 if ( !this._.disabled )
925 return this.fire( 'click', { dialog : this._.dialog } );
926 this.getElement().$.blur();
927 return false;
928 },
929
930 /**
931 * Enables the button.
932 * @example
933 */
934 enable : function()
935 {
936 this._.disabled = false;
937 var element = this.getElement();
938 element && element.removeClass( 'cke_disabled' );
939 },
940
941 /**
942 * Disables the button.
943 * @example
944 */
945 disable : function()
946 {
947 this._.disabled = true;
948 this.getElement().addClass( 'cke_disabled' );
949 },
950
951 isVisible : function()
952 {
953 return this.getElement().getFirst().isVisible();
954 },
955
956 isEnabled : function()
957 {
958 return !this._.disabled;
959 },
960
961 /**
962 * Defines the onChange event and onClick for button element definitions.
963 * @field
964 * @type Object
965 * @example
966 */
967 eventProcessors : CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
968 {
969 /** @ignore */
970 onClick : function( dialog, func )
971 {
972 this.on( 'click', func );
973 }
974 }, true ),
975
976 /**
977 * Handler for the element's access key up event. Simulates a click to
978 * the button.
979 * @example
980 */
981 accessKeyUp : function()
982 {
983 this.click();
984 },
985
986 /**
987 * Handler for the element's access key down event. Simulates a mouse
988 * down to the button.
989 * @example
990 */
991 accessKeyDown : function()
992 {
993 this.focus();
994 },
995
996 keyboardFocusable : true
997 }, true );
998
999 CKEDITOR.ui.dialog.textInput.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
1000 /** @lends CKEDITOR.ui.dialog.textInput.prototype */
1001 {
1002 /**
1003 * Gets the text input DOM element under this UI object.
1004 * @example
1005 * @returns {CKEDITOR.dom.element} The DOM element of the text input.
1006 */
1007 getInputElement : function()
1008 {
1009 return CKEDITOR.document.getById( this._.inputId );
1010 },
1011
1012 /**
1013 * Puts focus into the text input.
1014 * @example
1015 */
1016 focus : function()
1017 {
1018 var me = this.selectParentTab();
1019
1020 // GECKO BUG: setTimeout() is needed to workaround invisible selections.
1021 setTimeout( function()
1022 {
1023 var element = me.getInputElement();
1024 element && element.$.focus();
1025 }, 0 );
1026 },
1027
1028 /**
1029 * Selects all the text in the text input.
1030 * @example
1031 */
1032 select : function()
1033 {
1034 var me = this.selectParentTab();
1035
1036 // GECKO BUG: setTimeout() is needed to workaround invisible selections.
1037 setTimeout( function()
1038 {
1039 var e = me.getInputElement();
1040 if ( e )
1041 {
1042 e.$.focus();
1043 e.$.select();
1044 }
1045 }, 0 );
1046 },
1047
1048 /**
1049 * Handler for the text input's access key up event. Makes a select()
1050 * call to the text input.
1051 * @example
1052 */
1053 accessKeyUp : function()
1054 {
1055 this.select();
1056 },
1057
1058 /**
1059 * Sets the value of this text input object.
1060 * @param {Object} value The new value.
1061 * @returns {CKEDITOR.ui.dialog.textInput} The current UI element.
1062 * @example
1063 * uiElement.setValue( 'Blamo' );
1064 */
1065 setValue : function( value )
1066 {
1067 !value && ( value = '' );
1068 return CKEDITOR.ui.dialog.uiElement.prototype.setValue.apply( this, arguments );
1069 },
1070
1071 keyboardFocusable : true
1072 }, commonPrototype, true );
1073
1074 CKEDITOR.ui.dialog.textarea.prototype = new CKEDITOR.ui.dialog.textInput();
1075
1076 CKEDITOR.ui.dialog.select.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
1077 /** @lends CKEDITOR.ui.dialog.select.prototype */
1078 {
1079 /**
1080 * Gets the DOM element of the select box.
1081 * @returns {CKEDITOR.dom.element} The &lt;select&gt; element of this UI
1082 * element.
1083 * @example
1084 */
1085 getInputElement : function()
1086 {
1087 return this._.select.getElement();
1088 },
1089
1090 /**
1091 * Adds an option to the select box.
1092 * @param {String} label Option label.
1093 * @param {String} value (Optional) Option value, if not defined it'll be
1094 * assumed to be the same as the label.
1095 * @param {Number} index (Optional) Position of the option to be inserted
1096 * to. If not defined the new option will be inserted to the end of list.
1097 * @example
1098 * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
1099 */
1100 add : function( label, value, index )
1101 {
1102 var option = new CKEDITOR.dom.element( 'option', this.getDialog().getParentEditor().document ),
1103 selectElement = this.getInputElement().$;
1104 option.$.text = label;
1105 option.$.value = ( value === undefined || value === null ) ? label : value;
1106 if ( index === undefined || index === null )
1107 {
1108 if ( CKEDITOR.env.ie )
1109 selectElement.add( option.$ );
1110 else
1111 selectElement.add( option.$, null );
1112 }
1113 else
1114 selectElement.add( option.$, index );
1115 return this;
1116 },
1117
1118 /**
1119 * Removes an option from the selection list.
1120 * @param {Number} index Index of the option to be removed.
1121 * @example
1122 * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
1123 */
1124 remove : function( index )
1125 {
1126 var selectElement = this.getInputElement().$;
1127 selectElement.remove( index );
1128 return this;
1129 },
1130
1131 /**
1132 * Clears all options out of the selection list.
1133 * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
1134 */
1135 clear : function()
1136 {
1137 var selectElement = this.getInputElement().$;
1138 while ( selectElement.length > 0 )
1139 selectElement.remove( 0 );
1140 return this;
1141 },
1142
1143 keyboardFocusable : true
1144 }, commonPrototype, true );
1145
1146 CKEDITOR.ui.dialog.checkbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
1147 /** @lends CKEDITOR.ui.dialog.checkbox.prototype */
1148 {
1149 /**
1150 * Gets the checkbox DOM element.
1151 * @example
1152 * @returns {CKEDITOR.dom.element} The DOM element of the checkbox.
1153 */
1154 getInputElement : function()
1155 {
1156 return this._.checkbox.getElement();
1157 },
1158
1159 /**
1160 * Sets the state of the checkbox.
1161 * @example
1162 * @param {Boolean} true to tick the checkbox, false to untick it.
1163 * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.
1164 */
1165 setValue : function( checked, noChangeEvent )
1166 {
1167 this.getInputElement().$.checked = checked;
1168 !noChangeEvent && this.fire( 'change', { value : checked } );
1169 },
1170
1171 /**
1172 * Gets the state of the checkbox.
1173 * @example
1174 * @returns {Boolean} true means the checkbox is ticked, false means it's not ticked.
1175 */
1176 getValue : function()
1177 {
1178 return this.getInputElement().$.checked;
1179 },
1180
1181 /**
1182 * Handler for the access key up event. Toggles the checkbox.
1183 * @example
1184 */
1185 accessKeyUp : function()
1186 {
1187 this.setValue( !this.getValue() );
1188 },
1189
1190 /**
1191 * Defines the onChange event for UI element definitions.
1192 * @field
1193 * @type Object
1194 * @example
1195 */
1196 eventProcessors :
1197 {
1198 onChange : function( dialog, func )
1199 {
1200 if ( !CKEDITOR.env.ie )
1201 return commonEventProcessors.onChange.apply( this, arguments );
1202 else
1203 {
1204 dialog.on( 'load', function()
1205 {
1206 var element = this._.checkbox.getElement();
1207 element.on( 'propertychange', function( evt )
1208 {
1209 evt = evt.data.$;
1210 if ( evt.propertyName == 'checked' )
1211 this.fire( 'change', { value : element.$.checked } );
1212 }, this );
1213 }, this );
1214 this.on( 'change', func );
1215 }
1216 return null;
1217 }
1218 },
1219
1220 keyboardFocusable : true
1221 }, commonPrototype, true );
1222
1223 CKEDITOR.ui.dialog.radio.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
1224 /** @lends CKEDITOR.ui.dialog.radio.prototype */
1225 {
1226 /**
1227 * Checks one of the radio buttons in this button group.
1228 * @example
1229 * @param {String} value The value of the button to be chcked.
1230 * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.
1231 */
1232 setValue : function( value, noChangeEvent )
1233 {
1234 var children = this._.children,
1235 item;
1236 for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ )
1237 item.getElement().$.checked = ( item.getValue() == value );
1238 !noChangeEvent && this.fire( 'change', { value : value } );
1239 },
1240
1241 /**
1242 * Gets the value of the currently checked radio button.
1243 * @example
1244 * @returns {String} The currently checked button's value.
1245 */
1246 getValue : function()
1247 {
1248 var children = this._.children;
1249 for ( var i = 0 ; i < children.length ; i++ )
1250 {
1251 if ( children[i].getElement().$.checked )
1252 return children[i].getValue();
1253 }
1254 return null;
1255 },
1256
1257 /**
1258 * Handler for the access key up event. Focuses the currently
1259 * selected radio button, or the first radio button if none is
1260 * selected.
1261 * @example
1262 */
1263 accessKeyUp : function()
1264 {
1265 var children = this._.children, i;
1266 for ( i = 0 ; i < children.length ; i++ )
1267 {
1268 if ( children[i].getElement().$.checked )
1269 {
1270 children[i].getElement().focus();
1271 return;
1272 }
1273 }
1274 children[0].getElement().focus();
1275 },
1276
1277 /**
1278 * Defines the onChange event for UI element definitions.
1279 * @field
1280 * @type Object
1281 * @example
1282 */
1283 eventProcessors :
1284 {
1285 onChange : function( dialog, func )
1286 {
1287 if ( !CKEDITOR.env.ie )
1288 return commonEventProcessors.onChange.apply( this, arguments );
1289 else
1290 {
1291 dialog.on( 'load', function()
1292 {
1293 var children = this._.children, me = this;
1294 for ( var i = 0 ; i < children.length ; i++ )
1295 {
1296 var element = children[i].getElement();
1297 element.on( 'propertychange', function( evt )
1298 {
1299 evt = evt.data.$;
1300 if ( evt.propertyName == 'checked' && this.$.checked )
1301 me.fire( 'change', { value : this.getAttribute( 'value' ) } );
1302 } );
1303 }
1304 }, this );
1305 this.on( 'change', func );
1306 }
1307 return null;
1308 }
1309 },
1310
1311 keyboardFocusable : true
1312 }, commonPrototype, true );
1313
1314 CKEDITOR.ui.dialog.file.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
1315 commonPrototype,
1316 /** @lends CKEDITOR.ui.dialog.file.prototype */
1317 {
1318 /**
1319 * Gets the &lt;input&gt; element of this file input.
1320 * @returns {CKEDITOR.dom.element} The file input element.
1321 * @example
1322 */
1323 getInputElement : function()
1324 {
1325 var frameDocument = CKEDITOR.document.getById( this._.frameId ).getFrameDocument();
1326 return frameDocument.$.forms.length > 0 ?
1327 new CKEDITOR.dom.element( frameDocument.$.forms[0].elements[0] ) :
1328 this.getElement();
1329 },
1330
1331 /**
1332 * Uploads the file in the file input.
1333 * @returns {CKEDITOR.ui.dialog.file} This object.
1334 * @example
1335 */
1336 submit : function()
1337 {
1338 this.getInputElement().getParent().$.submit();
1339 return this;
1340 },
1341
1342 /**
1343 * Get the action assigned to the form.
1344 * @returns {String} The value of the action.
1345 * @example
1346 */
1347 getAction : function()
1348 {
1349 return this.getInputElement().getParent().$.action;
1350 },
1351
1352 /**
1353 * The events must be applied on the inner input element, and
1354 * that must be done when the iframe & form has been loaded
1355 */
1356 registerEvents : function( definition )
1357 {
1358 var regex = /^on([A-Z]\w+)/,
1359 match;
1360
1361 var registerDomEvent = function( uiElement, dialog, eventName, func )
1362 {
1363 uiElement.on( 'formLoaded', function()
1364 {
1365 uiElement.getInputElement().on( eventName, func, uiElement );
1366 });
1367 };
1368
1369 for ( var i in definition )
1370 {
1371 if ( !( match = i.match( regex ) ) )
1372 continue;
1373
1374 if ( this.eventProcessors[i] )
1375 this.eventProcessors[i].call( this, this._.dialog, definition[i] );
1376 else
1377 registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] );
1378 }
1379
1380 return this;
1381 },
1382
1383 /**
1384 * Redraws the file input and resets the file path in the file input.
1385 * The redraw logic is necessary because non-IE browsers tend to clear
1386 * the &lt;iframe&gt; containing the file input after closing the dialog.
1387 * @example
1388 */
1389 reset : function()
1390 {
1391 var _ = this._,
1392 frameElement = CKEDITOR.document.getById( _.frameId ),
1393 frameDocument = frameElement.getFrameDocument(),
1394 elementDefinition = _.definition,
1395 buttons = _.buttons,
1396 callNumber = this.formLoadedNumber,
1397 unloadNumber = this.formUnloadNumber,
1398 langDir = _.dialog._.editor.lang.dir,
1399 langCode = _.dialog._.editor.langCode;
1400
1401 // The callback function for the iframe, but we must call tools.addFunction only once
1402 // so we store the function number in this.formLoadedNumber
1403 if ( !callNumber )
1404 {
1405 callNumber = this.formLoadedNumber = CKEDITOR.tools.addFunction(
1406 function()
1407 {
1408 // Now we can apply the events to the input type=file
1409 this.fire( 'formLoaded' ) ;
1410 }, this ) ;
1411
1412 // Remove listeners attached to the content of the iframe (the file input)
1413 unloadNumber = this.formUnloadNumber = CKEDITOR.tools.addFunction(
1414 function()
1415 {
1416 this.getInputElement().clearCustomData();
1417 }, this ) ;
1418
1419 this.getDialog()._.editor.on( 'destroy', function()
1420 {
1421 CKEDITOR.tools.removeFunction( callNumber );
1422 CKEDITOR.tools.removeFunction( unloadNumber );
1423 } );
1424 }
1425
1426 function generateFormField()
1427 {
1428 frameDocument.$.open();
1429
1430 // Support for custom document.domain in IE.
1431 if ( CKEDITOR.env.isCustomDomain() )
1432 frameDocument.$.domain = document.domain;
1433
1434 var size = '';
1435 if ( elementDefinition.size )
1436 size = elementDefinition.size - ( CKEDITOR.env.ie ? 7 : 0 ); // "Browse" button is bigger in IE.
1437
1438 frameDocument.$.write( [ '<html dir="' + langDir + '" lang="' + langCode + '"><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">',
1439 '<form enctype="multipart/form-data" method="POST" dir="' + langDir + '" lang="' + langCode + '" action="',
1440 CKEDITOR.tools.htmlEncode( elementDefinition.action ),
1441 '">',
1442 '<input type="file" name="',
1443 CKEDITOR.tools.htmlEncode( elementDefinition.id || 'cke_upload' ),
1444 '" size="',
1445 CKEDITOR.tools.htmlEncode( size > 0 ? size : "" ),
1446 '" />',
1447 '</form>',
1448 '</body></html>',
1449 '<script>window.parent.CKEDITOR.tools.callFunction(' + callNumber + ');',
1450 'window.onbeforeunload = function() {window.parent.CKEDITOR.tools.callFunction(' + unloadNumber + ')}</script>' ].join( '' ) );
1451
1452 frameDocument.$.close();
1453
1454 for ( var i = 0 ; i < buttons.length ; i++ )
1455 buttons[i].enable();
1456 }
1457
1458 // #3465: Wait for the browser to finish rendering the dialog first.
1459 if ( CKEDITOR.env.gecko )
1460 setTimeout( generateFormField, 500 );
1461 else
1462 generateFormField();
1463 },
1464
1465 getValue : function()
1466 {
1467 return this.getInputElement().$.value || '';
1468 },
1469
1470 /***
1471 * The default value of input type="file" is an empty string, but during initialization
1472 * of this UI element, the iframe still isn't ready so it can't be read from that object
1473 * Setting it manually prevents later issues about the current value ("") being different
1474 * of the initial value (undefined as it asked for .value of a div)
1475 */
1476 setInitValue : function()
1477 {
1478 this._.initValue = '';
1479 },
1480
1481 /**
1482 * Defines the onChange event for UI element definitions.
1483 * @field
1484 * @type Object
1485 * @example
1486 */
1487 eventProcessors :
1488 {
1489 onChange : function( dialog, func )
1490 {
1491 // If this method is called several times (I'm not sure about how this can happen but the default
1492 // onChange processor includes this protection)
1493 // In order to reapply to the new element, the property is deleted at the beggining of the registerEvents method
1494 if ( !this._.domOnChangeRegistered )
1495 {
1496 // By listening for the formLoaded event, this handler will get reapplied when a new
1497 // form is created
1498 this.on( 'formLoaded', function()
1499 {
1500 this.getInputElement().on( 'change', function(){ this.fire( 'change', { value : this.getValue() } ); }, this );
1501 }, this );
1502 this._.domOnChangeRegistered = true;
1503 }
1504
1505 this.on( 'change', func );
1506 }
1507 },
1508
1509 keyboardFocusable : true
1510 }, true );
1511
1512 CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button;
1513
1514 CKEDITOR.ui.dialog.fieldset.prototype = CKEDITOR.tools.clone( CKEDITOR.ui.dialog.hbox.prototype );
1515
1516 CKEDITOR.dialog.addUIElement( 'text', textBuilder );
1517 CKEDITOR.dialog.addUIElement( 'password', textBuilder );
1518 CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder );
1519 CKEDITOR.dialog.addUIElement( 'checkbox', commonBuilder );
1520 CKEDITOR.dialog.addUIElement( 'radio', commonBuilder );
1521 CKEDITOR.dialog.addUIElement( 'button', commonBuilder );
1522 CKEDITOR.dialog.addUIElement( 'select', commonBuilder );
1523 CKEDITOR.dialog.addUIElement( 'file', commonBuilder );
1524 CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder );
1525 CKEDITOR.dialog.addUIElement( 'html', commonBuilder );
1526 CKEDITOR.dialog.addUIElement( 'fieldset', containerBuilder );
1527 })();
1528
1529 /**
1530 * Fired when the value of the uiElement is changed
1531 * @name CKEDITOR.ui.dialog.uiElement#change
1532 * @event
1533 */
1534
1535 /**
1536 * Fired when the inner frame created by the element is ready.
1537 * Each time the button is used or the dialog is loaded a new
1538 * form might be created.
1539 * @name CKEDITOR.ui.dialog.fileButton#formLoaded
1540 * @event
1541 */