initial commit
[namibia] / public / scripts / 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, children, 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  */