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