diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2017-01-20 00:55:51 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2017-01-20 00:55:51 +0100 |
commit | c63493c899de714b05b0521bb38aab60d19030ef (patch) | |
tree | fcb2b261afa0f3c2bd6b48929b64724c71192bae /sources/plugins/dialog | |
download | ludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.tar.gz ludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.tar.zst ludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.zip |
Validation initiale4.6.2.1
Diffstat (limited to 'sources/plugins/dialog')
-rw-r--r-- | sources/plugins/dialog/dialogDefinition.js | 1032 | ||||
-rw-r--r-- | sources/plugins/dialog/plugin.js | 3399 | ||||
-rw-r--r-- | sources/plugins/dialog/samples/assets/my_dialog.js | 49 | ||||
-rw-r--r-- | sources/plugins/dialog/samples/dialog.html | 190 |
4 files changed, 4670 insertions, 0 deletions
diff --git a/sources/plugins/dialog/dialogDefinition.js b/sources/plugins/dialog/dialogDefinition.js new file mode 100644 index 0000000..6ecb491 --- /dev/null +++ b/sources/plugins/dialog/dialogDefinition.js | |||
@@ -0,0 +1,1032 @@ | |||
1 | // jscs:disable disallowMixedSpacesAndTabs | ||
2 | /** | ||
3 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
4 | * For licensing, see LICENSE.md or http://ckeditor.com/license | ||
5 | */ | ||
6 | |||
7 | /** | ||
8 | * @fileOverview Defines the "virtual" dialog, dialog content and dialog button | ||
9 | * definition classes. | ||
10 | */ | ||
11 | |||
12 | /** | ||
13 | * The definition of a dialog window. | ||
14 | * | ||
15 | * This class is not really part of the API. It just illustrates the properties | ||
16 | * that developers can use to define and create dialogs. | ||
17 | * | ||
18 | * // There is no constructor for this class, the user just has to define an | ||
19 | * // object with the appropriate properties. | ||
20 | * | ||
21 | * CKEDITOR.dialog.add( 'testOnly', function( editor ) { | ||
22 | * return { | ||
23 | * title: 'Test Dialog', | ||
24 | * resizable: CKEDITOR.DIALOG_RESIZE_BOTH, | ||
25 | * minWidth: 500, | ||
26 | * minHeight: 400, | ||
27 | * contents: [ | ||
28 | * { | ||
29 | * id: 'tab1', | ||
30 | * label: 'First Tab', | ||
31 | * title: 'First Tab Title', | ||
32 | * accessKey: 'Q', | ||
33 | * elements: [ | ||
34 | * { | ||
35 | * type: 'text', | ||
36 | * label: 'Test Text 1', | ||
37 | * id: 'testText1', | ||
38 | * 'default': 'hello world!' | ||
39 | * } | ||
40 | * ] | ||
41 | * } | ||
42 | * ] | ||
43 | * }; | ||
44 | * } ); | ||
45 | * | ||
46 | * @class CKEDITOR.dialog.definition | ||
47 | */ | ||
48 | |||
49 | /** | ||
50 | * The dialog title, displayed in the dialog's header. Required. | ||
51 | * | ||
52 | * @property {String} title | ||
53 | */ | ||
54 | |||
55 | /** | ||
56 | * How the dialog can be resized, must be one of the four contents defined below. | ||
57 | * | ||
58 | * * {@link CKEDITOR#DIALOG_RESIZE_NONE} | ||
59 | * * {@link CKEDITOR#DIALOG_RESIZE_WIDTH} | ||
60 | * * {@link CKEDITOR#DIALOG_RESIZE_HEIGHT} | ||
61 | * * {@link CKEDITOR#DIALOG_RESIZE_BOTH} | ||
62 | * | ||
63 | * @property {Number} [resizable=CKEDITOR.DIALOG_RESIZE_NONE] | ||
64 | */ | ||
65 | |||
66 | /** | ||
67 | * The minimum width of the dialog, in pixels. | ||
68 | * | ||
69 | * @property {Number} [minWidth=600] | ||
70 | */ | ||
71 | |||
72 | /** | ||
73 | * The minimum height of the dialog, in pixels. | ||
74 | * | ||
75 | * @property {Number} [minHeight=400] | ||
76 | */ | ||
77 | |||
78 | |||
79 | /** | ||
80 | * The initial width of the dialog, in pixels. | ||
81 | * | ||
82 | * @since 3.5.3 | ||
83 | * @property {Number} [width=CKEDITOR.dialog.definition#minWidth] | ||
84 | */ | ||
85 | |||
86 | /** | ||
87 | * The initial height of the dialog, in pixels. | ||
88 | * | ||
89 | * @since 3.5.3 | ||
90 | * @property {Number} [height=CKEDITOR.dialog.definition.minHeight] | ||
91 | */ | ||
92 | |||
93 | /** | ||
94 | * The buttons in the dialog, defined as an array of | ||
95 | * {@link CKEDITOR.dialog.definition.button} objects. | ||
96 | * | ||
97 | * @property {Array} [buttons=[ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]] | ||
98 | */ | ||
99 | |||
100 | /** | ||
101 | * The contents in the dialog, defined as an array of | ||
102 | * {@link CKEDITOR.dialog.definition.content} objects. Required. | ||
103 | * | ||
104 | * @property {Array} contents | ||
105 | */ | ||
106 | |||
107 | /** | ||
108 | * The function to execute when OK is pressed. | ||
109 | * | ||
110 | * @property {Function} onOk | ||
111 | */ | ||
112 | |||
113 | /** | ||
114 | * The function to execute when Cancel is pressed. | ||
115 | * | ||
116 | * @property {Function} onCancel | ||
117 | */ | ||
118 | |||
119 | /** | ||
120 | * The function to execute when the dialog is displayed for the first time. | ||
121 | * | ||
122 | * @property {Function} onLoad | ||
123 | */ | ||
124 | |||
125 | /** | ||
126 | * The function to execute when the dialog is loaded (executed every time the dialog is opened). | ||
127 | * | ||
128 | * @property {Function} onShow | ||
129 | */ | ||
130 | |||
131 | /** | ||
132 | * This class is not really part of the API. It just illustrates the properties | ||
133 | * that developers can use to define and create dialog content pages. | ||
134 | * | ||
135 | * @class CKEDITOR.dialog.definition.content. | ||
136 | */ | ||
137 | |||
138 | /** | ||
139 | * The id of the content page. | ||
140 | * | ||
141 | * @property {String} id | ||
142 | */ | ||
143 | |||
144 | /** | ||
145 | * The tab label of the content page. | ||
146 | * | ||
147 | * @property {String} label | ||
148 | */ | ||
149 | |||
150 | /** | ||
151 | * The popup message of the tab label. | ||
152 | * | ||
153 | * @property {String} title | ||
154 | */ | ||
155 | |||
156 | /** | ||
157 | * The CTRL hotkey for switching to the tab. | ||
158 | * | ||
159 | * contentDefinition.accessKey = 'Q'; // Switch to this page when CTRL-Q is pressed. | ||
160 | * | ||
161 | * @property {String} accessKey | ||
162 | */ | ||
163 | |||
164 | /** | ||
165 | * The UI elements contained in this content page, defined as an array of | ||
166 | * {@link CKEDITOR.dialog.definition.uiElement} objects. | ||
167 | * | ||
168 | * @property {Array} elements | ||
169 | */ | ||
170 | |||
171 | /** | ||
172 | * The definition of user interface element (textarea, radio etc). | ||
173 | * | ||
174 | * This class is not really part of the API. It just illustrates the properties | ||
175 | * that developers can use to define and create dialog UI elements. | ||
176 | * | ||
177 | * @class CKEDITOR.dialog.definition.uiElement | ||
178 | * @see CKEDITOR.ui.dialog.uiElement | ||
179 | */ | ||
180 | |||
181 | /** | ||
182 | * The id of the UI element. | ||
183 | * | ||
184 | * @property {String} id | ||
185 | */ | ||
186 | |||
187 | /** | ||
188 | * The type of the UI element. Required. | ||
189 | * | ||
190 | * @property {String} type | ||
191 | */ | ||
192 | |||
193 | /** | ||
194 | * The popup label of the UI element. | ||
195 | * | ||
196 | * @property {String} title | ||
197 | */ | ||
198 | |||
199 | /** | ||
200 | * The content that needs to be allowed to enable this UI element. | ||
201 | * All formats accepted by {@link CKEDITOR.filter#check} may be used. | ||
202 | * | ||
203 | * When all UI elements in a tab are disabled, this tab will be disabled automatically. | ||
204 | * | ||
205 | * @property {String/Object/CKEDITOR.style} requiredContent | ||
206 | */ | ||
207 | |||
208 | /** | ||
209 | * CSS class names to append to the UI element. | ||
210 | * | ||
211 | * @property {String} className | ||
212 | */ | ||
213 | |||
214 | /** | ||
215 | * Inline CSS classes to append to the UI element. | ||
216 | * | ||
217 | * @property {String} style | ||
218 | */ | ||
219 | |||
220 | /** | ||
221 | * Horizontal alignment (in container) of the UI element. | ||
222 | * | ||
223 | * @property {String} align | ||
224 | */ | ||
225 | |||
226 | /** | ||
227 | * Function to execute the first time the UI element is displayed. | ||
228 | * | ||
229 | * @property {Function} onLoad | ||
230 | */ | ||
231 | |||
232 | /** | ||
233 | * Function to execute whenever the UI element's parent dialog is displayed. | ||
234 | * | ||
235 | * @property {Function} onShow | ||
236 | */ | ||
237 | |||
238 | /** | ||
239 | * Function to execute whenever the UI element's parent dialog is closed. | ||
240 | * | ||
241 | * @property {Function} onHide | ||
242 | */ | ||
243 | |||
244 | /** | ||
245 | * Function to execute whenever the UI element's parent | ||
246 | * dialog's {@link CKEDITOR.dialog#setupContent} method is executed. | ||
247 | * It usually takes care of the respective UI element as a standalone element. | ||
248 | * | ||
249 | * @property {Function} setup | ||
250 | */ | ||
251 | |||
252 | /** | ||
253 | * Function to execute whenever the UI element's parent | ||
254 | * dialog's {@link CKEDITOR.dialog#commitContent} method is executed. | ||
255 | * It usually takes care of the respective UI element as a standalone element. | ||
256 | * | ||
257 | * @property {Function} commit | ||
258 | */ | ||
259 | |||
260 | // ----- hbox ----------------------------------------------------------------- | ||
261 | |||
262 | /** | ||
263 | * Horizontal layout box for dialog UI elements, auto-expends to available width of container. | ||
264 | * | ||
265 | * This class is not really part of the API. It just illustrates the properties | ||
266 | * that developers can use to define and create horizontal layouts. | ||
267 | * | ||
268 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.hbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
269 | * | ||
270 | * // There is no constructor for this class, the user just has to define an | ||
271 | * // object with the appropriate properties. | ||
272 | * | ||
273 | * // Example: | ||
274 | * { | ||
275 | * type: 'hbox', | ||
276 | * widths: [ '25%', '25%', '50%' ], | ||
277 | * children: [ | ||
278 | * { | ||
279 | * type: 'text', | ||
280 | * id: 'id1', | ||
281 | * width: '40px', | ||
282 | * }, | ||
283 | * { | ||
284 | * type: 'text', | ||
285 | * id: 'id2', | ||
286 | * width: '40px', | ||
287 | * }, | ||
288 | * { | ||
289 | * type: 'text', | ||
290 | * id: 'id3' | ||
291 | * } | ||
292 | * ] | ||
293 | * } | ||
294 | * | ||
295 | * @class CKEDITOR.dialog.definition.hbox | ||
296 | * @extends CKEDITOR.dialog.definition.uiElement | ||
297 | */ | ||
298 | |||
299 | /** | ||
300 | * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. | ||
301 | * | ||
302 | * @property {Array} children | ||
303 | */ | ||
304 | |||
305 | /** | ||
306 | * (Optional) The widths of child cells. | ||
307 | * | ||
308 | * @property {Array} widths | ||
309 | */ | ||
310 | |||
311 | /** | ||
312 | * (Optional) The height of the layout. | ||
313 | * | ||
314 | * @property {Number} height | ||
315 | */ | ||
316 | |||
317 | /** | ||
318 | * The CSS styles to apply to this element. | ||
319 | * | ||
320 | * @property {String} styles | ||
321 | */ | ||
322 | |||
323 | /** | ||
324 | * (Optional) The padding width inside child cells. Example: 0, 1. | ||
325 | * | ||
326 | * @property {Number} padding | ||
327 | */ | ||
328 | |||
329 | /** | ||
330 | * (Optional) The alignment of the whole layout. Example: center, top. | ||
331 | * | ||
332 | * @property {String} align | ||
333 | */ | ||
334 | |||
335 | // ----- vbox ----------------------------------------------------------------- | ||
336 | |||
337 | /** | ||
338 | * Vertical layout box for dialog UI elements. | ||
339 | * | ||
340 | * This class is not really part of the API. It just illustrates the properties | ||
341 | * that developers can use to define and create vertical layouts. | ||
342 | * | ||
343 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.vbox} object and can | ||
344 | * be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
345 | * | ||
346 | * // There is no constructor for this class, the user just has to define an | ||
347 | * // object with the appropriate properties. | ||
348 | * | ||
349 | * // Example: | ||
350 | * { | ||
351 | * type: 'vbox', | ||
352 | * align: 'right', | ||
353 | * width: '200px', | ||
354 | * children: [ | ||
355 | * { | ||
356 | * type: 'text', | ||
357 | * id: 'age', | ||
358 | * label: 'Age' | ||
359 | * }, | ||
360 | * { | ||
361 | * type: 'text', | ||
362 | * id: 'sex', | ||
363 | * label: 'Sex' | ||
364 | * }, | ||
365 | * { | ||
366 | * type: 'text', | ||
367 | * id: 'nationality', | ||
368 | * label: 'Nationality' | ||
369 | * } | ||
370 | * ] | ||
371 | * } | ||
372 | * | ||
373 | * @class CKEDITOR.dialog.definition.vbox | ||
374 | * @extends CKEDITOR.dialog.definition.uiElement | ||
375 | */ | ||
376 | |||
377 | /** | ||
378 | * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. | ||
379 | * | ||
380 | * @property {Array} children | ||
381 | */ | ||
382 | |||
383 | /** | ||
384 | * (Optional) The width of the layout. | ||
385 | * | ||
386 | * @property {Array} width | ||
387 | */ | ||
388 | |||
389 | /** | ||
390 | * (Optional) The heights of individual cells. | ||
391 | * | ||
392 | * @property {Number} heights | ||
393 | */ | ||
394 | |||
395 | /** | ||
396 | * The CSS styles to apply to this element. | ||
397 | * | ||
398 | * @property {String} styles | ||
399 | */ | ||
400 | |||
401 | /** | ||
402 | * (Optional) The padding width inside child cells. Example: 0, 1. | ||
403 | * | ||
404 | * @property {Number} padding | ||
405 | */ | ||
406 | |||
407 | /** | ||
408 | * (Optional) The alignment of the whole layout. Example: center, top. | ||
409 | * | ||
410 | * @property {String} align | ||
411 | */ | ||
412 | |||
413 | /** | ||
414 | * (Optional) Whether the layout should expand vertically to fill its container. | ||
415 | * | ||
416 | * @property {Boolean} expand | ||
417 | */ | ||
418 | |||
419 | // ----- labeled element ------------------------------------------------------ | ||
420 | |||
421 | /** | ||
422 | * The definition of labeled user interface element (textarea, textInput etc). | ||
423 | * | ||
424 | * This class is not really part of the API. It just illustrates the properties | ||
425 | * that developers can use to define and create dialog UI elements. | ||
426 | * | ||
427 | * @class CKEDITOR.dialog.definition.labeledElement | ||
428 | * @extends CKEDITOR.dialog.definition.uiElement | ||
429 | * @see CKEDITOR.ui.dialog.labeledElement | ||
430 | */ | ||
431 | |||
432 | /** | ||
433 | * The label of the UI element. | ||
434 | * | ||
435 | * { | ||
436 | * type: 'text', | ||
437 | * label: 'My Label' | ||
438 | * } | ||
439 | * | ||
440 | * @property {String} label | ||
441 | */ | ||
442 | |||
443 | /** | ||
444 | * (Optional) Specify the layout of the label. Set to `'horizontal'` for horizontal layout. | ||
445 | * The default layout is vertical. | ||
446 | * | ||
447 | * { | ||
448 | * type: 'text', | ||
449 | * label: 'My Label', | ||
450 | * labelLayout: 'horizontal' | ||
451 | * } | ||
452 | * | ||
453 | * @property {String} labelLayout | ||
454 | */ | ||
455 | |||
456 | /** | ||
457 | * (Optional) Applies only to horizontal layouts: a two elements array of lengths to specify the widths of the | ||
458 | * label and the content element. See also {@link CKEDITOR.dialog.definition.labeledElement#labelLayout}. | ||
459 | * | ||
460 | * { | ||
461 | * type: 'text', | ||
462 | * label: 'My Label', | ||
463 | * labelLayout: 'horizontal', | ||
464 | * widths: [100, 200] | ||
465 | * } | ||
466 | * | ||
467 | * @property {Array} widths | ||
468 | */ | ||
469 | |||
470 | /** | ||
471 | * Specify the inline style of the uiElement label. | ||
472 | * | ||
473 | * { | ||
474 | * type: 'text', | ||
475 | * label: 'My Label', | ||
476 | * labelStyle: 'color: red' | ||
477 | * } | ||
478 | * | ||
479 | * @property {String} labelStyle | ||
480 | */ | ||
481 | |||
482 | |||
483 | /** | ||
484 | * Specify the inline style of the input element. | ||
485 | * | ||
486 | * { | ||
487 | * type: 'text', | ||
488 | * label: 'My Label', | ||
489 | * inputStyle: 'text-align: center' | ||
490 | * } | ||
491 | * | ||
492 | * @since 3.6.1 | ||
493 | * @property {String} inputStyle | ||
494 | */ | ||
495 | |||
496 | /** | ||
497 | * Specify the inline style of the input element container. | ||
498 | * | ||
499 | * { | ||
500 | * type: 'text', | ||
501 | * label: 'My Label', | ||
502 | * controlStyle: 'width: 3em' | ||
503 | * } | ||
504 | * | ||
505 | * @since 3.6.1 | ||
506 | * @property {String} controlStyle | ||
507 | */ | ||
508 | |||
509 | // ----- button --------------------------------------------------------------- | ||
510 | |||
511 | /** | ||
512 | * The definition of a button. | ||
513 | * | ||
514 | * This class is not really part of the API. It just illustrates the properties | ||
515 | * that developers can use to define and create buttons. | ||
516 | * | ||
517 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.button} object | ||
518 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
519 | * | ||
520 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
521 | * | ||
522 | * // There is no constructor for this class, the user just has to define an | ||
523 | * // object with the appropriate properties. | ||
524 | * | ||
525 | * // Example: | ||
526 | * { | ||
527 | * type: 'button', | ||
528 | * id: 'buttonId', | ||
529 | * label: 'Click me', | ||
530 | * title: 'My title', | ||
531 | * onClick: function() { | ||
532 | * // this = CKEDITOR.ui.dialog.button | ||
533 | * alert( 'Clicked: ' + this.id ); | ||
534 | * } | ||
535 | * } | ||
536 | * | ||
537 | * @class CKEDITOR.dialog.definition.button | ||
538 | * @extends CKEDITOR.dialog.definition.uiElement | ||
539 | */ | ||
540 | |||
541 | /** | ||
542 | * Whether the button is disabled. | ||
543 | * | ||
544 | * @property {Boolean} disabled | ||
545 | */ | ||
546 | |||
547 | /** | ||
548 | * The label of the UI element. | ||
549 | * | ||
550 | * @property {String} label | ||
551 | */ | ||
552 | |||
553 | // ----- checkbox ------ | ||
554 | /** | ||
555 | * The definition of a checkbox element. | ||
556 | * | ||
557 | * This class is not really part of the API. It just illustrates the properties | ||
558 | * that developers can use to define and create groups of checkbox buttons. | ||
559 | * | ||
560 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.checkbox} object | ||
561 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
562 | * | ||
563 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
564 | * | ||
565 | * // There is no constructor for this class, the user just has to define an | ||
566 | * // object with the appropriate properties. | ||
567 | * | ||
568 | * // Example: | ||
569 | * { | ||
570 | * type: 'checkbox', | ||
571 | * id: 'agree', | ||
572 | * label: 'I agree', | ||
573 | * 'default': 'checked', | ||
574 | * onClick: function() { | ||
575 | * // this = CKEDITOR.ui.dialog.checkbox | ||
576 | * alert( 'Checked: ' + this.getValue() ); | ||
577 | * } | ||
578 | * } | ||
579 | * | ||
580 | * @class CKEDITOR.dialog.definition.checkbox | ||
581 | * @extends CKEDITOR.dialog.definition.uiElement | ||
582 | */ | ||
583 | |||
584 | /** | ||
585 | * (Optional) The validation function. | ||
586 | * | ||
587 | * @property {Function} validate | ||
588 | */ | ||
589 | |||
590 | /** | ||
591 | * The label of the UI element. | ||
592 | * | ||
593 | * @property {String} label | ||
594 | */ | ||
595 | |||
596 | /** | ||
597 | * The default state. | ||
598 | * | ||
599 | * @property {String} [default='' (unchecked)] | ||
600 | */ | ||
601 | |||
602 | // ----- file ----------------------------------------------------------------- | ||
603 | |||
604 | /** | ||
605 | * The definition of a file upload input. | ||
606 | * | ||
607 | * This class is not really part of the API. It just illustrates the properties | ||
608 | * that developers can use to define and create file upload elements. | ||
609 | * | ||
610 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.file} object | ||
611 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
612 | * | ||
613 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
614 | * | ||
615 | * // There is no constructor for this class, the user just has to define an | ||
616 | * // object with the appropriate properties. | ||
617 | * | ||
618 | * // Example: | ||
619 | * { | ||
620 | * type: 'file', | ||
621 | * id: 'upload', | ||
622 | * label: 'Select file from your computer', | ||
623 | * size: 38 | ||
624 | * }, | ||
625 | * { | ||
626 | * type: 'fileButton', | ||
627 | * id: 'fileId', | ||
628 | * label: 'Upload file', | ||
629 | * 'for': [ 'tab1', 'upload' ], | ||
630 | * filebrowser: { | ||
631 | * onSelect: function( fileUrl, data ) { | ||
632 | * alert( 'Successfully uploaded: ' + fileUrl ); | ||
633 | * } | ||
634 | * } | ||
635 | * } | ||
636 | * | ||
637 | * @class CKEDITOR.dialog.definition.file | ||
638 | * @extends CKEDITOR.dialog.definition.labeledElement | ||
639 | */ | ||
640 | |||
641 | /** | ||
642 | * (Optional) The validation function. | ||
643 | * | ||
644 | * @property {Function} validate | ||
645 | */ | ||
646 | |||
647 | /** | ||
648 | * (Optional) The action attribute of the form element associated with this file upload input. | ||
649 | * If empty, CKEditor will use path to server connector for currently opened folder. | ||
650 | * | ||
651 | * @property {String} action | ||
652 | */ | ||
653 | |||
654 | /** | ||
655 | * The size of the UI element. | ||
656 | * | ||
657 | * @property {Number} size | ||
658 | */ | ||
659 | |||
660 | // ----- fileButton ----------------------------------------------------------- | ||
661 | |||
662 | /** | ||
663 | * The definition of a button for submitting the file in a file upload input. | ||
664 | * | ||
665 | * This class is not really part of the API. It just illustrates the properties | ||
666 | * that developers can use to define and create a button for submitting the file in a file upload input. | ||
667 | * | ||
668 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.fileButton} object | ||
669 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
670 | * | ||
671 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
672 | * | ||
673 | * @class CKEDITOR.dialog.definition.fileButton | ||
674 | * @extends CKEDITOR.dialog.definition.uiElement | ||
675 | */ | ||
676 | |||
677 | /** | ||
678 | * (Optional) The validation function. | ||
679 | * | ||
680 | * @property {Function} validate | ||
681 | */ | ||
682 | |||
683 | /** | ||
684 | * The label of the UI element. | ||
685 | * | ||
686 | * @property {String} label | ||
687 | */ | ||
688 | |||
689 | /** | ||
690 | * The instruction for CKEditor how to deal with file upload. | ||
691 | * By default, the file and fileButton elements will not work "as expected" if this attribute is not set. | ||
692 | * | ||
693 | * // Update field with id 'txtUrl' in the 'tab1' tab when file is uploaded. | ||
694 | * filebrowser: 'tab1:txtUrl' | ||
695 | * | ||
696 | * // Call custom onSelect function when file is successfully uploaded. | ||
697 | * filebrowser: { | ||
698 | * onSelect: function( fileUrl, data ) { | ||
699 | * alert( 'Successfully uploaded: ' + fileUrl ); | ||
700 | * } | ||
701 | * } | ||
702 | * | ||
703 | * @property {String} filebrowser/Object | ||
704 | */ | ||
705 | |||
706 | /** | ||
707 | * An array that contains pageId and elementId of the file upload input element for which this button is created. | ||
708 | * | ||
709 | * [ pageId, elementId ] | ||
710 | * | ||
711 | * @property {String} for | ||
712 | */ | ||
713 | |||
714 | // ----- html ----------------------------------------------------------------- | ||
715 | |||
716 | /** | ||
717 | * The definition of a raw HTML element. | ||
718 | * | ||
719 | * This class is not really part of the API. It just illustrates the properties | ||
720 | * that developers can use to define and create elements made from raw HTML code. | ||
721 | * | ||
722 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.html} object | ||
723 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
724 | * | ||
725 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
726 | * To access HTML elements use {@link CKEDITOR.dom.document#getById}. | ||
727 | * | ||
728 | * // There is no constructor for this class, the user just has to define an | ||
729 | * // object with the appropriate properties. | ||
730 | * | ||
731 | * // Example 1: | ||
732 | * { | ||
733 | * type: 'html', | ||
734 | * html: '<h3>This is some sample HTML content.</h3>' | ||
735 | * } | ||
736 | * | ||
737 | * // Example 2: | ||
738 | * // Complete sample with document.getById() call when the "Ok" button is clicked. | ||
739 | * var dialogDefinition = { | ||
740 | * title: 'Sample dialog', | ||
741 | * minWidth: 300, | ||
742 | * minHeight: 200, | ||
743 | * onOk: function() { | ||
744 | * // "this" is now a CKEDITOR.dialog object. | ||
745 | * var document = this.getElement().getDocument(); | ||
746 | * // document = CKEDITOR.dom.document | ||
747 | * var element = <b>document.getById( 'myDiv' );</b> | ||
748 | * if ( element ) | ||
749 | * alert( element.getHtml() ); | ||
750 | * }, | ||
751 | * contents: [ | ||
752 | * { | ||
753 | * id: 'tab1', | ||
754 | * label: '', | ||
755 | * title: '', | ||
756 | * elements: [ | ||
757 | * { | ||
758 | * type: 'html', | ||
759 | * html: '<div id="myDiv">Sample <b>text</b>.</div><div id="otherId">Another div.</div>' | ||
760 | * } | ||
761 | * ] | ||
762 | * } | ||
763 | * ], | ||
764 | * buttons: [ CKEDITOR.dialog.cancelButton, CKEDITOR.dialog.okButton ] | ||
765 | * }; | ||
766 | * | ||
767 | * @class CKEDITOR.dialog.definition.html | ||
768 | * @extends CKEDITOR.dialog.definition.uiElement | ||
769 | */ | ||
770 | |||
771 | /** | ||
772 | * (Required) HTML code of this element. | ||
773 | * | ||
774 | * @property {String} html | ||
775 | */ | ||
776 | |||
777 | // ----- radio ---------------------------------------------------------------- | ||
778 | |||
779 | /** | ||
780 | * The definition of a radio group. | ||
781 | * | ||
782 | * This class is not really part of the API. It just illustrates the properties | ||
783 | * that developers can use to define and create groups of radio buttons. | ||
784 | * | ||
785 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.radio} object | ||
786 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
787 | * | ||
788 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
789 | * | ||
790 | * // There is no constructor for this class, the user just has to define an | ||
791 | * // object with the appropriate properties. | ||
792 | * | ||
793 | * // Example: | ||
794 | * { | ||
795 | * type: 'radio', | ||
796 | * id: 'country', | ||
797 | * label: 'Which country is bigger', | ||
798 | * items: [ [ 'France', 'FR' ], [ 'Germany', 'DE' ] ], | ||
799 | * style: 'color: green', | ||
800 | * 'default': 'DE', | ||
801 | * onClick: function() { | ||
802 | * // this = CKEDITOR.ui.dialog.radio | ||
803 | * alert( 'Current value: ' + this.getValue() ); | ||
804 | * } | ||
805 | * } | ||
806 | * | ||
807 | * @class CKEDITOR.dialog.definition.radio | ||
808 | * @extends CKEDITOR.dialog.definition.labeledElement | ||
809 | */ | ||
810 | |||
811 | /** | ||
812 | * The default value. | ||
813 | * | ||
814 | * @property {String} default | ||
815 | */ | ||
816 | |||
817 | /** | ||
818 | * (Optional) The validation function. | ||
819 | * | ||
820 | * @property {Function} validate | ||
821 | */ | ||
822 | |||
823 | /** | ||
824 | * An array of options. Each option is a 1- or 2-item array of format `[ 'Description', 'Value' ]`. | ||
825 | * If `'Value'` is missing, then the value would be assumed to be the same as the description. | ||
826 | * | ||
827 | * @property {Array} items | ||
828 | */ | ||
829 | |||
830 | // ----- selectElement -------------------------------------------------------- | ||
831 | |||
832 | /** | ||
833 | * The definition of a select element. | ||
834 | * | ||
835 | * This class is not really part of the API. It just illustrates the properties | ||
836 | * that developers can use to define and create select elements. | ||
837 | * | ||
838 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.select} object | ||
839 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
840 | * | ||
841 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
842 | * | ||
843 | * // There is no constructor for this class, the user just has to define an | ||
844 | * // object with the appropriate properties. | ||
845 | * | ||
846 | * // Example: | ||
847 | * { | ||
848 | * type: 'select', | ||
849 | * id: 'sport', | ||
850 | * label: 'Select your favourite sport', | ||
851 | * items: [ [ 'Basketball' ], [ 'Baseball' ], [ 'Hockey' ], [ 'Football' ] ], | ||
852 | * 'default': 'Football', | ||
853 | * onChange: function( api ) { | ||
854 | * // this = CKEDITOR.ui.dialog.select | ||
855 | * alert( 'Current value: ' + this.getValue() ); | ||
856 | * } | ||
857 | * } | ||
858 | * | ||
859 | * @class CKEDITOR.dialog.definition.select | ||
860 | * @extends CKEDITOR.dialog.definition.labeledElement | ||
861 | */ | ||
862 | |||
863 | /** | ||
864 | * The default value. | ||
865 | * | ||
866 | * @property {String} default | ||
867 | */ | ||
868 | |||
869 | /** | ||
870 | * (Optional) The validation function. | ||
871 | * | ||
872 | * @property {Function} validate | ||
873 | */ | ||
874 | |||
875 | /** | ||
876 | * An array of options. Each option is a 1- or 2-item array of format `[ 'Description', 'Value' ]`. | ||
877 | * If `'Value'` is missing, then the value would be assumed to be the same as the description. | ||
878 | * | ||
879 | * @property {Array} items | ||
880 | */ | ||
881 | |||
882 | /** | ||
883 | * (Optional) Set this to true if you'd like to have a multiple-choice select box. | ||
884 | * | ||
885 | * @property {Boolean} [multiple=false] | ||
886 | */ | ||
887 | |||
888 | /** | ||
889 | * (Optional) The number of items to display in the select box. | ||
890 | * | ||
891 | * @property {Number} size | ||
892 | */ | ||
893 | |||
894 | // ----- textInput ------------------------------------------------------------ | ||
895 | |||
896 | /** | ||
897 | * The definition of a text field (single line). | ||
898 | * | ||
899 | * This class is not really part of the API. It just illustrates the properties | ||
900 | * that developers can use to define and create text fields. | ||
901 | * | ||
902 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textInput} object | ||
903 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
904 | * | ||
905 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
906 | * | ||
907 | * // There is no constructor for this class, the user just has to define an | ||
908 | * // object with the appropriate properties. | ||
909 | * | ||
910 | * { | ||
911 | * type: 'text', | ||
912 | * id: 'name', | ||
913 | * label: 'Your name', | ||
914 | * 'default': '', | ||
915 | * validate: function() { | ||
916 | * if ( !this.getValue() ) { | ||
917 | * api.openMsgDialog( '', 'Name cannot be empty.' ); | ||
918 | * return false; | ||
919 | * } | ||
920 | * } | ||
921 | * } | ||
922 | * | ||
923 | * @class CKEDITOR.dialog.definition.textInput | ||
924 | * @extends CKEDITOR.dialog.definition.labeledElement | ||
925 | */ | ||
926 | |||
927 | /** | ||
928 | * The default value. | ||
929 | * | ||
930 | * @property {String} default | ||
931 | */ | ||
932 | |||
933 | /** | ||
934 | * (Optional) The maximum length. | ||
935 | * | ||
936 | * @property {Number} maxLength | ||
937 | */ | ||
938 | |||
939 | /** | ||
940 | * (Optional) The size of the input field. | ||
941 | * | ||
942 | * @property {Number} size | ||
943 | */ | ||
944 | |||
945 | /** | ||
946 | * (Optional) The validation function. | ||
947 | * | ||
948 | * @property {Function} validate | ||
949 | */ | ||
950 | |||
951 | /** | ||
952 | * @property bidi | ||
953 | * @inheritdoc CKEDITOR.dialog.definition.textarea#bidi | ||
954 | */ | ||
955 | |||
956 | // ----- textarea ------------------------------------------------------------- | ||
957 | |||
958 | /** | ||
959 | * The definition of a text field (multiple lines). | ||
960 | * | ||
961 | * This class is not really part of the API. It just illustrates the properties | ||
962 | * that developers can use to define and create textarea. | ||
963 | * | ||
964 | * Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textarea} object | ||
965 | * and can be accessed with {@link CKEDITOR.dialog#getContentElement}. | ||
966 | * | ||
967 | * For a complete example of dialog definition, please check {@link CKEDITOR.dialog#add}. | ||
968 | * | ||
969 | * // There is no constructor for this class, the user just has to define an | ||
970 | * // object with the appropriate properties. | ||
971 | * | ||
972 | * // Example: | ||
973 | * { | ||
974 | * type: 'textarea', | ||
975 | * id: 'message', | ||
976 | * label: 'Your comment', | ||
977 | * 'default': '', | ||
978 | * validate: function() { | ||
979 | * if ( this.getValue().length < 5 ) { | ||
980 | * api.openMsgDialog( 'The comment is too short.' ); | ||
981 | * return false; | ||
982 | * } | ||
983 | * } | ||
984 | * } | ||
985 | * | ||
986 | * @class CKEDITOR.dialog.definition.textarea | ||
987 | * @extends CKEDITOR.dialog.definition.labeledElement | ||
988 | */ | ||
989 | |||
990 | /** | ||
991 | * The number of rows. | ||
992 | * | ||
993 | * @property {Number} rows | ||
994 | */ | ||
995 | |||
996 | /** | ||
997 | * The number of columns. | ||
998 | * | ||
999 | * @property {Number} cols | ||
1000 | */ | ||
1001 | |||
1002 | /** | ||
1003 | * (Optional) The validation function. | ||
1004 | * | ||
1005 | * @property {Function} validate | ||
1006 | */ | ||
1007 | |||
1008 | /** | ||
1009 | * The default value. | ||
1010 | * | ||
1011 | * @property {String} default | ||
1012 | */ | ||
1013 | |||
1014 | /** | ||
1015 | * Whether the text direction of this input should be togglable using the following keystrokes: | ||
1016 | * | ||
1017 | * * *Shift+Alt+End* – switch to Right-To-Left, | ||
1018 | * * *Shift+Alt+Home* – switch to Left-To-Right. | ||
1019 | * | ||
1020 | * By default the input will be loaded without any text direction set, which means that | ||
1021 | * the direction will be inherited from the editor's text direction. | ||
1022 | * | ||
1023 | * If the direction was set, a marker will be prepended to every non-empty value of this input: | ||
1024 | * | ||
1025 | * * [`\u202A`](http://unicode.org/cldr/utility/character.jsp?a=202A) – for Right-To-Left, | ||
1026 | * * [`\u202B`](http://unicode.org/cldr/utility/character.jsp?a=202B) – for Left-To-Right. | ||
1027 | * | ||
1028 | * This marker allows for restoring the same text direction upon the next dialog opening. | ||
1029 | * | ||
1030 | * @since 4.5 | ||
1031 | * @property {Boolean} bidi | ||
1032 | */ | ||
diff --git a/sources/plugins/dialog/plugin.js b/sources/plugins/dialog/plugin.js new file mode 100644 index 0000000..d6af6a2 --- /dev/null +++ b/sources/plugins/dialog/plugin.js | |||
@@ -0,0 +1,3399 @@ | |||
1 | /** | ||
2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | ||
4 | */ | ||
5 | |||
6 | /** | ||
7 | * @fileOverview The floating dialog plugin. | ||
8 | */ | ||
9 | |||
10 | /** | ||
11 | * No resize for this dialog. | ||
12 | * | ||
13 | * @readonly | ||
14 | * @property {Number} [=0] | ||
15 | * @member CKEDITOR | ||
16 | */ | ||
17 | CKEDITOR.DIALOG_RESIZE_NONE = 0; | ||
18 | |||
19 | /** | ||
20 | * Only allow horizontal resizing for this dialog, disable vertical resizing. | ||
21 | * | ||
22 | * @readonly | ||
23 | * @property {Number} [=1] | ||
24 | * @member CKEDITOR | ||
25 | */ | ||
26 | CKEDITOR.DIALOG_RESIZE_WIDTH = 1; | ||
27 | |||
28 | /** | ||
29 | * Only allow vertical resizing for this dialog, disable horizontal resizing. | ||
30 | * | ||
31 | * @readonly | ||
32 | * @property {Number} [=2] | ||
33 | * @member CKEDITOR | ||
34 | */ | ||
35 | CKEDITOR.DIALOG_RESIZE_HEIGHT = 2; | ||
36 | |||
37 | /** | ||
38 | * Allow the dialog to be resized in both directions. | ||
39 | * | ||
40 | * @readonly | ||
41 | * @property {Number} [=3] | ||
42 | * @member CKEDITOR | ||
43 | */ | ||
44 | CKEDITOR.DIALOG_RESIZE_BOTH = 3; | ||
45 | |||
46 | /** | ||
47 | * Dialog state when idle. | ||
48 | * | ||
49 | * @readonly | ||
50 | * @property {Number} [=1] | ||
51 | * @member CKEDITOR | ||
52 | */ | ||
53 | CKEDITOR.DIALOG_STATE_IDLE = 1; | ||
54 | |||
55 | /** | ||
56 | * Dialog state when busy. | ||
57 | * | ||
58 | * @readonly | ||
59 | * @property {Number} [=2] | ||
60 | * @member CKEDITOR | ||
61 | */ | ||
62 | CKEDITOR.DIALOG_STATE_BUSY = 2; | ||
63 | |||
64 | ( function() { | ||
65 | var cssLength = CKEDITOR.tools.cssLength; | ||
66 | |||
67 | function isTabVisible( tabId ) { | ||
68 | return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight; | ||
69 | } | ||
70 | |||
71 | function getPreviousVisibleTab() { | ||
72 | var tabId = this._.currentTabId, | ||
73 | length = this._.tabIdList.length, | ||
74 | tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length; | ||
75 | |||
76 | for ( var i = tabIndex - 1; i > tabIndex - length; i-- ) { | ||
77 | if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) | ||
78 | return this._.tabIdList[ i % length ]; | ||
79 | } | ||
80 | |||
81 | return null; | ||
82 | } | ||
83 | |||
84 | function getNextVisibleTab() { | ||
85 | var tabId = this._.currentTabId, | ||
86 | length = this._.tabIdList.length, | ||
87 | tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ); | ||
88 | |||
89 | for ( var i = tabIndex + 1; i < tabIndex + length; i++ ) { | ||
90 | if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) | ||
91 | return this._.tabIdList[ i % length ]; | ||
92 | } | ||
93 | |||
94 | return null; | ||
95 | } | ||
96 | |||
97 | |||
98 | function clearOrRecoverTextInputValue( container, isRecover ) { | ||
99 | var inputs = container.$.getElementsByTagName( 'input' ); | ||
100 | for ( var i = 0, length = inputs.length; i < length; i++ ) { | ||
101 | var item = new CKEDITOR.dom.element( inputs[ i ] ); | ||
102 | |||
103 | if ( item.getAttribute( 'type' ).toLowerCase() == 'text' ) { | ||
104 | if ( isRecover ) { | ||
105 | item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' ); | ||
106 | item.removeCustomData( 'fake_value' ); | ||
107 | } else { | ||
108 | item.setCustomData( 'fake_value', item.getAttribute( 'value' ) ); | ||
109 | item.setAttribute( 'value', '' ); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // Handle dialog element validation state UI changes. | ||
116 | function handleFieldValidated( isValid, msg ) { | ||
117 | var input = this.getInputElement(); | ||
118 | if ( input ) | ||
119 | isValid ? input.removeAttribute( 'aria-invalid' ) : input.setAttribute( 'aria-invalid', true ); | ||
120 | |||
121 | if ( !isValid ) { | ||
122 | if ( this.select ) | ||
123 | this.select(); | ||
124 | else | ||
125 | this.focus(); | ||
126 | } | ||
127 | |||
128 | msg && alert( msg ); // jshint ignore:line | ||
129 | |||
130 | this.fire( 'validated', { valid: isValid, msg: msg } ); | ||
131 | } | ||
132 | |||
133 | function resetField() { | ||
134 | var input = this.getInputElement(); | ||
135 | input && input.removeAttribute( 'aria-invalid' ); | ||
136 | } | ||
137 | |||
138 | var templateSource = '<div class="cke_reset_all {editorId} {editorDialogClass} {hidpi}' + | ||
139 | '" dir="{langDir}"' + | ||
140 | ' lang="{langCode}"' + | ||
141 | ' role="dialog"' + | ||
142 | ' aria-labelledby="cke_dialog_title_{id}"' + | ||
143 | '>' + | ||
144 | '<table class="cke_dialog ' + CKEDITOR.env.cssClass + ' cke_{langDir}"' + | ||
145 | ' style="position:absolute" role="presentation">' + | ||
146 | '<tr><td role="presentation">' + | ||
147 | '<div class="cke_dialog_body" role="presentation">' + | ||
148 | '<div id="cke_dialog_title_{id}" class="cke_dialog_title" role="presentation"></div>' + | ||
149 | '<a id="cke_dialog_close_button_{id}" class="cke_dialog_close_button" href="javascript:void(0)" title="{closeTitle}" role="button"><span class="cke_label">X</span></a>' + | ||
150 | '<div id="cke_dialog_tabs_{id}" class="cke_dialog_tabs" role="tablist"></div>' + | ||
151 | '<table class="cke_dialog_contents" role="presentation">' + | ||
152 | '<tr>' + | ||
153 | '<td id="cke_dialog_contents_{id}" class="cke_dialog_contents_body" role="presentation"></td>' + | ||
154 | '</tr>' + | ||
155 | '<tr>' + | ||
156 | '<td id="cke_dialog_footer_{id}" class="cke_dialog_footer" role="presentation"></td>' + | ||
157 | '</tr>' + | ||
158 | '</table>' + | ||
159 | '</div>' + | ||
160 | '</td></tr>' + | ||
161 | '</table>' + | ||
162 | '</div>'; | ||
163 | |||
164 | function buildDialog( editor ) { | ||
165 | var element = CKEDITOR.dom.element.createFromHtml( CKEDITOR.addTemplate( 'dialog', templateSource ).output( { | ||
166 | id: CKEDITOR.tools.getNextNumber(), | ||
167 | editorId: editor.id, | ||
168 | langDir: editor.lang.dir, | ||
169 | langCode: editor.langCode, | ||
170 | editorDialogClass: 'cke_editor_' + editor.name.replace( /\./g, '\\.' ) + '_dialog', | ||
171 | closeTitle: editor.lang.common.close, | ||
172 | hidpi: CKEDITOR.env.hidpi ? 'cke_hidpi' : '' | ||
173 | } ) ); | ||
174 | |||
175 | // TODO: Change this to getById(), so it'll support custom templates. | ||
176 | var body = element.getChild( [ 0, 0, 0, 0, 0 ] ), | ||
177 | title = body.getChild( 0 ), | ||
178 | close = body.getChild( 1 ); | ||
179 | |||
180 | // Don't allow dragging on dialog (#13184). | ||
181 | editor.plugins.clipboard && CKEDITOR.plugins.clipboard.preventDefaultDropOnElement( body ); | ||
182 | |||
183 | // IFrame shim for dialog that masks activeX in IE. (#7619) | ||
184 | if ( CKEDITOR.env.ie && !CKEDITOR.env.quirks && !CKEDITOR.env.edge ) { | ||
185 | var src = 'javascript:void(function(){' + encodeURIComponent( 'document.open();(' + CKEDITOR.tools.fixDomain + ')();document.close();' ) + '}())', // jshint ignore:line | ||
186 | iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' + | ||
187 | ' frameBorder="0"' + | ||
188 | ' class="cke_iframe_shim"' + | ||
189 | ' src="' + src + '"' + | ||
190 | ' tabIndex="-1"' + | ||
191 | '></iframe>' ); | ||
192 | iframe.appendTo( body.getParent() ); | ||
193 | } | ||
194 | |||
195 | // Make the Title and Close Button unselectable. | ||
196 | title.unselectable(); | ||
197 | close.unselectable(); | ||
198 | |||
199 | return { | ||
200 | element: element, | ||
201 | parts: { | ||
202 | dialog: element.getChild( 0 ), | ||
203 | title: title, | ||
204 | close: close, | ||
205 | tabs: body.getChild( 2 ), | ||
206 | contents: body.getChild( [ 3, 0, 0, 0 ] ), | ||
207 | footer: body.getChild( [ 3, 0, 1, 0 ] ) | ||
208 | } | ||
209 | }; | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * This is the base class for runtime dialog objects. An instance of this | ||
214 | * class represents a single named dialog for a single editor instance. | ||
215 | * | ||
216 | * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' ); | ||
217 | * | ||
218 | * @class | ||
219 | * @constructor Creates a dialog class instance. | ||
220 | * @param {Object} editor The editor which created the dialog. | ||
221 | * @param {String} dialogName The dialog's registered name. | ||
222 | */ | ||
223 | CKEDITOR.dialog = function( editor, dialogName ) { | ||
224 | // Load the dialog definition. | ||
225 | var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], | ||
226 | defaultDefinition = CKEDITOR.tools.clone( defaultDialogDefinition ), | ||
227 | buttonsOrder = editor.config.dialog_buttonsOrder || 'OS', | ||
228 | dir = editor.lang.dir, | ||
229 | tabsToRemove = {}, | ||
230 | i, processed, stopPropagation; | ||
231 | |||
232 | if ( ( buttonsOrder == 'OS' && CKEDITOR.env.mac ) || // The buttons in MacOS Apps are in reverse order (#4750) | ||
233 | ( buttonsOrder == 'rtl' && dir == 'ltr' ) || ( buttonsOrder == 'ltr' && dir == 'rtl' ) ) | ||
234 | defaultDefinition.buttons.reverse(); | ||
235 | |||
236 | |||
237 | // Completes the definition with the default values. | ||
238 | definition = CKEDITOR.tools.extend( definition( editor ), defaultDefinition ); | ||
239 | |||
240 | // Clone a functionally independent copy for this dialog. | ||
241 | definition = CKEDITOR.tools.clone( definition ); | ||
242 | |||
243 | // Create a complex definition object, extending it with the API | ||
244 | // functions. | ||
245 | definition = new definitionObject( this, definition ); | ||
246 | |||
247 | var themeBuilt = buildDialog( editor ); | ||
248 | |||
249 | // Initialize some basic parameters. | ||
250 | this._ = { | ||
251 | editor: editor, | ||
252 | element: themeBuilt.element, | ||
253 | name: dialogName, | ||
254 | contentSize: { width: 0, height: 0 }, | ||
255 | size: { width: 0, height: 0 }, | ||
256 | contents: {}, | ||
257 | buttons: {}, | ||
258 | accessKeyMap: {}, | ||
259 | |||
260 | // Initialize the tab and page map. | ||
261 | tabs: {}, | ||
262 | tabIdList: [], | ||
263 | currentTabId: null, | ||
264 | currentTabIndex: null, | ||
265 | pageCount: 0, | ||
266 | lastTab: null, | ||
267 | tabBarMode: false, | ||
268 | |||
269 | // Initialize the tab order array for input widgets. | ||
270 | focusList: [], | ||
271 | currentFocusIndex: 0, | ||
272 | hasFocus: false | ||
273 | }; | ||
274 | |||
275 | this.parts = themeBuilt.parts; | ||
276 | |||
277 | CKEDITOR.tools.setTimeout( function() { | ||
278 | editor.fire( 'ariaWidget', this.parts.contents ); | ||
279 | }, 0, this ); | ||
280 | |||
281 | // Set the startup styles for the dialog, avoiding it enlarging the | ||
282 | // page size on the dialog creation. | ||
283 | var startStyles = { | ||
284 | position: CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed', | ||
285 | top: 0, | ||
286 | visibility: 'hidden' | ||
287 | }; | ||
288 | |||
289 | startStyles[ dir == 'rtl' ? 'right' : 'left' ] = 0; | ||
290 | this.parts.dialog.setStyles( startStyles ); | ||
291 | |||
292 | |||
293 | // Call the CKEDITOR.event constructor to initialize this instance. | ||
294 | CKEDITOR.event.call( this ); | ||
295 | |||
296 | // Fire the "dialogDefinition" event, making it possible to customize | ||
297 | // the dialog definition. | ||
298 | this.definition = definition = CKEDITOR.fire( 'dialogDefinition', { | ||
299 | name: dialogName, | ||
300 | definition: definition | ||
301 | }, editor ).definition; | ||
302 | |||
303 | // Cache tabs that should be removed. | ||
304 | if ( !( 'removeDialogTabs' in editor._ ) && editor.config.removeDialogTabs ) { | ||
305 | var removeContents = editor.config.removeDialogTabs.split( ';' ); | ||
306 | |||
307 | for ( i = 0; i < removeContents.length; i++ ) { | ||
308 | var parts = removeContents[ i ].split( ':' ); | ||
309 | if ( parts.length == 2 ) { | ||
310 | var removeDialogName = parts[ 0 ]; | ||
311 | if ( !tabsToRemove[ removeDialogName ] ) | ||
312 | tabsToRemove[ removeDialogName ] = []; | ||
313 | tabsToRemove[ removeDialogName ].push( parts[ 1 ] ); | ||
314 | } | ||
315 | } | ||
316 | editor._.removeDialogTabs = tabsToRemove; | ||
317 | } | ||
318 | |||
319 | // Remove tabs of this dialog. | ||
320 | if ( editor._.removeDialogTabs && ( tabsToRemove = editor._.removeDialogTabs[ dialogName ] ) ) { | ||
321 | for ( i = 0; i < tabsToRemove.length; i++ ) | ||
322 | definition.removeContents( tabsToRemove[ i ] ); | ||
323 | } | ||
324 | |||
325 | // Initialize load, show, hide, ok and cancel events. | ||
326 | if ( definition.onLoad ) | ||
327 | this.on( 'load', definition.onLoad ); | ||
328 | |||
329 | if ( definition.onShow ) | ||
330 | this.on( 'show', definition.onShow ); | ||
331 | |||
332 | if ( definition.onHide ) | ||
333 | this.on( 'hide', definition.onHide ); | ||
334 | |||
335 | if ( definition.onOk ) { | ||
336 | this.on( 'ok', function( evt ) { | ||
337 | // Dialog confirm might probably introduce content changes (#5415). | ||
338 | editor.fire( 'saveSnapshot' ); | ||
339 | setTimeout( function() { | ||
340 | editor.fire( 'saveSnapshot' ); | ||
341 | }, 0 ); | ||
342 | if ( definition.onOk.call( this, evt ) === false ) | ||
343 | evt.data.hide = false; | ||
344 | } ); | ||
345 | } | ||
346 | |||
347 | // Set default dialog state. | ||
348 | this.state = CKEDITOR.DIALOG_STATE_IDLE; | ||
349 | |||
350 | if ( definition.onCancel ) { | ||
351 | this.on( 'cancel', function( evt ) { | ||
352 | if ( definition.onCancel.call( this, evt ) === false ) | ||
353 | evt.data.hide = false; | ||
354 | } ); | ||
355 | } | ||
356 | |||
357 | var me = this; | ||
358 | |||
359 | // Iterates over all items inside all content in the dialog, calling a | ||
360 | // function for each of them. | ||
361 | var iterContents = function( func ) { | ||
362 | var contents = me._.contents, | ||
363 | stop = false; | ||
364 | |||
365 | for ( var i in contents ) { | ||
366 | for ( var j in contents[ i ] ) { | ||
367 | stop = func.call( this, contents[ i ][ j ] ); | ||
368 | if ( stop ) | ||
369 | return; | ||
370 | } | ||
371 | } | ||
372 | }; | ||
373 | |||
374 | this.on( 'ok', function( evt ) { | ||
375 | iterContents( function( item ) { | ||
376 | if ( item.validate ) { | ||
377 | var retval = item.validate( this ), | ||
378 | invalid = ( typeof retval == 'string' ) || retval === false; | ||
379 | |||
380 | if ( invalid ) { | ||
381 | evt.data.hide = false; | ||
382 | evt.stop(); | ||
383 | } | ||
384 | |||
385 | handleFieldValidated.call( item, !invalid, typeof retval == 'string' ? retval : undefined ); | ||
386 | return invalid; | ||
387 | } | ||
388 | } ); | ||
389 | }, this, null, 0 ); | ||
390 | |||
391 | this.on( 'cancel', function( evt ) { | ||
392 | iterContents( function( item ) { | ||
393 | if ( item.isChanged() ) { | ||
394 | if ( !editor.config.dialog_noConfirmCancel && !confirm( editor.lang.common.confirmCancel ) ) // jshint ignore:line | ||
395 | evt.data.hide = false; | ||
396 | return true; | ||
397 | } | ||
398 | } ); | ||
399 | }, this, null, 0 ); | ||
400 | |||
401 | this.parts.close.on( 'click', function( evt ) { | ||
402 | if ( this.fire( 'cancel', { hide: true } ).hide !== false ) | ||
403 | this.hide(); | ||
404 | evt.data.preventDefault(); | ||
405 | }, this ); | ||
406 | |||
407 | // Sort focus list according to tab order definitions. | ||
408 | function setupFocus() { | ||
409 | var focusList = me._.focusList; | ||
410 | focusList.sort( function( a, b ) { | ||
411 | // Mimics browser tab order logics; | ||
412 | if ( a.tabIndex != b.tabIndex ) | ||
413 | return b.tabIndex - a.tabIndex; | ||
414 | // Sort is not stable in some browsers, | ||
415 | // fall-back the comparator to 'focusIndex'; | ||
416 | else | ||
417 | return a.focusIndex - b.focusIndex; | ||
418 | } ); | ||
419 | |||
420 | var size = focusList.length; | ||
421 | for ( var i = 0; i < size; i++ ) | ||
422 | focusList[ i ].focusIndex = i; | ||
423 | } | ||
424 | |||
425 | // Expects 1 or -1 as an offset, meaning direction of the offset change. | ||
426 | function changeFocus( offset ) { | ||
427 | var focusList = me._.focusList; | ||
428 | offset = offset || 0; | ||
429 | |||
430 | if ( focusList.length < 1 ) | ||
431 | return; | ||
432 | |||
433 | var startIndex = me._.currentFocusIndex; | ||
434 | |||
435 | if ( me._.tabBarMode && offset < 0 ) { | ||
436 | // If we are in tab mode, we need to mimic that we started tabbing back from the first | ||
437 | // focusList (so it will go to the last one). | ||
438 | startIndex = 0; | ||
439 | } | ||
440 | |||
441 | // Trigger the 'blur' event of any input element before anything, | ||
442 | // since certain UI updates may depend on it. | ||
443 | try { | ||
444 | focusList[ startIndex ].getInputElement().$.blur(); | ||
445 | } catch ( e ) {} | ||
446 | |||
447 | var currentIndex = startIndex, | ||
448 | hasTabs = me._.pageCount > 1; | ||
449 | |||
450 | do { | ||
451 | currentIndex = currentIndex + offset; | ||
452 | |||
453 | if ( hasTabs && !me._.tabBarMode && ( currentIndex == focusList.length || currentIndex == -1 ) ) { | ||
454 | // If the dialog was not in tab mode, then focus the first tab (#13027). | ||
455 | me._.tabBarMode = true; | ||
456 | me._.tabs[ me._.currentTabId ][ 0 ].focus(); | ||
457 | me._.currentFocusIndex = -1; | ||
458 | |||
459 | // Early return, in order to avoid accessing focusList[ -1 ]. | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | currentIndex = ( currentIndex + focusList.length ) % focusList.length; | ||
464 | |||
465 | if ( currentIndex == startIndex ) { | ||
466 | break; | ||
467 | } | ||
468 | } while ( offset && !focusList[ currentIndex ].isFocusable() ); | ||
469 | |||
470 | focusList[ currentIndex ].focus(); | ||
471 | |||
472 | // Select whole field content. | ||
473 | if ( focusList[ currentIndex ].type == 'text' ) | ||
474 | focusList[ currentIndex ].select(); | ||
475 | } | ||
476 | |||
477 | this.changeFocus = changeFocus; | ||
478 | |||
479 | |||
480 | function keydownHandler( evt ) { | ||
481 | // If I'm not the top dialog, ignore. | ||
482 | if ( me != CKEDITOR.dialog._.currentTop ) | ||
483 | return; | ||
484 | |||
485 | var keystroke = evt.data.getKeystroke(), | ||
486 | rtl = editor.lang.dir == 'rtl', | ||
487 | arrowKeys = [ 37, 38, 39, 40 ], | ||
488 | button; | ||
489 | |||
490 | processed = stopPropagation = 0; | ||
491 | |||
492 | if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 ) { | ||
493 | var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 ); | ||
494 | changeFocus( shiftPressed ? -1 : 1 ); | ||
495 | processed = 1; | ||
496 | } else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 ) { | ||
497 | // Alt-F10 puts focus into the current tab item in the tab bar. | ||
498 | me._.tabBarMode = true; | ||
499 | me._.tabs[ me._.currentTabId ][ 0 ].focus(); | ||
500 | me._.currentFocusIndex = -1; | ||
501 | processed = 1; | ||
502 | } else if ( CKEDITOR.tools.indexOf( arrowKeys, keystroke ) != -1 && me._.tabBarMode ) { | ||
503 | // Array with key codes that activate previous tab. | ||
504 | var prevKeyCodes = [ | ||
505 | // Depending on the lang dir: right or left key | ||
506 | rtl ? 39 : 37, | ||
507 | // Top/bot arrow: actually for both cases it's the same. | ||
508 | 38 | ||
509 | ], | ||
510 | nextId = CKEDITOR.tools.indexOf( prevKeyCodes, keystroke ) != -1 ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ); | ||
511 | |||
512 | me.selectPage( nextId ); | ||
513 | me._.tabs[ nextId ][ 0 ].focus(); | ||
514 | processed = 1; | ||
515 | } else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode ) { | ||
516 | this.selectPage( this._.currentTabId ); | ||
517 | this._.tabBarMode = false; | ||
518 | this._.currentFocusIndex = -1; | ||
519 | changeFocus( 1 ); | ||
520 | processed = 1; | ||
521 | } | ||
522 | // If user presses enter key in a text box, it implies clicking OK for the dialog. | ||
523 | else if ( keystroke == 13 /*ENTER*/ ) { | ||
524 | // Don't do that for a target that handles ENTER. | ||
525 | var target = evt.data.getTarget(); | ||
526 | if ( !target.is( 'a', 'button', 'select', 'textarea' ) && ( !target.is( 'input' ) || target.$.type != 'button' ) ) { | ||
527 | button = this.getButton( 'ok' ); | ||
528 | button && CKEDITOR.tools.setTimeout( button.click, 0, button ); | ||
529 | processed = 1; | ||
530 | } | ||
531 | stopPropagation = 1; // Always block the propagation (#4269) | ||
532 | } else if ( keystroke == 27 /*ESC*/ ) { | ||
533 | button = this.getButton( 'cancel' ); | ||
534 | |||
535 | // If there's a Cancel button, click it, else just fire the cancel event and hide the dialog. | ||
536 | if ( button ) | ||
537 | CKEDITOR.tools.setTimeout( button.click, 0, button ); | ||
538 | else { | ||
539 | if ( this.fire( 'cancel', { hide: true } ).hide !== false ) | ||
540 | this.hide(); | ||
541 | } | ||
542 | stopPropagation = 1; // Always block the propagation (#4269) | ||
543 | } else { | ||
544 | return; | ||
545 | } | ||
546 | |||
547 | keypressHandler( evt ); | ||
548 | } | ||
549 | |||
550 | function keypressHandler( evt ) { | ||
551 | if ( processed ) | ||
552 | evt.data.preventDefault( 1 ); | ||
553 | else if ( stopPropagation ) | ||
554 | evt.data.stopPropagation(); | ||
555 | } | ||
556 | |||
557 | var dialogElement = this._.element; | ||
558 | |||
559 | editor.focusManager.add( dialogElement, 1 ); | ||
560 | |||
561 | // Add the dialog keyboard handlers. | ||
562 | this.on( 'show', function() { | ||
563 | dialogElement.on( 'keydown', keydownHandler, this ); | ||
564 | |||
565 | // Some browsers instead, don't cancel key events in the keydown, but in the | ||
566 | // keypress. So we must do a longer trip in those cases. (#4531,#8985) | ||
567 | if ( CKEDITOR.env.gecko ) | ||
568 | dialogElement.on( 'keypress', keypressHandler, this ); | ||
569 | |||
570 | } ); | ||
571 | this.on( 'hide', function() { | ||
572 | dialogElement.removeListener( 'keydown', keydownHandler ); | ||
573 | if ( CKEDITOR.env.gecko ) | ||
574 | dialogElement.removeListener( 'keypress', keypressHandler ); | ||
575 | |||
576 | // Reset fields state when closing dialog. | ||
577 | iterContents( function( item ) { | ||
578 | resetField.apply( item ); | ||
579 | } ); | ||
580 | } ); | ||
581 | this.on( 'iframeAdded', function( evt ) { | ||
582 | var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document ); | ||
583 | doc.on( 'keydown', keydownHandler, this, null, 0 ); | ||
584 | } ); | ||
585 | |||
586 | // Auto-focus logic in dialog. | ||
587 | this.on( 'show', function() { | ||
588 | // Setup tabIndex on showing the dialog instead of on loading | ||
589 | // to allow dynamic tab order happen in dialog definition. | ||
590 | setupFocus(); | ||
591 | |||
592 | var hasTabs = me._.pageCount > 1; | ||
593 | |||
594 | if ( editor.config.dialog_startupFocusTab && hasTabs ) { | ||
595 | me._.tabBarMode = true; | ||
596 | me._.tabs[ me._.currentTabId ][ 0 ].focus(); | ||
597 | me._.currentFocusIndex = -1; | ||
598 | } else if ( !this._.hasFocus ) { | ||
599 | // http://dev.ckeditor.com/ticket/13114#comment:4. | ||
600 | this._.currentFocusIndex = hasTabs ? -1 : this._.focusList.length - 1; | ||
601 | |||
602 | // Decide where to put the initial focus. | ||
603 | if ( definition.onFocus ) { | ||
604 | var initialFocus = definition.onFocus.call( this ); | ||
605 | // Focus the field that the user specified. | ||
606 | initialFocus && initialFocus.focus(); | ||
607 | } | ||
608 | // Focus the first field in layout order. | ||
609 | else { | ||
610 | changeFocus( 1 ); | ||
611 | } | ||
612 | } | ||
613 | }, this, null, 0xffffffff ); | ||
614 | |||
615 | // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661). | ||
616 | // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken. | ||
617 | if ( CKEDITOR.env.ie6Compat ) { | ||
618 | this.on( 'load', function() { | ||
619 | var outer = this.getElement(), | ||
620 | inner = outer.getFirst(); | ||
621 | inner.remove(); | ||
622 | inner.appendTo( outer ); | ||
623 | }, this ); | ||
624 | } | ||
625 | |||
626 | initDragAndDrop( this ); | ||
627 | initResizeHandles( this ); | ||
628 | |||
629 | // Insert the title. | ||
630 | ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title ); | ||
631 | |||
632 | // Insert the tabs and contents. | ||
633 | for ( i = 0; i < definition.contents.length; i++ ) { | ||
634 | var page = definition.contents[ i ]; | ||
635 | page && this.addPage( page ); | ||
636 | } | ||
637 | |||
638 | this.parts.tabs.on( 'click', function( evt ) { | ||
639 | var target = evt.data.getTarget(); | ||
640 | // If we aren't inside a tab, bail out. | ||
641 | if ( target.hasClass( 'cke_dialog_tab' ) ) { | ||
642 | // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix. | ||
643 | var id = target.$.id; | ||
644 | this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) ); | ||
645 | |||
646 | if ( this._.tabBarMode ) { | ||
647 | this._.tabBarMode = false; | ||
648 | this._.currentFocusIndex = -1; | ||
649 | changeFocus( 1 ); | ||
650 | } | ||
651 | evt.data.preventDefault(); | ||
652 | } | ||
653 | }, this ); | ||
654 | |||
655 | // Insert buttons. | ||
656 | var buttonsHtml = [], | ||
657 | buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this, { | ||
658 | type: 'hbox', | ||
659 | className: 'cke_dialog_footer_buttons', | ||
660 | widths: [], | ||
661 | children: definition.buttons | ||
662 | }, buttonsHtml ).getChild(); | ||
663 | this.parts.footer.setHtml( buttonsHtml.join( '' ) ); | ||
664 | |||
665 | for ( i = 0; i < buttons.length; i++ ) | ||
666 | this._.buttons[ buttons[ i ].id ] = buttons[ i ]; | ||
667 | |||
668 | /** | ||
669 | * Current state of the dialog. Use the {@link #setState} method to update it. | ||
670 | * See the {@link #event-state} event to know more. | ||
671 | * | ||
672 | * @readonly | ||
673 | * @property {Number} [state=CKEDITOR.DIALOG_STATE_IDLE] | ||
674 | */ | ||
675 | }; | ||
676 | |||
677 | // Focusable interface. Use it via dialog.addFocusable. | ||
678 | function Focusable( dialog, element, index ) { | ||
679 | this.element = element; | ||
680 | this.focusIndex = index; | ||
681 | // TODO: support tabIndex for focusables. | ||
682 | this.tabIndex = 0; | ||
683 | this.isFocusable = function() { | ||
684 | return !element.getAttribute( 'disabled' ) && element.isVisible(); | ||
685 | }; | ||
686 | this.focus = function() { | ||
687 | dialog._.currentFocusIndex = this.focusIndex; | ||
688 | this.element.focus(); | ||
689 | }; | ||
690 | // Bind events | ||
691 | element.on( 'keydown', function( e ) { | ||
692 | if ( e.data.getKeystroke() in { 32: 1, 13: 1 } ) | ||
693 | this.fire( 'click' ); | ||
694 | } ); | ||
695 | element.on( 'focus', function() { | ||
696 | this.fire( 'mouseover' ); | ||
697 | } ); | ||
698 | element.on( 'blur', function() { | ||
699 | this.fire( 'mouseout' ); | ||
700 | } ); | ||
701 | } | ||
702 | |||
703 | // Re-layout the dialog on window resize. | ||
704 | function resizeWithWindow( dialog ) { | ||
705 | var win = CKEDITOR.document.getWindow(); | ||
706 | function resizeHandler() { | ||
707 | dialog.layout(); | ||
708 | } | ||
709 | win.on( 'resize', resizeHandler ); | ||
710 | dialog.on( 'hide', function() { | ||
711 | win.removeListener( 'resize', resizeHandler ); | ||
712 | } ); | ||
713 | } | ||
714 | |||
715 | CKEDITOR.dialog.prototype = { | ||
716 | destroy: function() { | ||
717 | this.hide(); | ||
718 | this._.element.remove(); | ||
719 | }, | ||
720 | |||
721 | /** | ||
722 | * Resizes the dialog. | ||
723 | * | ||
724 | * dialogObj.resize( 800, 640 ); | ||
725 | * | ||
726 | * @method | ||
727 | * @param {Number} width The width of the dialog in pixels. | ||
728 | * @param {Number} height The height of the dialog in pixels. | ||
729 | */ | ||
730 | resize: ( function() { | ||
731 | return function( width, height ) { | ||
732 | if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height ) | ||
733 | return; | ||
734 | |||
735 | CKEDITOR.dialog.fire( 'resize', { | ||
736 | dialog: this, | ||
737 | width: width, | ||
738 | height: height | ||
739 | }, this._.editor ); | ||
740 | |||
741 | this.fire( 'resize', { | ||
742 | width: width, | ||
743 | height: height | ||
744 | }, this._.editor ); | ||
745 | |||
746 | var contents = this.parts.contents; | ||
747 | contents.setStyles( { | ||
748 | width: width + 'px', | ||
749 | height: height + 'px' | ||
750 | } ); | ||
751 | |||
752 | // Update dialog position when dimension get changed in RTL. | ||
753 | if ( this._.editor.lang.dir == 'rtl' && this._.position ) | ||
754 | this._.position.x = CKEDITOR.document.getWindow().getViewPaneSize().width - this._.contentSize.width - parseInt( this._.element.getFirst().getStyle( 'right' ), 10 ); | ||
755 | |||
756 | this._.contentSize = { width: width, height: height }; | ||
757 | }; | ||
758 | } )(), | ||
759 | |||
760 | /** | ||
761 | * Gets the current size of the dialog in pixels. | ||
762 | * | ||
763 | * var width = dialogObj.getSize().width; | ||
764 | * | ||
765 | * @returns {Object} | ||
766 | * @returns {Number} return.width | ||
767 | * @returns {Number} return.height | ||
768 | */ | ||
769 | getSize: function() { | ||
770 | var element = this._.element.getFirst(); | ||
771 | return { width: element.$.offsetWidth || 0, height: element.$.offsetHeight || 0 }; | ||
772 | }, | ||
773 | |||
774 | /** | ||
775 | * Moves the dialog to an `(x, y)` coordinate relative to the window. | ||
776 | * | ||
777 | * dialogObj.move( 10, 40 ); | ||
778 | * | ||
779 | * @method | ||
780 | * @param {Number} x The target x-coordinate. | ||
781 | * @param {Number} y The target y-coordinate. | ||
782 | * @param {Boolean} save Flag indicate whether the dialog position should be remembered on next open up. | ||
783 | */ | ||
784 | move: function( x, y, save ) { | ||
785 | |||
786 | // The dialog may be fixed positioned or absolute positioned. Ask the | ||
787 | // browser what is the current situation first. | ||
788 | var element = this._.element.getFirst(), rtl = this._.editor.lang.dir == 'rtl'; | ||
789 | var isFixed = element.getComputedStyle( 'position' ) == 'fixed'; | ||
790 | |||
791 | // (#8888) In some cases of a very small viewport, dialog is incorrectly | ||
792 | // positioned in IE7. It also happens that it remains sticky and user cannot | ||
793 | // scroll down/up to reveal dialog's content below/above the viewport; this is | ||
794 | // cumbersome. | ||
795 | // The only way to fix this is to move mouse out of the browser and | ||
796 | // go back to see that dialog position is automagically fixed. No events, | ||
797 | // no style change - pure magic. This is a IE7 rendering issue, which can be | ||
798 | // fixed with dummy style redraw on each move. | ||
799 | if ( CKEDITOR.env.ie ) | ||
800 | element.setStyle( 'zoom', '100%' ); | ||
801 | |||
802 | if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y ) | ||
803 | return; | ||
804 | |||
805 | // Save the current position. | ||
806 | this._.position = { x: x, y: y }; | ||
807 | |||
808 | // If not fixed positioned, add scroll position to the coordinates. | ||
809 | if ( !isFixed ) { | ||
810 | var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition(); | ||
811 | x += scrollPosition.x; | ||
812 | y += scrollPosition.y; | ||
813 | } | ||
814 | |||
815 | // Translate coordinate for RTL. | ||
816 | if ( rtl ) { | ||
817 | var dialogSize = this.getSize(), viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(); | ||
818 | x = viewPaneSize.width - dialogSize.width - x; | ||
819 | } | ||
820 | |||
821 | var styles = { 'top': ( y > 0 ? y : 0 ) + 'px' }; | ||
822 | styles[ rtl ? 'right' : 'left' ] = ( x > 0 ? x : 0 ) + 'px'; | ||
823 | |||
824 | element.setStyles( styles ); | ||
825 | |||
826 | save && ( this._.moved = 1 ); | ||
827 | }, | ||
828 | |||
829 | /** | ||
830 | * Gets the dialog's position in the window. | ||
831 | * | ||
832 | * var dialogX = dialogObj.getPosition().x; | ||
833 | * | ||
834 | * @returns {Object} | ||
835 | * @returns {Number} return.x | ||
836 | * @returns {Number} return.y | ||
837 | */ | ||
838 | getPosition: function() { | ||
839 | return CKEDITOR.tools.extend( {}, this._.position ); | ||
840 | }, | ||
841 | |||
842 | /** | ||
843 | * Shows the dialog box. | ||
844 | * | ||
845 | * dialogObj.show(); | ||
846 | */ | ||
847 | show: function() { | ||
848 | // Insert the dialog's element to the root document. | ||
849 | var element = this._.element; | ||
850 | var definition = this.definition; | ||
851 | if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) ) | ||
852 | element.appendTo( CKEDITOR.document.getBody() ); | ||
853 | else | ||
854 | element.setStyle( 'display', 'block' ); | ||
855 | |||
856 | // First, set the dialog to an appropriate size. | ||
857 | this.resize( | ||
858 | this._.contentSize && this._.contentSize.width || definition.width || definition.minWidth, | ||
859 | this._.contentSize && this._.contentSize.height || definition.height || definition.minHeight | ||
860 | ); | ||
861 | |||
862 | // Reset all inputs back to their default value. | ||
863 | this.reset(); | ||
864 | |||
865 | // Select the first tab by default. | ||
866 | this.selectPage( this.definition.contents[ 0 ].id ); | ||
867 | |||
868 | // Set z-index. | ||
869 | if ( CKEDITOR.dialog._.currentZIndex === null ) | ||
870 | CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex; | ||
871 | this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 ); | ||
872 | |||
873 | // Maintain the dialog ordering and dialog cover. | ||
874 | if ( CKEDITOR.dialog._.currentTop === null ) { | ||
875 | CKEDITOR.dialog._.currentTop = this; | ||
876 | this._.parentDialog = null; | ||
877 | showCover( this._.editor ); | ||
878 | |||
879 | } else { | ||
880 | this._.parentDialog = CKEDITOR.dialog._.currentTop; | ||
881 | var parentElement = this._.parentDialog.getElement().getFirst(); | ||
882 | parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 ); | ||
883 | CKEDITOR.dialog._.currentTop = this; | ||
884 | } | ||
885 | |||
886 | element.on( 'keydown', accessKeyDownHandler ); | ||
887 | element.on( 'keyup', accessKeyUpHandler ); | ||
888 | |||
889 | // Reset the hasFocus state. | ||
890 | this._.hasFocus = false; | ||
891 | |||
892 | for ( var i in definition.contents ) { | ||
893 | if ( !definition.contents[ i ] ) | ||
894 | continue; | ||
895 | |||
896 | var content = definition.contents[ i ], | ||
897 | tab = this._.tabs[ content.id ], | ||
898 | requiredContent = content.requiredContent, | ||
899 | enableElements = 0; | ||
900 | |||
901 | if ( !tab ) | ||
902 | continue; | ||
903 | |||
904 | for ( var j in this._.contents[ content.id ] ) { | ||
905 | var elem = this._.contents[ content.id ][ j ]; | ||
906 | |||
907 | if ( elem.type == 'hbox' || elem.type == 'vbox' || !elem.getInputElement() ) | ||
908 | continue; | ||
909 | |||
910 | if ( elem.requiredContent && !this._.editor.activeFilter.check( elem.requiredContent ) ) | ||
911 | elem.disable(); | ||
912 | else { | ||
913 | elem.enable(); | ||
914 | enableElements++; | ||
915 | } | ||
916 | } | ||
917 | |||
918 | if ( !enableElements || ( requiredContent && !this._.editor.activeFilter.check( requiredContent ) ) ) | ||
919 | tab[ 0 ].addClass( 'cke_dialog_tab_disabled' ); | ||
920 | else | ||
921 | tab[ 0 ].removeClass( 'cke_dialog_tab_disabled' ); | ||
922 | } | ||
923 | |||
924 | CKEDITOR.tools.setTimeout( function() { | ||
925 | this.layout(); | ||
926 | resizeWithWindow( this ); | ||
927 | |||
928 | this.parts.dialog.setStyle( 'visibility', '' ); | ||
929 | |||
930 | // Execute onLoad for the first show. | ||
931 | this.fireOnce( 'load', {} ); | ||
932 | CKEDITOR.ui.fire( 'ready', this ); | ||
933 | |||
934 | this.fire( 'show', {} ); | ||
935 | this._.editor.fire( 'dialogShow', this ); | ||
936 | |||
937 | if ( !this._.parentDialog ) | ||
938 | this._.editor.focusManager.lock(); | ||
939 | |||
940 | // Save the initial values of the dialog. | ||
941 | this.foreach( function( contentObj ) { | ||
942 | contentObj.setInitValue && contentObj.setInitValue(); | ||
943 | } ); | ||
944 | |||
945 | }, 100, this ); | ||
946 | }, | ||
947 | |||
948 | /** | ||
949 | * Rearrange the dialog to its previous position or the middle of the window. | ||
950 | * | ||
951 | * @since 3.5 | ||
952 | */ | ||
953 | layout: function() { | ||
954 | var el = this.parts.dialog; | ||
955 | var dialogSize = this.getSize(); | ||
956 | var win = CKEDITOR.document.getWindow(), | ||
957 | viewSize = win.getViewPaneSize(); | ||
958 | |||
959 | var posX = ( viewSize.width - dialogSize.width ) / 2, | ||
960 | posY = ( viewSize.height - dialogSize.height ) / 2; | ||
961 | |||
962 | // Switch to absolute position when viewport is smaller than dialog size. | ||
963 | if ( !CKEDITOR.env.ie6Compat ) { | ||
964 | if ( dialogSize.height + ( posY > 0 ? posY : 0 ) > viewSize.height || dialogSize.width + ( posX > 0 ? posX : 0 ) > viewSize.width ) { | ||
965 | el.setStyle( 'position', 'absolute' ); | ||
966 | } else { | ||
967 | el.setStyle( 'position', 'fixed' ); | ||
968 | } | ||
969 | } | ||
970 | |||
971 | this.move( this._.moved ? this._.position.x : posX, this._.moved ? this._.position.y : posY ); | ||
972 | }, | ||
973 | |||
974 | /** | ||
975 | * Executes a function for each UI element. | ||
976 | * | ||
977 | * @param {Function} fn Function to execute for each UI element. | ||
978 | * @returns {CKEDITOR.dialog} The current dialog object. | ||
979 | */ | ||
980 | foreach: function( fn ) { | ||
981 | for ( var i in this._.contents ) { | ||
982 | for ( var j in this._.contents[ i ] ) { | ||
983 | fn.call( this, this._.contents[i][j] ); | ||
984 | } | ||
985 | } | ||
986 | |||
987 | return this; | ||
988 | }, | ||
989 | |||
990 | /** | ||
991 | * Resets all input values in the dialog. | ||
992 | * | ||
993 | * dialogObj.reset(); | ||
994 | * | ||
995 | * @method | ||
996 | * @chainable | ||
997 | */ | ||
998 | reset: ( function() { | ||
999 | var fn = function( widget ) { | ||
1000 | if ( widget.reset ) | ||
1001 | widget.reset( 1 ); | ||
1002 | }; | ||
1003 | return function() { | ||
1004 | this.foreach( fn ); | ||
1005 | return this; | ||
1006 | }; | ||
1007 | } )(), | ||
1008 | |||
1009 | |||
1010 | /** | ||
1011 | * Calls the {@link CKEDITOR.dialog.definition.uiElement#setup} method of each | ||
1012 | * of the UI elements, with the arguments passed through it. | ||
1013 | * It is usually being called when the dialog is opened, to put the initial value inside the field. | ||
1014 | * | ||
1015 | * dialogObj.setupContent(); | ||
1016 | * | ||
1017 | * var timestamp = ( new Date() ).valueOf(); | ||
1018 | * dialogObj.setupContent( timestamp ); | ||
1019 | */ | ||
1020 | setupContent: function() { | ||
1021 | var args = arguments; | ||
1022 | this.foreach( function( widget ) { | ||
1023 | if ( widget.setup ) | ||
1024 | widget.setup.apply( widget, args ); | ||
1025 | } ); | ||
1026 | }, | ||
1027 | |||
1028 | /** | ||
1029 | * Calls the {@link CKEDITOR.dialog.definition.uiElement#commit} method of each | ||
1030 | * of the UI elements, with the arguments passed through it. | ||
1031 | * It is usually being called when the user confirms the dialog, to process the values. | ||
1032 | * | ||
1033 | * dialogObj.commitContent(); | ||
1034 | * | ||
1035 | * var timestamp = ( new Date() ).valueOf(); | ||
1036 | * dialogObj.commitContent( timestamp ); | ||
1037 | */ | ||
1038 | commitContent: function() { | ||
1039 | var args = arguments; | ||
1040 | this.foreach( function( widget ) { | ||
1041 | // Make sure IE triggers "change" event on last focused input before closing the dialog. (#7915) | ||
1042 | if ( CKEDITOR.env.ie && this._.currentFocusIndex == widget.focusIndex ) | ||
1043 | widget.getInputElement().$.blur(); | ||
1044 | |||
1045 | if ( widget.commit ) | ||
1046 | widget.commit.apply( widget, args ); | ||
1047 | } ); | ||
1048 | }, | ||
1049 | |||
1050 | /** | ||
1051 | * Hides the dialog box. | ||
1052 | * | ||
1053 | * dialogObj.hide(); | ||
1054 | */ | ||
1055 | hide: function() { | ||
1056 | if ( !this.parts.dialog.isVisible() ) | ||
1057 | return; | ||
1058 | |||
1059 | this.fire( 'hide', {} ); | ||
1060 | this._.editor.fire( 'dialogHide', this ); | ||
1061 | // Reset the tab page. | ||
1062 | this.selectPage( this._.tabIdList[ 0 ] ); | ||
1063 | var element = this._.element; | ||
1064 | element.setStyle( 'display', 'none' ); | ||
1065 | this.parts.dialog.setStyle( 'visibility', 'hidden' ); | ||
1066 | // Unregister all access keys associated with this dialog. | ||
1067 | unregisterAccessKey( this ); | ||
1068 | |||
1069 | // Close any child(top) dialogs first. | ||
1070 | while ( CKEDITOR.dialog._.currentTop != this ) | ||
1071 | CKEDITOR.dialog._.currentTop.hide(); | ||
1072 | |||
1073 | // Maintain dialog ordering and remove cover if needed. | ||
1074 | if ( !this._.parentDialog ) | ||
1075 | hideCover( this._.editor ); | ||
1076 | else { | ||
1077 | var parentElement = this._.parentDialog.getElement().getFirst(); | ||
1078 | parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) ); | ||
1079 | } | ||
1080 | CKEDITOR.dialog._.currentTop = this._.parentDialog; | ||
1081 | |||
1082 | // Deduct or clear the z-index. | ||
1083 | if ( !this._.parentDialog ) { | ||
1084 | CKEDITOR.dialog._.currentZIndex = null; | ||
1085 | |||
1086 | // Remove access key handlers. | ||
1087 | element.removeListener( 'keydown', accessKeyDownHandler ); | ||
1088 | element.removeListener( 'keyup', accessKeyUpHandler ); | ||
1089 | |||
1090 | var editor = this._.editor; | ||
1091 | editor.focus(); | ||
1092 | |||
1093 | // Give a while before unlock, waiting for focus to return to the editable. (#172) | ||
1094 | setTimeout( function() { | ||
1095 | editor.focusManager.unlock(); | ||
1096 | |||
1097 | // Fixed iOS focus issue (#12381). | ||
1098 | // Keep in mind that editor.focus() does not work in this case. | ||
1099 | if ( CKEDITOR.env.iOS ) { | ||
1100 | editor.window.focus(); | ||
1101 | } | ||
1102 | }, 0 ); | ||
1103 | |||
1104 | } else { | ||
1105 | CKEDITOR.dialog._.currentZIndex -= 10; | ||
1106 | } | ||
1107 | |||
1108 | delete this._.parentDialog; | ||
1109 | // Reset the initial values of the dialog. | ||
1110 | this.foreach( function( contentObj ) { | ||
1111 | contentObj.resetInitValue && contentObj.resetInitValue(); | ||
1112 | } ); | ||
1113 | |||
1114 | // Reset dialog state back to IDLE, if busy (#13213). | ||
1115 | this.setState( CKEDITOR.DIALOG_STATE_IDLE ); | ||
1116 | }, | ||
1117 | |||
1118 | /** | ||
1119 | * Adds a tabbed page into the dialog. | ||
1120 | * | ||
1121 | * @param {Object} contents Content definition. | ||
1122 | */ | ||
1123 | addPage: function( contents ) { | ||
1124 | if ( contents.requiredContent && !this._.editor.filter.check( contents.requiredContent ) ) | ||
1125 | return; | ||
1126 | |||
1127 | var pageHtml = [], | ||
1128 | titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '', | ||
1129 | vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this, { | ||
1130 | type: 'vbox', | ||
1131 | className: 'cke_dialog_page_contents', | ||
1132 | children: contents.elements, | ||
1133 | expand: !!contents.expand, | ||
1134 | padding: contents.padding, | ||
1135 | style: contents.style || 'width: 100%;' | ||
1136 | }, pageHtml ); | ||
1137 | |||
1138 | var contentMap = this._.contents[ contents.id ] = {}, | ||
1139 | cursor, | ||
1140 | children = vbox.getChild(), | ||
1141 | enabledFields = 0; | ||
1142 | |||
1143 | while ( ( cursor = children.shift() ) ) { | ||
1144 | // Count all allowed fields. | ||
1145 | if ( !cursor.notAllowed && cursor.type != 'hbox' && cursor.type != 'vbox' ) | ||
1146 | enabledFields++; | ||
1147 | |||
1148 | contentMap[ cursor.id ] = cursor; | ||
1149 | if ( typeof cursor.getChild == 'function' ) | ||
1150 | children.push.apply( children, cursor.getChild() ); | ||
1151 | } | ||
1152 | |||
1153 | // If all fields are disabled (because they are not allowed) hide this tab. | ||
1154 | if ( !enabledFields ) | ||
1155 | contents.hidden = true; | ||
1156 | |||
1157 | // Create the HTML for the tab and the content block. | ||
1158 | var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) ); | ||
1159 | page.setAttribute( 'role', 'tabpanel' ); | ||
1160 | |||
1161 | var env = CKEDITOR.env; | ||
1162 | var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(), | ||
1163 | tab = CKEDITOR.dom.element.createFromHtml( [ | ||
1164 | '<a class="cke_dialog_tab"', | ||
1165 | ( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ), | ||
1166 | titleHtml, | ||
1167 | ( !!contents.hidden ? ' style="display:none"' : '' ), | ||
1168 | ' id="', tabId, '"', | ||
1169 | env.gecko && !env.hc ? '' : ' href="javascript:void(0)"', | ||
1170 | ' tabIndex="-1"', | ||
1171 | ' hidefocus="true"', | ||
1172 | ' role="tab">', | ||
1173 | contents.label, | ||
1174 | '</a>' | ||
1175 | ].join( '' ) ); | ||
1176 | |||
1177 | page.setAttribute( 'aria-labelledby', tabId ); | ||
1178 | |||
1179 | // Take records for the tabs and elements created. | ||
1180 | this._.tabs[ contents.id ] = [ tab, page ]; | ||
1181 | this._.tabIdList.push( contents.id ); | ||
1182 | !contents.hidden && this._.pageCount++; | ||
1183 | this._.lastTab = tab; | ||
1184 | this.updateStyle(); | ||
1185 | |||
1186 | // Attach the DOM nodes. | ||
1187 | |||
1188 | page.setAttribute( 'name', contents.id ); | ||
1189 | page.appendTo( this.parts.contents ); | ||
1190 | |||
1191 | tab.unselectable(); | ||
1192 | this.parts.tabs.append( tab ); | ||
1193 | |||
1194 | // Add access key handlers if access key is defined. | ||
1195 | if ( contents.accessKey ) { | ||
1196 | registerAccessKey( this, this, 'CTRL+' + contents.accessKey, tabAccessKeyDown, tabAccessKeyUp ); | ||
1197 | this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id; | ||
1198 | } | ||
1199 | }, | ||
1200 | |||
1201 | /** | ||
1202 | * Activates a tab page in the dialog by its id. | ||
1203 | * | ||
1204 | * dialogObj.selectPage( 'tab_1' ); | ||
1205 | * | ||
1206 | * @param {String} id The id of the dialog tab to be activated. | ||
1207 | */ | ||
1208 | selectPage: function( id ) { | ||
1209 | if ( this._.currentTabId == id ) | ||
1210 | return; | ||
1211 | |||
1212 | if ( this._.tabs[ id ][ 0 ].hasClass( 'cke_dialog_tab_disabled' ) ) | ||
1213 | return; | ||
1214 | |||
1215 | // If event was canceled - do nothing. | ||
1216 | if ( this.fire( 'selectPage', { page: id, currentPage: this._.currentTabId } ) === false ) | ||
1217 | return; | ||
1218 | |||
1219 | // Hide the non-selected tabs and pages. | ||
1220 | for ( var i in this._.tabs ) { | ||
1221 | var tab = this._.tabs[ i ][ 0 ], | ||
1222 | page = this._.tabs[ i ][ 1 ]; | ||
1223 | if ( i != id ) { | ||
1224 | tab.removeClass( 'cke_dialog_tab_selected' ); | ||
1225 | page.hide(); | ||
1226 | } | ||
1227 | page.setAttribute( 'aria-hidden', i != id ); | ||
1228 | } | ||
1229 | |||
1230 | var selected = this._.tabs[ id ]; | ||
1231 | selected[ 0 ].addClass( 'cke_dialog_tab_selected' ); | ||
1232 | |||
1233 | // [IE] an invisible input[type='text'] will enlarge it's width | ||
1234 | // if it's value is long when it shows, so we clear it's value | ||
1235 | // before it shows and then recover it (#5649) | ||
1236 | if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat ) { | ||
1237 | clearOrRecoverTextInputValue( selected[ 1 ] ); | ||
1238 | selected[ 1 ].show(); | ||
1239 | setTimeout( function() { | ||
1240 | clearOrRecoverTextInputValue( selected[ 1 ], 1 ); | ||
1241 | }, 0 ); | ||
1242 | } else { | ||
1243 | selected[ 1 ].show(); | ||
1244 | } | ||
1245 | |||
1246 | this._.currentTabId = id; | ||
1247 | this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id ); | ||
1248 | }, | ||
1249 | |||
1250 | /** | ||
1251 | * Dialog state-specific style updates. | ||
1252 | */ | ||
1253 | updateStyle: function() { | ||
1254 | // If only a single page shown, a different style is used in the central pane. | ||
1255 | this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' ); | ||
1256 | }, | ||
1257 | |||
1258 | /** | ||
1259 | * Hides a page's tab away from the dialog. | ||
1260 | * | ||
1261 | * dialog.hidePage( 'tab_3' ); | ||
1262 | * | ||
1263 | * @param {String} id The page's Id. | ||
1264 | */ | ||
1265 | hidePage: function( id ) { | ||
1266 | var tab = this._.tabs[ id ] && this._.tabs[ id ][ 0 ]; | ||
1267 | if ( !tab || this._.pageCount == 1 || !tab.isVisible() ) | ||
1268 | return; | ||
1269 | // Switch to other tab first when we're hiding the active tab. | ||
1270 | else if ( id == this._.currentTabId ) | ||
1271 | this.selectPage( getPreviousVisibleTab.call( this ) ); | ||
1272 | |||
1273 | tab.hide(); | ||
1274 | this._.pageCount--; | ||
1275 | this.updateStyle(); | ||
1276 | }, | ||
1277 | |||
1278 | /** | ||
1279 | * Unhides a page's tab. | ||
1280 | * | ||
1281 | * dialog.showPage( 'tab_2' ); | ||
1282 | * | ||
1283 | * @param {String} id The page's Id. | ||
1284 | */ | ||
1285 | showPage: function( id ) { | ||
1286 | var tab = this._.tabs[ id ] && this._.tabs[ id ][ 0 ]; | ||
1287 | if ( !tab ) | ||
1288 | return; | ||
1289 | tab.show(); | ||
1290 | this._.pageCount++; | ||
1291 | this.updateStyle(); | ||
1292 | }, | ||
1293 | |||
1294 | /** | ||
1295 | * Gets the root DOM element of the dialog. | ||
1296 | * | ||
1297 | * var dialogElement = dialogObj.getElement().getFirst(); | ||
1298 | * dialogElement.setStyle( 'padding', '5px' ); | ||
1299 | * | ||
1300 | * @returns {CKEDITOR.dom.element} The `<span>` element containing this dialog. | ||
1301 | */ | ||
1302 | getElement: function() { | ||
1303 | return this._.element; | ||
1304 | }, | ||
1305 | |||
1306 | /** | ||
1307 | * Gets the name of the dialog. | ||
1308 | * | ||
1309 | * var dialogName = dialogObj.getName(); | ||
1310 | * | ||
1311 | * @returns {String} The name of this dialog. | ||
1312 | */ | ||
1313 | getName: function() { | ||
1314 | return this._.name; | ||
1315 | }, | ||
1316 | |||
1317 | /** | ||
1318 | * Gets a dialog UI element object from a dialog page. | ||
1319 | * | ||
1320 | * dialogObj.getContentElement( 'tabId', 'elementId' ).setValue( 'Example' ); | ||
1321 | * | ||
1322 | * @param {String} pageId id of dialog page. | ||
1323 | * @param {String} elementId id of UI element. | ||
1324 | * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element. | ||
1325 | */ | ||
1326 | getContentElement: function( pageId, elementId ) { | ||
1327 | var page = this._.contents[ pageId ]; | ||
1328 | return page && page[ elementId ]; | ||
1329 | }, | ||
1330 | |||
1331 | /** | ||
1332 | * Gets the value of a dialog UI element. | ||
1333 | * | ||
1334 | * alert( dialogObj.getValueOf( 'tabId', 'elementId' ) ); | ||
1335 | * | ||
1336 | * @param {String} pageId id of dialog page. | ||
1337 | * @param {String} elementId id of UI element. | ||
1338 | * @returns {Object} The value of the UI element. | ||
1339 | */ | ||
1340 | getValueOf: function( pageId, elementId ) { | ||
1341 | return this.getContentElement( pageId, elementId ).getValue(); | ||
1342 | }, | ||
1343 | |||
1344 | /** | ||
1345 | * Sets the value of a dialog UI element. | ||
1346 | * | ||
1347 | * dialogObj.setValueOf( 'tabId', 'elementId', 'Example' ); | ||
1348 | * | ||
1349 | * @param {String} pageId id of the dialog page. | ||
1350 | * @param {String} elementId id of the UI element. | ||
1351 | * @param {Object} value The new value of the UI element. | ||
1352 | */ | ||
1353 | setValueOf: function( pageId, elementId, value ) { | ||
1354 | return this.getContentElement( pageId, elementId ).setValue( value ); | ||
1355 | }, | ||
1356 | |||
1357 | /** | ||
1358 | * Gets the UI element of a button in the dialog's button row. | ||
1359 | * | ||
1360 | * @returns {CKEDITOR.ui.dialog.button} The button object. | ||
1361 | * | ||
1362 | * @param {String} id The id of the button. | ||
1363 | */ | ||
1364 | getButton: function( id ) { | ||
1365 | return this._.buttons[ id ]; | ||
1366 | }, | ||
1367 | |||
1368 | /** | ||
1369 | * Simulates a click to a dialog button in the dialog's button row. | ||
1370 | * | ||
1371 | * @returns The return value of the dialog's `click` event. | ||
1372 | * | ||
1373 | * @param {String} id The id of the button. | ||
1374 | */ | ||
1375 | click: function( id ) { | ||
1376 | return this._.buttons[ id ].click(); | ||
1377 | }, | ||
1378 | |||
1379 | /** | ||
1380 | * Disables a dialog button. | ||
1381 | * | ||
1382 | * @param {String} id The id of the button. | ||
1383 | */ | ||
1384 | disableButton: function( id ) { | ||
1385 | return this._.buttons[ id ].disable(); | ||
1386 | }, | ||
1387 | |||
1388 | /** | ||
1389 | * Enables a dialog button. | ||
1390 | * | ||
1391 | * @param {String} id The id of the button. | ||
1392 | */ | ||
1393 | enableButton: function( id ) { | ||
1394 | return this._.buttons[ id ].enable(); | ||
1395 | }, | ||
1396 | |||
1397 | /** | ||
1398 | * Gets the number of pages in the dialog. | ||
1399 | * | ||
1400 | * @returns {Number} Page count. | ||
1401 | */ | ||
1402 | getPageCount: function() { | ||
1403 | return this._.pageCount; | ||
1404 | }, | ||
1405 | |||
1406 | /** | ||
1407 | * Gets the editor instance which opened this dialog. | ||
1408 | * | ||
1409 | * @returns {CKEDITOR.editor} Parent editor instances. | ||
1410 | */ | ||
1411 | getParentEditor: function() { | ||
1412 | return this._.editor; | ||
1413 | }, | ||
1414 | |||
1415 | /** | ||
1416 | * Gets the element that was selected when opening the dialog, if any. | ||
1417 | * | ||
1418 | * @returns {CKEDITOR.dom.element} The element that was selected, or `null`. | ||
1419 | */ | ||
1420 | getSelectedElement: function() { | ||
1421 | return this.getParentEditor().getSelection().getSelectedElement(); | ||
1422 | }, | ||
1423 | |||
1424 | /** | ||
1425 | * Adds element to dialog's focusable list. | ||
1426 | * | ||
1427 | * @param {CKEDITOR.dom.element} element | ||
1428 | * @param {Number} [index] | ||
1429 | */ | ||
1430 | addFocusable: function( element, index ) { | ||
1431 | if ( typeof index == 'undefined' ) { | ||
1432 | index = this._.focusList.length; | ||
1433 | this._.focusList.push( new Focusable( this, element, index ) ); | ||
1434 | } else { | ||
1435 | this._.focusList.splice( index, 0, new Focusable( this, element, index ) ); | ||
1436 | for ( var i = index + 1; i < this._.focusList.length; i++ ) | ||
1437 | this._.focusList[ i ].focusIndex++; | ||
1438 | } | ||
1439 | }, | ||
1440 | |||
1441 | /** | ||
1442 | * Sets the dialog {@link #property-state}. | ||
1443 | * | ||
1444 | * @since 4.5 | ||
1445 | * @param {Number} state Either {@link CKEDITOR#DIALOG_STATE_IDLE} or {@link CKEDITOR#DIALOG_STATE_BUSY}. | ||
1446 | */ | ||
1447 | setState: function( state ) { | ||
1448 | var oldState = this.state; | ||
1449 | |||
1450 | if ( oldState == state ) { | ||
1451 | return; | ||
1452 | } | ||
1453 | |||
1454 | this.state = state; | ||
1455 | |||
1456 | if ( state == CKEDITOR.DIALOG_STATE_BUSY ) { | ||
1457 | // Insert the spinner on demand. | ||
1458 | if ( !this.parts.spinner ) { | ||
1459 | var dir = this.getParentEditor().lang.dir, | ||
1460 | spinnerDef = { | ||
1461 | attributes: { | ||
1462 | 'class': 'cke_dialog_spinner' | ||
1463 | }, | ||
1464 | styles: { | ||
1465 | 'float': dir == 'rtl' ? 'right' : 'left' | ||
1466 | } | ||
1467 | }; | ||
1468 | |||
1469 | spinnerDef.styles[ 'margin-' + ( dir == 'rtl' ? 'left' : 'right' ) ] = '8px'; | ||
1470 | |||
1471 | this.parts.spinner = CKEDITOR.document.createElement( 'div', spinnerDef ); | ||
1472 | |||
1473 | this.parts.spinner.setHtml( '⌛' ); | ||
1474 | this.parts.spinner.appendTo( this.parts.title, 1 ); | ||
1475 | } | ||
1476 | |||
1477 | // Finally, show the spinner. | ||
1478 | this.parts.spinner.show(); | ||
1479 | |||
1480 | this.getButton( 'ok' ).disable(); | ||
1481 | } else if ( state == CKEDITOR.DIALOG_STATE_IDLE ) { | ||
1482 | // Hide the spinner. But don't do anything if there is no spinner yet. | ||
1483 | this.parts.spinner && this.parts.spinner.hide(); | ||
1484 | |||
1485 | this.getButton( 'ok' ).enable(); | ||
1486 | } | ||
1487 | |||
1488 | this.fire( 'state', state ); | ||
1489 | } | ||
1490 | }; | ||
1491 | |||
1492 | CKEDITOR.tools.extend( CKEDITOR.dialog, { | ||
1493 | /** | ||
1494 | * Registers a dialog. | ||
1495 | * | ||
1496 | * // Full sample plugin, which does not only register a dialog window but also adds an item to the context menu. | ||
1497 | * // To open the dialog window, choose "Open dialog" in the context menu. | ||
1498 | * CKEDITOR.plugins.add( 'myplugin', { | ||
1499 | * init: function( editor ) { | ||
1500 | * editor.addCommand( 'mydialog',new CKEDITOR.dialogCommand( 'mydialog' ) ); | ||
1501 | * | ||
1502 | * if ( editor.contextMenu ) { | ||
1503 | * editor.addMenuGroup( 'mygroup', 10 ); | ||
1504 | * editor.addMenuItem( 'My Dialog', { | ||
1505 | * label: 'Open dialog', | ||
1506 | * command: 'mydialog', | ||
1507 | * group: 'mygroup' | ||
1508 | * } ); | ||
1509 | * editor.contextMenu.addListener( function( element ) { | ||
1510 | * return { 'My Dialog': CKEDITOR.TRISTATE_OFF }; | ||
1511 | * } ); | ||
1512 | * } | ||
1513 | * | ||
1514 | * CKEDITOR.dialog.add( 'mydialog', function( api ) { | ||
1515 | * // CKEDITOR.dialog.definition | ||
1516 | * var dialogDefinition = { | ||
1517 | * title: 'Sample dialog', | ||
1518 | * minWidth: 390, | ||
1519 | * minHeight: 130, | ||
1520 | * contents: [ | ||
1521 | * { | ||
1522 | * id: 'tab1', | ||
1523 | * label: 'Label', | ||
1524 | * title: 'Title', | ||
1525 | * expand: true, | ||
1526 | * padding: 0, | ||
1527 | * elements: [ | ||
1528 | * { | ||
1529 | * type: 'html', | ||
1530 | * html: '<p>This is some sample HTML content.</p>' | ||
1531 | * }, | ||
1532 | * { | ||
1533 | * type: 'textarea', | ||
1534 | * id: 'textareaId', | ||
1535 | * rows: 4, | ||
1536 | * cols: 40 | ||
1537 | * } | ||
1538 | * ] | ||
1539 | * } | ||
1540 | * ], | ||
1541 | * buttons: [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ], | ||
1542 | * onOk: function() { | ||
1543 | * // "this" is now a CKEDITOR.dialog object. | ||
1544 | * // Accessing dialog elements: | ||
1545 | * var textareaObj = this.getContentElement( 'tab1', 'textareaId' ); | ||
1546 | * alert( "You have entered: " + textareaObj.getValue() ); | ||
1547 | * } | ||
1548 | * }; | ||
1549 | * | ||
1550 | * return dialogDefinition; | ||
1551 | * } ); | ||
1552 | * } | ||
1553 | * } ); | ||
1554 | * | ||
1555 | * CKEDITOR.replace( 'editor1', { extraPlugins: 'myplugin' } ); | ||
1556 | * | ||
1557 | * @static | ||
1558 | * @param {String} name The dialog's name. | ||
1559 | * @param {Function/String} dialogDefinition | ||
1560 | * A function returning the dialog's definition, or the URL to the `.js` file holding the function. | ||
1561 | * The function should accept an argument `editor` which is the current editor instance, and | ||
1562 | * return an object conforming to {@link CKEDITOR.dialog.definition}. | ||
1563 | * @see CKEDITOR.dialog.definition | ||
1564 | */ | ||
1565 | add: function( name, dialogDefinition ) { | ||
1566 | // Avoid path registration from multiple instances override definition. | ||
1567 | if ( !this._.dialogDefinitions[ name ] || typeof dialogDefinition == 'function' ) | ||
1568 | this._.dialogDefinitions[ name ] = dialogDefinition; | ||
1569 | }, | ||
1570 | |||
1571 | /** | ||
1572 | * @static | ||
1573 | * @todo | ||
1574 | */ | ||
1575 | exists: function( name ) { | ||
1576 | return !!this._.dialogDefinitions[ name ]; | ||
1577 | }, | ||
1578 | |||
1579 | /** | ||
1580 | * @static | ||
1581 | * @todo | ||
1582 | */ | ||
1583 | getCurrent: function() { | ||
1584 | return CKEDITOR.dialog._.currentTop; | ||
1585 | }, | ||
1586 | |||
1587 | /** | ||
1588 | * Check whether tab wasn't removed by {@link CKEDITOR.config#removeDialogTabs}. | ||
1589 | * | ||
1590 | * @since 4.1 | ||
1591 | * @static | ||
1592 | * @param {CKEDITOR.editor} editor | ||
1593 | * @param {String} dialogName | ||
1594 | * @param {String} tabName | ||
1595 | * @returns {Boolean} | ||
1596 | */ | ||
1597 | isTabEnabled: function( editor, dialogName, tabName ) { | ||
1598 | var cfg = editor.config.removeDialogTabs; | ||
1599 | |||
1600 | return !( cfg && cfg.match( new RegExp( '(?:^|;)' + dialogName + ':' + tabName + '(?:$|;)', 'i' ) ) ); | ||
1601 | }, | ||
1602 | |||
1603 | /** | ||
1604 | * The default OK button for dialogs. Fires the `ok` event and closes the dialog if the event succeeds. | ||
1605 | * | ||
1606 | * @static | ||
1607 | * @method | ||
1608 | */ | ||
1609 | okButton: ( function() { | ||
1610 | var retval = function( editor, override ) { | ||
1611 | override = override || {}; | ||
1612 | return CKEDITOR.tools.extend( { | ||
1613 | id: 'ok', | ||
1614 | type: 'button', | ||
1615 | label: editor.lang.common.ok, | ||
1616 | 'class': 'cke_dialog_ui_button_ok', | ||
1617 | onClick: function( evt ) { | ||
1618 | var dialog = evt.data.dialog; | ||
1619 | if ( dialog.fire( 'ok', { hide: true } ).hide !== false ) | ||
1620 | dialog.hide(); | ||
1621 | } | ||
1622 | }, override, true ); | ||
1623 | }; | ||
1624 | retval.type = 'button'; | ||
1625 | retval.override = function( override ) { | ||
1626 | return CKEDITOR.tools.extend( function( editor ) { | ||
1627 | return retval( editor, override ); | ||
1628 | }, { type: 'button' }, true ); | ||
1629 | }; | ||
1630 | return retval; | ||
1631 | } )(), | ||
1632 | |||
1633 | /** | ||
1634 | * The default cancel button for dialogs. Fires the `cancel` event and | ||
1635 | * closes the dialog if no UI element value changed. | ||
1636 | * | ||
1637 | * @static | ||
1638 | * @method | ||
1639 | */ | ||
1640 | cancelButton: ( function() { | ||
1641 | var retval = function( editor, override ) { | ||
1642 | override = override || {}; | ||
1643 | return CKEDITOR.tools.extend( { | ||
1644 | id: 'cancel', | ||
1645 | type: 'button', | ||
1646 | label: editor.lang.common.cancel, | ||
1647 | 'class': 'cke_dialog_ui_button_cancel', | ||
1648 | onClick: function( evt ) { | ||
1649 | var dialog = evt.data.dialog; | ||
1650 | if ( dialog.fire( 'cancel', { hide: true } ).hide !== false ) | ||
1651 | dialog.hide(); | ||
1652 | } | ||
1653 | }, override, true ); | ||
1654 | }; | ||
1655 | retval.type = 'button'; | ||
1656 | retval.override = function( override ) { | ||
1657 | return CKEDITOR.tools.extend( function( editor ) { | ||
1658 | return retval( editor, override ); | ||
1659 | }, { type: 'button' }, true ); | ||
1660 | }; | ||
1661 | return retval; | ||
1662 | } )(), | ||
1663 | |||
1664 | /** | ||
1665 | * Registers a dialog UI element. | ||
1666 | * | ||
1667 | * @static | ||
1668 | * @param {String} typeName The name of the UI element. | ||
1669 | * @param {Function} builder The function to build the UI element. | ||
1670 | */ | ||
1671 | addUIElement: function( typeName, builder ) { | ||
1672 | this._.uiElementBuilders[ typeName ] = builder; | ||
1673 | } | ||
1674 | } ); | ||
1675 | |||
1676 | CKEDITOR.dialog._ = { | ||
1677 | uiElementBuilders: {}, | ||
1678 | |||
1679 | dialogDefinitions: {}, | ||
1680 | |||
1681 | currentTop: null, | ||
1682 | |||
1683 | currentZIndex: null | ||
1684 | }; | ||
1685 | |||
1686 | // "Inherit" (copy actually) from CKEDITOR.event. | ||
1687 | CKEDITOR.event.implementOn( CKEDITOR.dialog ); | ||
1688 | CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype ); | ||
1689 | |||
1690 | var defaultDialogDefinition = { | ||
1691 | resizable: CKEDITOR.DIALOG_RESIZE_BOTH, | ||
1692 | minWidth: 600, | ||
1693 | minHeight: 400, | ||
1694 | buttons: [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] | ||
1695 | }; | ||
1696 | |||
1697 | // Tool function used to return an item from an array based on its id | ||
1698 | // property. | ||
1699 | var getById = function( array, id, recurse ) { | ||
1700 | for ( var i = 0, item; | ||
1701 | ( item = array[ i ] ); i++ ) { | ||
1702 | if ( item.id == id ) | ||
1703 | return item; | ||
1704 | if ( recurse && item[ recurse ] ) { | ||
1705 | var retval = getById( item[ recurse ], id, recurse ); | ||
1706 | if ( retval ) | ||
1707 | return retval; | ||
1708 | } | ||
1709 | } | ||
1710 | return null; | ||
1711 | }; | ||
1712 | |||
1713 | // Tool function used to add an item into an array. | ||
1714 | var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound ) { | ||
1715 | if ( nextSiblingId ) { | ||
1716 | for ( var i = 0, item; | ||
1717 | ( item = array[ i ] ); i++ ) { | ||
1718 | if ( item.id == nextSiblingId ) { | ||
1719 | array.splice( i, 0, newItem ); | ||
1720 | return newItem; | ||
1721 | } | ||
1722 | |||
1723 | if ( recurse && item[ recurse ] ) { | ||
1724 | var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true ); | ||
1725 | if ( retval ) | ||
1726 | return retval; | ||
1727 | } | ||
1728 | } | ||
1729 | |||
1730 | if ( nullIfNotFound ) | ||
1731 | return null; | ||
1732 | } | ||
1733 | |||
1734 | array.push( newItem ); | ||
1735 | return newItem; | ||
1736 | }; | ||
1737 | |||
1738 | // Tool function used to remove an item from an array based on its id. | ||
1739 | var removeById = function( array, id, recurse ) { | ||
1740 | for ( var i = 0, item; | ||
1741 | ( item = array[ i ] ); i++ ) { | ||
1742 | if ( item.id == id ) | ||
1743 | return array.splice( i, 1 ); | ||
1744 | if ( recurse && item[ recurse ] ) { | ||
1745 | var retval = removeById( item[ recurse ], id, recurse ); | ||
1746 | if ( retval ) | ||
1747 | return retval; | ||
1748 | } | ||
1749 | } | ||
1750 | return null; | ||
1751 | }; | ||
1752 | |||
1753 | /** | ||
1754 | * This class is not really part of the API. It is the `definition` property value | ||
1755 | * passed to `dialogDefinition` event handlers. | ||
1756 | * | ||
1757 | * CKEDITOR.on( 'dialogDefinition', function( evt ) { | ||
1758 | * var definition = evt.data.definition; | ||
1759 | * var content = definition.getContents( 'page1' ); | ||
1760 | * // ... | ||
1761 | * } ); | ||
1762 | * | ||
1763 | * @private | ||
1764 | * @class CKEDITOR.dialog.definitionObject | ||
1765 | * @extends CKEDITOR.dialog.definition | ||
1766 | * @constructor Creates a definitionObject class instance. | ||
1767 | */ | ||
1768 | var definitionObject = function( dialog, dialogDefinition ) { | ||
1769 | // TODO : Check if needed. | ||
1770 | this.dialog = dialog; | ||
1771 | |||
1772 | // Transform the contents entries in contentObjects. | ||
1773 | var contents = dialogDefinition.contents; | ||
1774 | for ( var i = 0, content; | ||
1775 | ( content = contents[ i ] ); i++ ) | ||
1776 | contents[ i ] = content && new contentObject( dialog, content ); | ||
1777 | |||
1778 | CKEDITOR.tools.extend( this, dialogDefinition ); | ||
1779 | }; | ||
1780 | |||
1781 | definitionObject.prototype = { | ||
1782 | /** | ||
1783 | * Gets a content definition. | ||
1784 | * | ||
1785 | * @param {String} id The id of the content definition. | ||
1786 | * @returns {CKEDITOR.dialog.definition.content} The content definition matching id. | ||
1787 | */ | ||
1788 | getContents: function( id ) { | ||
1789 | return getById( this.contents, id ); | ||
1790 | }, | ||
1791 | |||
1792 | /** | ||
1793 | * Gets a button definition. | ||
1794 | * | ||
1795 | * @param {String} id The id of the button definition. | ||
1796 | * @returns {CKEDITOR.dialog.definition.button} The button definition matching id. | ||
1797 | */ | ||
1798 | getButton: function( id ) { | ||
1799 | return getById( this.buttons, id ); | ||
1800 | }, | ||
1801 | |||
1802 | /** | ||
1803 | * Adds a content definition object under this dialog definition. | ||
1804 | * | ||
1805 | * @param {CKEDITOR.dialog.definition.content} contentDefinition The | ||
1806 | * content definition. | ||
1807 | * @param {String} [nextSiblingId] The id of an existing content | ||
1808 | * definition which the new content definition will be inserted | ||
1809 | * before. Omit if the new content definition is to be inserted as | ||
1810 | * the last item. | ||
1811 | * @returns {CKEDITOR.dialog.definition.content} The inserted content definition. | ||
1812 | */ | ||
1813 | addContents: function( contentDefinition, nextSiblingId ) { | ||
1814 | return addById( this.contents, contentDefinition, nextSiblingId ); | ||
1815 | }, | ||
1816 | |||
1817 | /** | ||
1818 | * Adds a button definition object under this dialog definition. | ||
1819 | * | ||
1820 | * @param {CKEDITOR.dialog.definition.button} buttonDefinition The | ||
1821 | * button definition. | ||
1822 | * @param {String} [nextSiblingId] The id of an existing button | ||
1823 | * definition which the new button definition will be inserted | ||
1824 | * before. Omit if the new button definition is to be inserted as | ||
1825 | * the last item. | ||
1826 | * @returns {CKEDITOR.dialog.definition.button} The inserted button definition. | ||
1827 | */ | ||
1828 | addButton: function( buttonDefinition, nextSiblingId ) { | ||
1829 | return addById( this.buttons, buttonDefinition, nextSiblingId ); | ||
1830 | }, | ||
1831 | |||
1832 | /** | ||
1833 | * Removes a content definition from this dialog definition. | ||
1834 | * | ||
1835 | * @param {String} id The id of the content definition to be removed. | ||
1836 | * @returns {CKEDITOR.dialog.definition.content} The removed content definition. | ||
1837 | */ | ||
1838 | removeContents: function( id ) { | ||
1839 | removeById( this.contents, id ); | ||
1840 | }, | ||
1841 | |||
1842 | /** | ||
1843 | * Removes a button definition from the dialog definition. | ||
1844 | * | ||
1845 | * @param {String} id The id of the button definition to be removed. | ||
1846 | * @returns {CKEDITOR.dialog.definition.button} The removed button definition. | ||
1847 | */ | ||
1848 | removeButton: function( id ) { | ||
1849 | removeById( this.buttons, id ); | ||
1850 | } | ||
1851 | }; | ||
1852 | |||
1853 | /** | ||
1854 | * This class is not really part of the API. It is the template of the | ||
1855 | * objects representing content pages inside the | ||
1856 | * CKEDITOR.dialog.definitionObject. | ||
1857 | * | ||
1858 | * CKEDITOR.on( 'dialogDefinition', function( evt ) { | ||
1859 | * var definition = evt.data.definition; | ||
1860 | * var content = definition.getContents( 'page1' ); | ||
1861 | * content.remove( 'textInput1' ); | ||
1862 | * // ... | ||
1863 | * } ); | ||
1864 | * | ||
1865 | * @private | ||
1866 | * @class CKEDITOR.dialog.definition.contentObject | ||
1867 | * @constructor Creates a contentObject class instance. | ||
1868 | */ | ||
1869 | function contentObject( dialog, contentDefinition ) { | ||
1870 | this._ = { | ||
1871 | dialog: dialog | ||
1872 | }; | ||
1873 | |||
1874 | CKEDITOR.tools.extend( this, contentDefinition ); | ||
1875 | } | ||
1876 | |||
1877 | contentObject.prototype = { | ||
1878 | /** | ||
1879 | * Gets a UI element definition under the content definition. | ||
1880 | * | ||
1881 | * @param {String} id The id of the UI element definition. | ||
1882 | * @returns {CKEDITOR.dialog.definition.uiElement} | ||
1883 | */ | ||
1884 | get: function( id ) { | ||
1885 | return getById( this.elements, id, 'children' ); | ||
1886 | }, | ||
1887 | |||
1888 | /** | ||
1889 | * Adds a UI element definition to the content definition. | ||
1890 | * | ||
1891 | * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition The | ||
1892 | * UI elemnet definition to be added. | ||
1893 | * @param {String} nextSiblingId The id of an existing UI element | ||
1894 | * definition which the new UI element definition will be inserted | ||
1895 | * before. Omit if the new button definition is to be inserted as | ||
1896 | * the last item. | ||
1897 | * @returns {CKEDITOR.dialog.definition.uiElement} The element definition inserted. | ||
1898 | */ | ||
1899 | add: function( elementDefinition, nextSiblingId ) { | ||
1900 | return addById( this.elements, elementDefinition, nextSiblingId, 'children' ); | ||
1901 | }, | ||
1902 | |||
1903 | /** | ||
1904 | * Removes a UI element definition from the content definition. | ||
1905 | * | ||
1906 | * @param {String} id The id of the UI element definition to be removed. | ||
1907 | * @returns {CKEDITOR.dialog.definition.uiElement} The element definition removed. | ||
1908 | */ | ||
1909 | remove: function( id ) { | ||
1910 | removeById( this.elements, id, 'children' ); | ||
1911 | } | ||
1912 | }; | ||
1913 | |||
1914 | function initDragAndDrop( dialog ) { | ||
1915 | var lastCoords = null, | ||
1916 | abstractDialogCoords = null, | ||
1917 | editor = dialog.getParentEditor(), | ||
1918 | magnetDistance = editor.config.dialog_magnetDistance, | ||
1919 | margins = CKEDITOR.skin.margins || [ 0, 0, 0, 0 ]; | ||
1920 | |||
1921 | if ( typeof magnetDistance == 'undefined' ) | ||
1922 | magnetDistance = 20; | ||
1923 | |||
1924 | function mouseMoveHandler( evt ) { | ||
1925 | var dialogSize = dialog.getSize(), | ||
1926 | viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), | ||
1927 | x = evt.data.$.screenX, | ||
1928 | y = evt.data.$.screenY, | ||
1929 | dx = x - lastCoords.x, | ||
1930 | dy = y - lastCoords.y, | ||
1931 | realX, realY; | ||
1932 | |||
1933 | lastCoords = { x: x, y: y }; | ||
1934 | abstractDialogCoords.x += dx; | ||
1935 | abstractDialogCoords.y += dy; | ||
1936 | |||
1937 | if ( abstractDialogCoords.x + margins[ 3 ] < magnetDistance ) | ||
1938 | realX = -margins[ 3 ]; | ||
1939 | else if ( abstractDialogCoords.x - margins[ 1 ] > viewPaneSize.width - dialogSize.width - magnetDistance ) | ||
1940 | realX = viewPaneSize.width - dialogSize.width + ( editor.lang.dir == 'rtl' ? 0 : margins[ 1 ] ); | ||
1941 | else | ||
1942 | realX = abstractDialogCoords.x; | ||
1943 | |||
1944 | if ( abstractDialogCoords.y + margins[ 0 ] < magnetDistance ) | ||
1945 | realY = -margins[ 0 ]; | ||
1946 | else if ( abstractDialogCoords.y - margins[ 2 ] > viewPaneSize.height - dialogSize.height - magnetDistance ) | ||
1947 | realY = viewPaneSize.height - dialogSize.height + margins[ 2 ]; | ||
1948 | else | ||
1949 | realY = abstractDialogCoords.y; | ||
1950 | |||
1951 | dialog.move( realX, realY, 1 ); | ||
1952 | |||
1953 | evt.data.preventDefault(); | ||
1954 | } | ||
1955 | |||
1956 | function mouseUpHandler() { | ||
1957 | CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); | ||
1958 | CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); | ||
1959 | |||
1960 | if ( CKEDITOR.env.ie6Compat ) { | ||
1961 | var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); | ||
1962 | coverDoc.removeListener( 'mousemove', mouseMoveHandler ); | ||
1963 | coverDoc.removeListener( 'mouseup', mouseUpHandler ); | ||
1964 | } | ||
1965 | } | ||
1966 | |||
1967 | dialog.parts.title.on( 'mousedown', function( evt ) { | ||
1968 | lastCoords = { x: evt.data.$.screenX, y: evt.data.$.screenY }; | ||
1969 | |||
1970 | CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); | ||
1971 | CKEDITOR.document.on( 'mouseup', mouseUpHandler ); | ||
1972 | abstractDialogCoords = dialog.getPosition(); | ||
1973 | |||
1974 | if ( CKEDITOR.env.ie6Compat ) { | ||
1975 | var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); | ||
1976 | coverDoc.on( 'mousemove', mouseMoveHandler ); | ||
1977 | coverDoc.on( 'mouseup', mouseUpHandler ); | ||
1978 | } | ||
1979 | |||
1980 | evt.data.preventDefault(); | ||
1981 | }, dialog ); | ||
1982 | } | ||
1983 | |||
1984 | function initResizeHandles( dialog ) { | ||
1985 | var def = dialog.definition, | ||
1986 | resizable = def.resizable; | ||
1987 | |||
1988 | if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ) | ||
1989 | return; | ||
1990 | |||
1991 | var editor = dialog.getParentEditor(); | ||
1992 | var wrapperWidth, wrapperHeight, viewSize, origin, startSize, dialogCover; | ||
1993 | |||
1994 | var mouseDownFn = CKEDITOR.tools.addFunction( function( $event ) { | ||
1995 | startSize = dialog.getSize(); | ||
1996 | |||
1997 | var content = dialog.parts.contents, | ||
1998 | iframeDialog = content.$.getElementsByTagName( 'iframe' ).length; | ||
1999 | |||
2000 | // Shim to help capturing "mousemove" over iframe. | ||
2001 | if ( iframeDialog ) { | ||
2002 | dialogCover = CKEDITOR.dom.element.createFromHtml( '<div class="cke_dialog_resize_cover" style="height: 100%; position: absolute; width: 100%;"></div>' ); | ||
2003 | content.append( dialogCover ); | ||
2004 | } | ||
2005 | |||
2006 | // Calculate the offset between content and chrome size. | ||
2007 | wrapperHeight = startSize.height - dialog.parts.contents.getSize( 'height', !( CKEDITOR.env.gecko || CKEDITOR.env.ie && CKEDITOR.env.quirks ) ); | ||
2008 | wrapperWidth = startSize.width - dialog.parts.contents.getSize( 'width', 1 ); | ||
2009 | |||
2010 | origin = { x: $event.screenX, y: $event.screenY }; | ||
2011 | |||
2012 | viewSize = CKEDITOR.document.getWindow().getViewPaneSize(); | ||
2013 | |||
2014 | CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); | ||
2015 | CKEDITOR.document.on( 'mouseup', mouseUpHandler ); | ||
2016 | |||
2017 | if ( CKEDITOR.env.ie6Compat ) { | ||
2018 | var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); | ||
2019 | coverDoc.on( 'mousemove', mouseMoveHandler ); | ||
2020 | coverDoc.on( 'mouseup', mouseUpHandler ); | ||
2021 | } | ||
2022 | |||
2023 | $event.preventDefault && $event.preventDefault(); | ||
2024 | } ); | ||
2025 | |||
2026 | // Prepend the grip to the dialog. | ||
2027 | dialog.on( 'load', function() { | ||
2028 | var direction = ''; | ||
2029 | if ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH ) | ||
2030 | direction = ' cke_resizer_horizontal'; | ||
2031 | else if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT ) | ||
2032 | direction = ' cke_resizer_vertical'; | ||
2033 | var resizer = CKEDITOR.dom.element.createFromHtml( | ||
2034 | '<div' + | ||
2035 | ' class="cke_resizer' + direction + ' cke_resizer_' + editor.lang.dir + '"' + | ||
2036 | ' title="' + CKEDITOR.tools.htmlEncode( editor.lang.common.resize ) + '"' + | ||
2037 | ' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event )">' + | ||
2038 | // BLACK LOWER RIGHT TRIANGLE (ltr) | ||
2039 | // BLACK LOWER LEFT TRIANGLE (rtl) | ||
2040 | ( editor.lang.dir == 'ltr' ? '\u25E2' : '\u25E3' ) + | ||
2041 | '</div>' ); | ||
2042 | dialog.parts.footer.append( resizer, 1 ); | ||
2043 | } ); | ||
2044 | editor.on( 'destroy', function() { | ||
2045 | CKEDITOR.tools.removeFunction( mouseDownFn ); | ||
2046 | } ); | ||
2047 | |||
2048 | function mouseMoveHandler( evt ) { | ||
2049 | var rtl = editor.lang.dir == 'rtl', | ||
2050 | dx = ( evt.data.$.screenX - origin.x ) * ( rtl ? -1 : 1 ), | ||
2051 | dy = evt.data.$.screenY - origin.y, | ||
2052 | width = startSize.width, | ||
2053 | height = startSize.height, | ||
2054 | internalWidth = width + dx * ( dialog._.moved ? 1 : 2 ), | ||
2055 | internalHeight = height + dy * ( dialog._.moved ? 1 : 2 ), | ||
2056 | element = dialog._.element.getFirst(), | ||
2057 | right = rtl && element.getComputedStyle( 'right' ), | ||
2058 | position = dialog.getPosition(); | ||
2059 | |||
2060 | if ( position.y + internalHeight > viewSize.height ) | ||
2061 | internalHeight = viewSize.height - position.y; | ||
2062 | |||
2063 | if ( ( rtl ? right : position.x ) + internalWidth > viewSize.width ) | ||
2064 | internalWidth = viewSize.width - ( rtl ? right : position.x ); | ||
2065 | |||
2066 | // Make sure the dialog will not be resized to the wrong side when it's in the leftmost position for RTL. | ||
2067 | if ( ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) ) | ||
2068 | width = Math.max( def.minWidth || 0, internalWidth - wrapperWidth ); | ||
2069 | |||
2070 | if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) | ||
2071 | height = Math.max( def.minHeight || 0, internalHeight - wrapperHeight ); | ||
2072 | |||
2073 | dialog.resize( width, height ); | ||
2074 | |||
2075 | if ( !dialog._.moved ) | ||
2076 | dialog.layout(); | ||
2077 | |||
2078 | evt.data.preventDefault(); | ||
2079 | } | ||
2080 | |||
2081 | function mouseUpHandler() { | ||
2082 | CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); | ||
2083 | CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); | ||
2084 | |||
2085 | if ( dialogCover ) { | ||
2086 | dialogCover.remove(); | ||
2087 | dialogCover = null; | ||
2088 | } | ||
2089 | |||
2090 | if ( CKEDITOR.env.ie6Compat ) { | ||
2091 | var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); | ||
2092 | coverDoc.removeListener( 'mouseup', mouseUpHandler ); | ||
2093 | coverDoc.removeListener( 'mousemove', mouseMoveHandler ); | ||
2094 | } | ||
2095 | } | ||
2096 | } | ||
2097 | |||
2098 | var resizeCover; | ||
2099 | // Caching resuable covers and allowing only one cover | ||
2100 | // on screen. | ||
2101 | var covers = {}, | ||
2102 | currentCover; | ||
2103 | |||
2104 | function cancelEvent( ev ) { | ||
2105 | ev.data.preventDefault( 1 ); | ||
2106 | } | ||
2107 | |||
2108 | function showCover( editor ) { | ||
2109 | var win = CKEDITOR.document.getWindow(), | ||
2110 | config = editor.config, | ||
2111 | skinName = ( CKEDITOR.skinName || editor.config.skin ), | ||
2112 | backgroundColorStyle = config.dialog_backgroundCoverColor || ( skinName == 'moono-lisa' ? 'black' : 'white' ), | ||
2113 | backgroundCoverOpacity = config.dialog_backgroundCoverOpacity, | ||
2114 | baseFloatZIndex = config.baseFloatZIndex, | ||
2115 | coverKey = CKEDITOR.tools.genKey( backgroundColorStyle, backgroundCoverOpacity, baseFloatZIndex ), | ||
2116 | coverElement = covers[ coverKey ]; | ||
2117 | |||
2118 | if ( !coverElement ) { | ||
2119 | var html = [ | ||
2120 | '<div tabIndex="-1" style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ), | ||
2121 | '; z-index: ', baseFloatZIndex, | ||
2122 | '; top: 0px; left: 0px; ', | ||
2123 | ( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ), | ||
2124 | '" class="cke_dialog_background_cover">' | ||
2125 | ]; | ||
2126 | |||
2127 | if ( CKEDITOR.env.ie6Compat ) { | ||
2128 | // Support for custom document.domain in IE. | ||
2129 | var iframeHtml = '<html><body style=\\\'background-color:' + backgroundColorStyle + ';\\\'></body></html>'; | ||
2130 | |||
2131 | html.push( '<iframe' + | ||
2132 | ' hidefocus="true"' + | ||
2133 | ' frameborder="0"' + | ||
2134 | ' id="cke_dialog_background_iframe"' + | ||
2135 | ' src="javascript:' ); | ||
2136 | |||
2137 | html.push( 'void((function(){' + encodeURIComponent( | ||
2138 | 'document.open();' + | ||
2139 | // Support for custom document.domain in IE. | ||
2140 | '(' + CKEDITOR.tools.fixDomain + ')();' + | ||
2141 | 'document.write( \'' + iframeHtml + '\' );' + | ||
2142 | 'document.close();' | ||
2143 | ) + '})())' ); | ||
2144 | |||
2145 | html.push( '"' + | ||
2146 | ' style="' + | ||
2147 | 'position:absolute;' + | ||
2148 | 'left:0;' + | ||
2149 | 'top:0;' + | ||
2150 | 'width:100%;' + | ||
2151 | 'height: 100%;' + | ||
2152 | 'filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0)">' + | ||
2153 | '</iframe>' ); | ||
2154 | } | ||
2155 | |||
2156 | html.push( '</div>' ); | ||
2157 | |||
2158 | coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) ); | ||
2159 | coverElement.setOpacity( backgroundCoverOpacity !== undefined ? backgroundCoverOpacity : 0.5 ); | ||
2160 | |||
2161 | coverElement.on( 'keydown', cancelEvent ); | ||
2162 | coverElement.on( 'keypress', cancelEvent ); | ||
2163 | coverElement.on( 'keyup', cancelEvent ); | ||
2164 | |||
2165 | coverElement.appendTo( CKEDITOR.document.getBody() ); | ||
2166 | covers[ coverKey ] = coverElement; | ||
2167 | } else { | ||
2168 | coverElement.show(); | ||
2169 | } | ||
2170 | |||
2171 | // Makes the dialog cover a focus holder as well. | ||
2172 | editor.focusManager.add( coverElement ); | ||
2173 | |||
2174 | currentCover = coverElement; | ||
2175 | var resizeFunc = function() { | ||
2176 | var size = win.getViewPaneSize(); | ||
2177 | coverElement.setStyles( { | ||
2178 | width: size.width + 'px', | ||
2179 | height: size.height + 'px' | ||
2180 | } ); | ||
2181 | }; | ||
2182 | |||
2183 | var scrollFunc = function() { | ||
2184 | var pos = win.getScrollPosition(), | ||
2185 | cursor = CKEDITOR.dialog._.currentTop; | ||
2186 | coverElement.setStyles( { | ||
2187 | left: pos.x + 'px', | ||
2188 | top: pos.y + 'px' | ||
2189 | } ); | ||
2190 | |||
2191 | if ( cursor ) { | ||
2192 | do { | ||
2193 | var dialogPos = cursor.getPosition(); | ||
2194 | cursor.move( dialogPos.x, dialogPos.y ); | ||
2195 | } while ( ( cursor = cursor._.parentDialog ) ); | ||
2196 | } | ||
2197 | }; | ||
2198 | |||
2199 | resizeCover = resizeFunc; | ||
2200 | win.on( 'resize', resizeFunc ); | ||
2201 | resizeFunc(); | ||
2202 | // Using Safari/Mac, focus must be kept where it is (#7027) | ||
2203 | if ( !( CKEDITOR.env.mac && CKEDITOR.env.webkit ) ) | ||
2204 | coverElement.focus(); | ||
2205 | |||
2206 | if ( CKEDITOR.env.ie6Compat ) { | ||
2207 | // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll. | ||
2208 | // So we need to invent a really funny way to make it work. | ||
2209 | var myScrollHandler = function() { | ||
2210 | scrollFunc(); | ||
2211 | arguments.callee.prevScrollHandler.apply( this, arguments ); | ||
2212 | }; | ||
2213 | win.$.setTimeout( function() { | ||
2214 | myScrollHandler.prevScrollHandler = window.onscroll || | ||
2215 | function() {}; | ||
2216 | window.onscroll = myScrollHandler; | ||
2217 | }, 0 ); | ||
2218 | scrollFunc(); | ||
2219 | } | ||
2220 | } | ||
2221 | |||
2222 | function hideCover( editor ) { | ||
2223 | if ( !currentCover ) | ||
2224 | return; | ||
2225 | |||
2226 | editor.focusManager.remove( currentCover ); | ||
2227 | var win = CKEDITOR.document.getWindow(); | ||
2228 | currentCover.hide(); | ||
2229 | win.removeListener( 'resize', resizeCover ); | ||
2230 | |||
2231 | if ( CKEDITOR.env.ie6Compat ) { | ||
2232 | win.$.setTimeout( function() { | ||
2233 | var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler; | ||
2234 | window.onscroll = prevScrollHandler || null; | ||
2235 | }, 0 ); | ||
2236 | } | ||
2237 | resizeCover = null; | ||
2238 | } | ||
2239 | |||
2240 | function removeCovers() { | ||
2241 | for ( var coverId in covers ) | ||
2242 | covers[ coverId ].remove(); | ||
2243 | covers = {}; | ||
2244 | } | ||
2245 | |||
2246 | var accessKeyProcessors = {}; | ||
2247 | |||
2248 | var accessKeyDownHandler = function( evt ) { | ||
2249 | var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, | ||
2250 | alt = evt.data.$.altKey, | ||
2251 | shift = evt.data.$.shiftKey, | ||
2252 | key = String.fromCharCode( evt.data.$.keyCode ), | ||
2253 | keyProcessor = accessKeyProcessors[ ( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '' ) + ( shift ? 'SHIFT+' : '' ) + key ]; | ||
2254 | |||
2255 | if ( !keyProcessor || !keyProcessor.length ) | ||
2256 | return; | ||
2257 | |||
2258 | keyProcessor = keyProcessor[ keyProcessor.length - 1 ]; | ||
2259 | keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); | ||
2260 | evt.data.preventDefault(); | ||
2261 | }; | ||
2262 | |||
2263 | var accessKeyUpHandler = function( evt ) { | ||
2264 | var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, | ||
2265 | alt = evt.data.$.altKey, | ||
2266 | shift = evt.data.$.shiftKey, | ||
2267 | key = String.fromCharCode( evt.data.$.keyCode ), | ||
2268 | keyProcessor = accessKeyProcessors[ ( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '' ) + ( shift ? 'SHIFT+' : '' ) + key ]; | ||
2269 | |||
2270 | if ( !keyProcessor || !keyProcessor.length ) | ||
2271 | return; | ||
2272 | |||
2273 | keyProcessor = keyProcessor[ keyProcessor.length - 1 ]; | ||
2274 | if ( keyProcessor.keyup ) { | ||
2275 | keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); | ||
2276 | evt.data.preventDefault(); | ||
2277 | } | ||
2278 | }; | ||
2279 | |||
2280 | var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc ) { | ||
2281 | var procList = accessKeyProcessors[ key ] || ( accessKeyProcessors[ key ] = [] ); | ||
2282 | procList.push( { | ||
2283 | uiElement: uiElement, | ||
2284 | dialog: dialog, | ||
2285 | key: key, | ||
2286 | keyup: upFunc || uiElement.accessKeyUp, | ||
2287 | keydown: downFunc || uiElement.accessKeyDown | ||
2288 | } ); | ||
2289 | }; | ||
2290 | |||
2291 | var unregisterAccessKey = function( obj ) { | ||
2292 | for ( var i in accessKeyProcessors ) { | ||
2293 | var list = accessKeyProcessors[ i ]; | ||
2294 | for ( var j = list.length - 1; j >= 0; j-- ) { | ||
2295 | if ( list[ j ].dialog == obj || list[ j ].uiElement == obj ) | ||
2296 | list.splice( j, 1 ); | ||
2297 | } | ||
2298 | if ( list.length === 0 ) | ||
2299 | delete accessKeyProcessors[ i ]; | ||
2300 | } | ||
2301 | }; | ||
2302 | |||
2303 | var tabAccessKeyUp = function( dialog, key ) { | ||
2304 | if ( dialog._.accessKeyMap[ key ] ) | ||
2305 | dialog.selectPage( dialog._.accessKeyMap[ key ] ); | ||
2306 | }; | ||
2307 | |||
2308 | var tabAccessKeyDown = function() {}; | ||
2309 | |||
2310 | ( function() { | ||
2311 | CKEDITOR.ui.dialog = { | ||
2312 | /** | ||
2313 | * The base class of all dialog UI elements. | ||
2314 | * | ||
2315 | * @class CKEDITOR.ui.dialog.uiElement | ||
2316 | * @constructor Creates a uiElement class instance. | ||
2317 | * @param {CKEDITOR.dialog} dialog Parent dialog object. | ||
2318 | * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element | ||
2319 | * definition. | ||
2320 | * | ||
2321 | * Accepted fields: | ||
2322 | * | ||
2323 | * * `id` (Required) The id of the UI element. See {@link CKEDITOR.dialog#getContentElement}. | ||
2324 | * * `type` (Required) The type of the UI element. The | ||
2325 | * value to this field specifies which UI element class will be used to | ||
2326 | * generate the final widget. | ||
2327 | * * `title` (Optional) The popup tooltip for the UI | ||
2328 | * element. | ||
2329 | * * `hidden` (Optional) A flag that tells if the element | ||
2330 | * should be initially visible. | ||
2331 | * * `className` (Optional) Additional CSS class names | ||
2332 | * to add to the UI element. Separated by space. | ||
2333 | * * `style` (Optional) Additional CSS inline styles | ||
2334 | * to add to the UI element. A semicolon (;) is required after the last | ||
2335 | * style declaration. | ||
2336 | * * `accessKey` (Optional) The alphanumeric access key | ||
2337 | * for this element. Access keys are automatically prefixed by CTRL. | ||
2338 | * * `on*` (Optional) Any UI element definition field that | ||
2339 | * starts with `on` followed immediately by a capital letter and | ||
2340 | * probably more letters is an event handler. Event handlers may be further | ||
2341 | * divided into registered event handlers and DOM event handlers. Please | ||
2342 | * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and | ||
2343 | * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more information. | ||
2344 | * | ||
2345 | * @param {Array} htmlList | ||
2346 | * List of HTML code to be added to the dialog's content area. | ||
2347 | * @param {Function/String} [nodeNameArg='div'] | ||
2348 | * A function returning a string, or a simple string for the node name for | ||
2349 | * the root DOM node. | ||
2350 | * @param {Function/Object} [stylesArg={}] | ||
2351 | * A function returning an object, or a simple object for CSS styles applied | ||
2352 | * to the DOM node. | ||
2353 | * @param {Function/Object} [attributesArg={}] | ||
2354 | * A fucntion returning an object, or a simple object for attributes applied | ||
2355 | * to the DOM node. | ||
2356 | * @param {Function/String} [contentsArg=''] | ||
2357 | * A function returning a string, or a simple string for the HTML code inside | ||
2358 | * the root DOM node. Default is empty string. | ||
2359 | */ | ||
2360 | uiElement: function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg ) { | ||
2361 | if ( arguments.length < 4 ) | ||
2362 | return; | ||
2363 | |||
2364 | var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div', | ||
2365 | html = [ '<', nodeName, ' ' ], | ||
2366 | styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {}, | ||
2367 | attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {}, | ||
2368 | innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '', | ||
2369 | domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement', | ||
2370 | i; | ||
2371 | |||
2372 | if ( elementDefinition.requiredContent && !dialog.getParentEditor().filter.check( elementDefinition.requiredContent ) ) { | ||
2373 | styles.display = 'none'; | ||
2374 | this.notAllowed = true; | ||
2375 | } | ||
2376 | |||
2377 | // Set the id, a unique id is required for getElement() to work. | ||
2378 | attributes.id = domId; | ||
2379 | |||
2380 | // Set the type and definition CSS class names. | ||
2381 | var classes = {}; | ||
2382 | if ( elementDefinition.type ) | ||
2383 | classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1; | ||
2384 | if ( elementDefinition.className ) | ||
2385 | classes[ elementDefinition.className ] = 1; | ||
2386 | if ( elementDefinition.disabled ) | ||
2387 | classes.cke_disabled = 1; | ||
2388 | |||
2389 | var attributeClasses = ( attributes[ 'class' ] && attributes[ 'class' ].split ) ? attributes[ 'class' ].split( ' ' ) : []; | ||
2390 | for ( i = 0; i < attributeClasses.length; i++ ) { | ||
2391 | if ( attributeClasses[ i ] ) | ||
2392 | classes[ attributeClasses[ i ] ] = 1; | ||
2393 | } | ||
2394 | var finalClasses = []; | ||
2395 | for ( i in classes ) | ||
2396 | finalClasses.push( i ); | ||
2397 | attributes[ 'class' ] = finalClasses.join( ' ' ); | ||
2398 | |||
2399 | // Set the popup tooltop. | ||
2400 | if ( elementDefinition.title ) | ||
2401 | attributes.title = elementDefinition.title; | ||
2402 | |||
2403 | // Write the inline CSS styles. | ||
2404 | var styleStr = ( elementDefinition.style || '' ).split( ';' ); | ||
2405 | |||
2406 | // Element alignment support. | ||
2407 | if ( elementDefinition.align ) { | ||
2408 | var align = elementDefinition.align; | ||
2409 | styles[ 'margin-left' ] = align == 'left' ? 0 : 'auto'; | ||
2410 | styles[ 'margin-right' ] = align == 'right' ? 0 : 'auto'; | ||
2411 | } | ||
2412 | |||
2413 | for ( i in styles ) | ||
2414 | styleStr.push( i + ':' + styles[ i ] ); | ||
2415 | if ( elementDefinition.hidden ) | ||
2416 | styleStr.push( 'display:none' ); | ||
2417 | for ( i = styleStr.length - 1; i >= 0; i-- ) { | ||
2418 | if ( styleStr[ i ] === '' ) | ||
2419 | styleStr.splice( i, 1 ); | ||
2420 | } | ||
2421 | if ( styleStr.length > 0 ) | ||
2422 | attributes.style = ( attributes.style ? ( attributes.style + '; ' ) : '' ) + styleStr.join( '; ' ); | ||
2423 | |||
2424 | // Write the attributes. | ||
2425 | for ( i in attributes ) | ||
2426 | html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[ i ] ) + '" ' ); | ||
2427 | |||
2428 | // Write the content HTML. | ||
2429 | html.push( '>', innerHTML, '</', nodeName, '>' ); | ||
2430 | |||
2431 | // Add contents to the parent HTML array. | ||
2432 | htmlList.push( html.join( '' ) ); | ||
2433 | |||
2434 | ( this._ || ( this._ = {} ) ).dialog = dialog; | ||
2435 | |||
2436 | // Override isChanged if it is defined in element definition. | ||
2437 | if ( typeof elementDefinition.isChanged == 'boolean' ) | ||
2438 | this.isChanged = function() { | ||
2439 | return elementDefinition.isChanged; | ||
2440 | }; | ||
2441 | if ( typeof elementDefinition.isChanged == 'function' ) | ||
2442 | this.isChanged = elementDefinition.isChanged; | ||
2443 | |||
2444 | // Overload 'get(set)Value' on definition. | ||
2445 | if ( typeof elementDefinition.setValue == 'function' ) { | ||
2446 | this.setValue = CKEDITOR.tools.override( this.setValue, function( org ) { | ||
2447 | return function( val ) { | ||
2448 | org.call( this, elementDefinition.setValue.call( this, val ) ); | ||
2449 | }; | ||
2450 | } ); | ||
2451 | } | ||
2452 | |||
2453 | if ( typeof elementDefinition.getValue == 'function' ) { | ||
2454 | this.getValue = CKEDITOR.tools.override( this.getValue, function( org ) { | ||
2455 | return function() { | ||
2456 | return elementDefinition.getValue.call( this, org.call( this ) ); | ||
2457 | }; | ||
2458 | } ); | ||
2459 | } | ||
2460 | |||
2461 | // Add events. | ||
2462 | CKEDITOR.event.implementOn( this ); | ||
2463 | |||
2464 | this.registerEvents( elementDefinition ); | ||
2465 | if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey ) | ||
2466 | registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey ); | ||
2467 | |||
2468 | var me = this; | ||
2469 | dialog.on( 'load', function() { | ||
2470 | var input = me.getInputElement(); | ||
2471 | if ( input ) { | ||
2472 | var focusClass = me.type in { 'checkbox': 1, 'ratio': 1 } && CKEDITOR.env.ie && CKEDITOR.env.version < 8 ? 'cke_dialog_ui_focused' : ''; | ||
2473 | input.on( 'focus', function() { | ||
2474 | dialog._.tabBarMode = false; | ||
2475 | dialog._.hasFocus = true; | ||
2476 | me.fire( 'focus' ); | ||
2477 | focusClass && this.addClass( focusClass ); | ||
2478 | |||
2479 | } ); | ||
2480 | |||
2481 | input.on( 'blur', function() { | ||
2482 | me.fire( 'blur' ); | ||
2483 | focusClass && this.removeClass( focusClass ); | ||
2484 | } ); | ||
2485 | } | ||
2486 | } ); | ||
2487 | |||
2488 | // Completes this object with everything we have in the | ||
2489 | // definition. | ||
2490 | CKEDITOR.tools.extend( this, elementDefinition ); | ||
2491 | |||
2492 | // Register the object as a tab focus if it can be included. | ||
2493 | if ( this.keyboardFocusable ) { | ||
2494 | this.tabIndex = elementDefinition.tabIndex || 0; | ||
2495 | |||
2496 | this.focusIndex = dialog._.focusList.push( this ) - 1; | ||
2497 | this.on( 'focus', function() { | ||
2498 | dialog._.currentFocusIndex = me.focusIndex; | ||
2499 | } ); | ||
2500 | } | ||
2501 | }, | ||
2502 | |||
2503 | /** | ||
2504 | * Horizontal layout box for dialog UI elements, auto-expends to available width of container. | ||
2505 | * | ||
2506 | * @class CKEDITOR.ui.dialog.hbox | ||
2507 | * @extends CKEDITOR.ui.dialog.uiElement | ||
2508 | * @constructor Creates a hbox class instance. | ||
2509 | * @param {CKEDITOR.dialog} dialog Parent dialog object. | ||
2510 | * @param {Array} childObjList | ||
2511 | * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. | ||
2512 | * @param {Array} childHtmlList | ||
2513 | * Array of HTML code that correspond to the HTML output of all the | ||
2514 | * objects in childObjList. | ||
2515 | * @param {Array} htmlList | ||
2516 | * Array of HTML code that this element will output to. | ||
2517 | * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition | ||
2518 | * The element definition. Accepted fields: | ||
2519 | * | ||
2520 | * * `widths` (Optional) The widths of child cells. | ||
2521 | * * `height` (Optional) The height of the layout. | ||
2522 | * * `padding` (Optional) The padding width inside child cells. | ||
2523 | * * `align` (Optional) The alignment of the whole layout. | ||
2524 | */ | ||
2525 | hbox: function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) { | ||
2526 | if ( arguments.length < 4 ) | ||
2527 | return; | ||
2528 | |||
2529 | this._ || ( this._ = {} ); | ||
2530 | |||
2531 | var children = this._.children = childObjList, | ||
2532 | widths = elementDefinition && elementDefinition.widths || null, | ||
2533 | height = elementDefinition && elementDefinition.height || null, | ||
2534 | styles = {}, | ||
2535 | i; | ||
2536 | /** @ignore */ | ||
2537 | var innerHTML = function() { | ||
2538 | var html = [ '<tbody><tr class="cke_dialog_ui_hbox">' ]; | ||
2539 | for ( i = 0; i < childHtmlList.length; i++ ) { | ||
2540 | var className = 'cke_dialog_ui_hbox_child', | ||
2541 | styles = []; | ||
2542 | if ( i === 0 ) { | ||
2543 | className = 'cke_dialog_ui_hbox_first'; | ||
2544 | } | ||
2545 | if ( i == childHtmlList.length - 1 ) { | ||
2546 | className = 'cke_dialog_ui_hbox_last'; | ||
2547 | } | ||
2548 | |||
2549 | html.push( '<td class="', className, '" role="presentation" ' ); | ||
2550 | if ( widths ) { | ||
2551 | if ( widths[ i ] ) { | ||
2552 | styles.push( 'width:' + cssLength( widths[i] ) ); | ||
2553 | } | ||
2554 | } else { | ||
2555 | styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' ); | ||
2556 | } | ||
2557 | if ( height ) { | ||
2558 | styles.push( 'height:' + cssLength( height ) ); | ||
2559 | } | ||
2560 | if ( elementDefinition && elementDefinition.padding !== undefined ) { | ||
2561 | styles.push( 'padding:' + cssLength( elementDefinition.padding ) ); | ||
2562 | } | ||
2563 | // In IE Quirks alignment has to be done on table cells. (#7324) | ||
2564 | if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && children[ i ].align ) { | ||
2565 | styles.push( 'text-align:' + children[ i ].align ); | ||
2566 | } | ||
2567 | if ( styles.length > 0 ) { | ||
2568 | html.push( 'style="' + styles.join( '; ' ) + '" ' ); | ||
2569 | } | ||
2570 | html.push( '>', childHtmlList[ i ], '</td>' ); | ||
2571 | } | ||
2572 | html.push( '</tr></tbody>' ); | ||
2573 | return html.join( '' ); | ||
2574 | }; | ||
2575 | |||
2576 | var attribs = { role: 'presentation' }; | ||
2577 | elementDefinition && elementDefinition.align && ( attribs.align = elementDefinition.align ); | ||
2578 | |||
2579 | CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type: 'hbox' }, htmlList, 'table', styles, attribs, innerHTML ); | ||
2580 | }, | ||
2581 | |||
2582 | /** | ||
2583 | * Vertical layout box for dialog UI elements. | ||
2584 | * | ||
2585 | * @class CKEDITOR.ui.dialog.vbox | ||
2586 | * @extends CKEDITOR.ui.dialog.hbox | ||
2587 | * @constructor Creates a vbox class instance. | ||
2588 | * @param {CKEDITOR.dialog} dialog Parent dialog object. | ||
2589 | * @param {Array} childObjList | ||
2590 | * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. | ||
2591 | * @param {Array} childHtmlList | ||
2592 | * Array of HTML code that correspond to the HTML output of all the | ||
2593 | * objects in childObjList. | ||
2594 | * @param {Array} htmlList Array of HTML code that this element will output to. | ||
2595 | * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition | ||
2596 | * The element definition. Accepted fields: | ||
2597 | * | ||
2598 | * * `width` (Optional) The width of the layout. | ||
2599 | * * `heights` (Optional) The heights of individual cells. | ||
2600 | * * `align` (Optional) The alignment of the layout. | ||
2601 | * * `padding` (Optional) The padding width inside child cells. | ||
2602 | * * `expand` (Optional) Whether the layout should expand | ||
2603 | * vertically to fill its container. | ||
2604 | */ | ||
2605 | vbox: function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) { | ||
2606 | if ( arguments.length < 3 ) | ||
2607 | return; | ||
2608 | |||
2609 | this._ || ( this._ = {} ); | ||
2610 | |||
2611 | var children = this._.children = childObjList, | ||
2612 | width = elementDefinition && elementDefinition.width || null, | ||
2613 | heights = elementDefinition && elementDefinition.heights || null; | ||
2614 | /** @ignore */ | ||
2615 | var innerHTML = function() { | ||
2616 | var html = [ '<table role="presentation" cellspacing="0" border="0" ' ]; | ||
2617 | html.push( 'style="' ); | ||
2618 | if ( elementDefinition && elementDefinition.expand ) | ||
2619 | html.push( 'height:100%;' ); | ||
2620 | html.push( 'width:' + cssLength( width || '100%' ), ';' ); | ||
2621 | |||
2622 | // (#10123) Temp fix for dialog broken layout in latest webkit. | ||
2623 | if ( CKEDITOR.env.webkit ) | ||
2624 | html.push( 'float:none;' ); | ||
2625 | |||
2626 | html.push( '"' ); | ||
2627 | html.push( 'align="', CKEDITOR.tools.htmlEncode( | ||
2628 | ( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' ); | ||
2629 | |||
2630 | html.push( '><tbody>' ); | ||
2631 | for ( var i = 0; i < childHtmlList.length; i++ ) { | ||
2632 | var styles = []; | ||
2633 | html.push( '<tr><td role="presentation" ' ); | ||
2634 | if ( width ) | ||
2635 | styles.push( 'width:' + cssLength( width || '100%' ) ); | ||
2636 | if ( heights ) | ||
2637 | styles.push( 'height:' + cssLength( heights[ i ] ) ); | ||
2638 | else if ( elementDefinition && elementDefinition.expand ) | ||
2639 | styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' ); | ||
2640 | if ( elementDefinition && elementDefinition.padding !== undefined ) | ||
2641 | styles.push( 'padding:' + cssLength( elementDefinition.padding ) ); | ||
2642 | // In IE Quirks alignment has to be done on table cells. (#7324) | ||
2643 | if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && children[ i ].align ) | ||
2644 | styles.push( 'text-align:' + children[ i ].align ); | ||
2645 | if ( styles.length > 0 ) | ||
2646 | html.push( 'style="', styles.join( '; ' ), '" ' ); | ||
2647 | html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[ i ], '</td></tr>' ); | ||
2648 | } | ||
2649 | html.push( '</tbody></table>' ); | ||
2650 | return html.join( '' ); | ||
2651 | }; | ||
2652 | CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type: 'vbox' }, htmlList, 'div', null, { role: 'presentation' }, innerHTML ); | ||
2653 | } | ||
2654 | }; | ||
2655 | } )(); | ||
2656 | |||
2657 | /** @class CKEDITOR.ui.dialog.uiElement */ | ||
2658 | CKEDITOR.ui.dialog.uiElement.prototype = { | ||
2659 | /** | ||
2660 | * Gets the root DOM element of this dialog UI object. | ||
2661 | * | ||
2662 | * uiElement.getElement().hide(); | ||
2663 | * | ||
2664 | * @returns {CKEDITOR.dom.element} Root DOM element of UI object. | ||
2665 | */ | ||
2666 | getElement: function() { | ||
2667 | return CKEDITOR.document.getById( this.domId ); | ||
2668 | }, | ||
2669 | |||
2670 | /** | ||
2671 | * Gets the DOM element that the user inputs values. | ||
2672 | * | ||
2673 | * This function is used by {@link #setValue}, {@link #getValue} and {@link #focus}. It should | ||
2674 | * be overrided in child classes where the input element isn't the root | ||
2675 | * element. | ||
2676 | * | ||
2677 | * var rawValue = textInput.getInputElement().$.value; | ||
2678 | * | ||
2679 | * @returns {CKEDITOR.dom.element} The element where the user input values. | ||
2680 | */ | ||
2681 | getInputElement: function() { | ||
2682 | return this.getElement(); | ||
2683 | }, | ||
2684 | |||
2685 | /** | ||
2686 | * Gets the parent dialog object containing this UI element. | ||
2687 | * | ||
2688 | * var dialog = uiElement.getDialog(); | ||
2689 | * | ||
2690 | * @returns {CKEDITOR.dialog} Parent dialog object. | ||
2691 | */ | ||
2692 | getDialog: function() { | ||
2693 | return this._.dialog; | ||
2694 | }, | ||
2695 | |||
2696 | /** | ||
2697 | * Sets the value of this dialog UI object. | ||
2698 | * | ||
2699 | * uiElement.setValue( 'Dingo' ); | ||
2700 | * | ||
2701 | * @chainable | ||
2702 | * @param {Object} value The new value. | ||
2703 | * @param {Boolean} noChangeEvent Internal commit, to supress `change` event on this element. | ||
2704 | */ | ||
2705 | setValue: function( value, noChangeEvent ) { | ||
2706 | this.getInputElement().setValue( value ); | ||
2707 | !noChangeEvent && this.fire( 'change', { value: value } ); | ||
2708 | return this; | ||
2709 | }, | ||
2710 | |||
2711 | /** | ||
2712 | * Gets the current value of this dialog UI object. | ||
2713 | * | ||
2714 | * var myValue = uiElement.getValue(); | ||
2715 | * | ||
2716 | * @returns {Object} The current value. | ||
2717 | */ | ||
2718 | getValue: function() { | ||
2719 | return this.getInputElement().getValue(); | ||
2720 | }, | ||
2721 | |||
2722 | /** | ||
2723 | * Tells whether the UI object's value has changed. | ||
2724 | * | ||
2725 | * if ( uiElement.isChanged() ) | ||
2726 | * confirm( 'Value changed! Continue?' ); | ||
2727 | * | ||
2728 | * @returns {Boolean} `true` if changed, `false` if not changed. | ||
2729 | */ | ||
2730 | isChanged: function() { | ||
2731 | // Override in input classes. | ||
2732 | return false; | ||
2733 | }, | ||
2734 | |||
2735 | /** | ||
2736 | * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods. | ||
2737 | * | ||
2738 | * focus : function() { | ||
2739 | * this.selectParentTab(); | ||
2740 | * // do something else. | ||
2741 | * } | ||
2742 | * | ||
2743 | * @chainable | ||
2744 | */ | ||
2745 | selectParentTab: function() { | ||
2746 | var element = this.getInputElement(), | ||
2747 | cursor = element, | ||
2748 | tabId; | ||
2749 | while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 ) { | ||
2750 | |||
2751 | } | ||
2752 | |||
2753 | // Some widgets don't have parent tabs (e.g. OK and Cancel buttons). | ||
2754 | if ( !cursor ) | ||
2755 | return this; | ||
2756 | |||
2757 | tabId = cursor.getAttribute( 'name' ); | ||
2758 | // Avoid duplicate select. | ||
2759 | if ( this._.dialog._.currentTabId != tabId ) | ||
2760 | this._.dialog.selectPage( tabId ); | ||
2761 | return this; | ||
2762 | }, | ||
2763 | |||
2764 | /** | ||
2765 | * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page. | ||
2766 | * | ||
2767 | * uiElement.focus(); | ||
2768 | * | ||
2769 | * @chainable | ||
2770 | */ | ||
2771 | focus: function() { | ||
2772 | this.selectParentTab().getInputElement().focus(); | ||
2773 | return this; | ||
2774 | }, | ||
2775 | |||
2776 | /** | ||
2777 | * Registers the `on*` event handlers defined in the element definition. | ||
2778 | * | ||
2779 | * The default behavior of this function is: | ||
2780 | * | ||
2781 | * 1. If the on* event is defined in the class's eventProcesors list, | ||
2782 | * then the registration is delegated to the corresponding function | ||
2783 | * in the eventProcessors list. | ||
2784 | * 2. If the on* event is not defined in the eventProcessors list, then | ||
2785 | * register the event handler under the corresponding DOM event of | ||
2786 | * the UI element's input DOM element (as defined by the return value | ||
2787 | * of {@link #getInputElement}). | ||
2788 | * | ||
2789 | * This function is only called at UI element instantiation, but can | ||
2790 | * be overridded in child classes if they require more flexibility. | ||
2791 | * | ||
2792 | * @chainable | ||
2793 | * @param {CKEDITOR.dialog.definition.uiElement} definition The UI element | ||
2794 | * definition. | ||
2795 | */ | ||
2796 | registerEvents: function( definition ) { | ||
2797 | var regex = /^on([A-Z]\w+)/, | ||
2798 | match; | ||
2799 | |||
2800 | var registerDomEvent = function( uiElement, dialog, eventName, func ) { | ||
2801 | dialog.on( 'load', function() { | ||
2802 | uiElement.getInputElement().on( eventName, func, uiElement ); | ||
2803 | } ); | ||
2804 | }; | ||
2805 | |||
2806 | for ( var i in definition ) { | ||
2807 | if ( !( match = i.match( regex ) ) ) | ||
2808 | continue; | ||
2809 | if ( this.eventProcessors[ i ] ) | ||
2810 | this.eventProcessors[ i ].call( this, this._.dialog, definition[ i ] ); | ||
2811 | else | ||
2812 | registerDomEvent( this, this._.dialog, match[ 1 ].toLowerCase(), definition[ i ] ); | ||
2813 | } | ||
2814 | |||
2815 | return this; | ||
2816 | }, | ||
2817 | |||
2818 | /** | ||
2819 | * The event processor list used by | ||
2820 | * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element | ||
2821 | * instantiation. The default list defines three `on*` events: | ||
2822 | * | ||
2823 | * 1. `onLoad` - Called when the element's parent dialog opens for the | ||
2824 | * first time. | ||
2825 | * 2. `onShow` - Called whenever the element's parent dialog opens. | ||
2826 | * 3. `onHide` - Called whenever the element's parent dialog closes. | ||
2827 | * | ||
2828 | * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick | ||
2829 | * // handlers in the UI element's definitions. | ||
2830 | * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {}, | ||
2831 | * CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, | ||
2832 | * { onClick : function( dialog, func ) { this.on( 'click', func ); } }, | ||
2833 | * true | ||
2834 | * ); | ||
2835 | * | ||
2836 | * @property {Object} | ||
2837 | */ | ||
2838 | eventProcessors: { | ||
2839 | onLoad: function( dialog, func ) { | ||
2840 | dialog.on( 'load', func, this ); | ||
2841 | }, | ||
2842 | |||
2843 | onShow: function( dialog, func ) { | ||
2844 | dialog.on( 'show', func, this ); | ||
2845 | }, | ||
2846 | |||
2847 | onHide: function( dialog, func ) { | ||
2848 | dialog.on( 'hide', func, this ); | ||
2849 | } | ||
2850 | }, | ||
2851 | |||
2852 | /** | ||
2853 | * The default handler for a UI element's access key down event, which | ||
2854 | * tries to put focus to the UI element. | ||
2855 | * | ||
2856 | * Can be overridded in child classes for more sophisticaed behavior. | ||
2857 | * | ||
2858 | * @param {CKEDITOR.dialog} dialog The parent dialog object. | ||
2859 | * @param {String} key The key combination pressed. Since access keys | ||
2860 | * are defined to always include the `CTRL` key, its value should always | ||
2861 | * include a `'CTRL+'` prefix. | ||
2862 | */ | ||
2863 | accessKeyDown: function() { | ||
2864 | this.focus(); | ||
2865 | }, | ||
2866 | |||
2867 | /** | ||
2868 | * The default handler for a UI element's access key up event, which | ||
2869 | * does nothing. | ||
2870 | * | ||
2871 | * Can be overridded in child classes for more sophisticated behavior. | ||
2872 | * | ||
2873 | * @param {CKEDITOR.dialog} dialog The parent dialog object. | ||
2874 | * @param {String} key The key combination pressed. Since access keys | ||
2875 | * are defined to always include the `CTRL` key, its value should always | ||
2876 | * include a `'CTRL+'` prefix. | ||
2877 | */ | ||
2878 | accessKeyUp: function() {}, | ||
2879 | |||
2880 | /** | ||
2881 | * Disables a UI element. | ||
2882 | */ | ||
2883 | disable: function() { | ||
2884 | var element = this.getElement(), | ||
2885 | input = this.getInputElement(); | ||
2886 | input.setAttribute( 'disabled', 'true' ); | ||
2887 | element.addClass( 'cke_disabled' ); | ||
2888 | }, | ||
2889 | |||
2890 | /** | ||
2891 | * Enables a UI element. | ||
2892 | */ | ||
2893 | enable: function() { | ||
2894 | var element = this.getElement(), | ||
2895 | input = this.getInputElement(); | ||
2896 | input.removeAttribute( 'disabled' ); | ||
2897 | element.removeClass( 'cke_disabled' ); | ||
2898 | }, | ||
2899 | |||
2900 | /** | ||
2901 | * Determines whether an UI element is enabled or not. | ||
2902 | * | ||
2903 | * @returns {Boolean} Whether the UI element is enabled. | ||
2904 | */ | ||
2905 | isEnabled: function() { | ||
2906 | return !this.getElement().hasClass( 'cke_disabled' ); | ||
2907 | }, | ||
2908 | |||
2909 | /** | ||
2910 | * Determines whether an UI element is visible or not. | ||
2911 | * | ||
2912 | * @returns {Boolean} Whether the UI element is visible. | ||
2913 | */ | ||
2914 | isVisible: function() { | ||
2915 | return this.getInputElement().isVisible(); | ||
2916 | }, | ||
2917 | |||
2918 | /** | ||
2919 | * Determines whether an UI element is focus-able or not. | ||
2920 | * Focus-able is defined as being both visible and enabled. | ||
2921 | * | ||
2922 | * @returns {Boolean} Whether the UI element can be focused. | ||
2923 | */ | ||
2924 | isFocusable: function() { | ||
2925 | if ( !this.isEnabled() || !this.isVisible() ) | ||
2926 | return false; | ||
2927 | return true; | ||
2928 | } | ||
2929 | }; | ||
2930 | |||
2931 | /** @class CKEDITOR.ui.dialog.hbox */ | ||
2932 | CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement(), { | ||
2933 | /** | ||
2934 | * Gets a child UI element inside this container. | ||
2935 | * | ||
2936 | * var checkbox = hbox.getChild( [0,1] ); | ||
2937 | * checkbox.setValue( true ); | ||
2938 | * | ||
2939 | * @param {Array/Number} indices An array or a single number to indicate the child's | ||
2940 | * position in the container's descendant tree. Omit to get all the children in an array. | ||
2941 | * @returns {Array/CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container | ||
2942 | * if no argument given, or the specified UI element if indices is given. | ||
2943 | */ | ||
2944 | getChild: function( indices ) { | ||
2945 | // If no arguments, return a clone of the children array. | ||
2946 | if ( arguments.length < 1 ) | ||
2947 | return this._.children.concat(); | ||
2948 | |||
2949 | // If indices isn't array, make it one. | ||
2950 | if ( !indices.splice ) | ||
2951 | indices = [ indices ]; | ||
2952 | |||
2953 | // Retrieve the child element according to tree position. | ||
2954 | if ( indices.length < 2 ) | ||
2955 | return this._.children[ indices[ 0 ] ]; | ||
2956 | else | ||
2957 | return ( this._.children[ indices[ 0 ] ] && this._.children[ indices[ 0 ] ].getChild ) ? this._.children[ indices[ 0 ] ].getChild( indices.slice( 1, indices.length ) ) : null; | ||
2958 | } | ||
2959 | }, true ); | ||
2960 | |||
2961 | CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox(); | ||
2962 | |||
2963 | ( function() { | ||
2964 | var commonBuilder = { | ||
2965 | build: function( dialog, elementDefinition, output ) { | ||
2966 | var children = elementDefinition.children, | ||
2967 | child, | ||
2968 | childHtmlList = [], | ||
2969 | childObjList = []; | ||
2970 | for ( var i = 0; | ||
2971 | ( i < children.length && ( child = children[ i ] ) ); i++ ) { | ||
2972 | var childHtml = []; | ||
2973 | childHtmlList.push( childHtml ); | ||
2974 | childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); | ||
2975 | } | ||
2976 | return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition ); | ||
2977 | } | ||
2978 | }; | ||
2979 | |||
2980 | CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder ); | ||
2981 | CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder ); | ||
2982 | } )(); | ||
2983 | |||
2984 | /** | ||
2985 | * Generic dialog command. It opens a specific dialog when executed. | ||
2986 | * | ||
2987 | * // Register the "link" command, which opens the "link" dialog. | ||
2988 | * editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) ); | ||
2989 | * | ||
2990 | * @class | ||
2991 | * @constructor Creates a dialogCommand class instance. | ||
2992 | * @extends CKEDITOR.commandDefinition | ||
2993 | * @param {String} dialogName The name of the dialog to open when executing | ||
2994 | * this command. | ||
2995 | * @param {Object} [ext] Additional command definition's properties. | ||
2996 | */ | ||
2997 | CKEDITOR.dialogCommand = function( dialogName, ext ) { | ||
2998 | this.dialogName = dialogName; | ||
2999 | CKEDITOR.tools.extend( this, ext, true ); | ||
3000 | }; | ||
3001 | |||
3002 | CKEDITOR.dialogCommand.prototype = { | ||
3003 | exec: function( editor ) { | ||
3004 | editor.openDialog( this.dialogName ); | ||
3005 | }, | ||
3006 | |||
3007 | // Dialog commands just open a dialog ui, thus require no undo logic, | ||
3008 | // undo support should dedicate to specific dialog implementation. | ||
3009 | canUndo: false, | ||
3010 | |||
3011 | editorFocus: 1 | ||
3012 | }; | ||
3013 | |||
3014 | ( function() { | ||
3015 | var notEmptyRegex = /^([a]|[^a])+$/, | ||
3016 | integerRegex = /^\d*$/, | ||
3017 | numberRegex = /^\d*(?:\.\d+)?$/, | ||
3018 | htmlLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|\%)?)?$/, | ||
3019 | cssLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|em|ex|in|cm|mm|pt|pc|\%)?)?$/i, | ||
3020 | inlineStyleRegex = /^(\s*[\w-]+\s*:\s*[^:;]+(?:;|$))*$/; | ||
3021 | |||
3022 | CKEDITOR.VALIDATE_OR = 1; | ||
3023 | CKEDITOR.VALIDATE_AND = 2; | ||
3024 | |||
3025 | CKEDITOR.dialog.validate = { | ||
3026 | functions: function() { | ||
3027 | var args = arguments; | ||
3028 | return function() { | ||
3029 | /** | ||
3030 | * It's important for validate functions to be able to accept the value | ||
3031 | * as argument in addition to this.getValue(), so that it is possible to | ||
3032 | * combine validate functions together to make more sophisticated | ||
3033 | * validators. | ||
3034 | */ | ||
3035 | var value = this && this.getValue ? this.getValue() : args[ 0 ]; | ||
3036 | |||
3037 | var msg, | ||
3038 | relation = CKEDITOR.VALIDATE_AND, | ||
3039 | functions = [], | ||
3040 | i; | ||
3041 | |||
3042 | for ( i = 0; i < args.length; i++ ) { | ||
3043 | if ( typeof args[ i ] == 'function' ) | ||
3044 | functions.push( args[ i ] ); | ||
3045 | else | ||
3046 | break; | ||
3047 | } | ||
3048 | |||
3049 | if ( i < args.length && typeof args[ i ] == 'string' ) { | ||
3050 | msg = args[ i ]; | ||
3051 | i++; | ||
3052 | } | ||
3053 | |||
3054 | if ( i < args.length && typeof args[ i ] == 'number' ) | ||
3055 | relation = args[ i ]; | ||
3056 | |||
3057 | var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false ); | ||
3058 | for ( i = 0; i < functions.length; i++ ) { | ||
3059 | if ( relation == CKEDITOR.VALIDATE_AND ) | ||
3060 | passed = passed && functions[ i ]( value ); | ||
3061 | else | ||
3062 | passed = passed || functions[ i ]( value ); | ||
3063 | } | ||
3064 | |||
3065 | return !passed ? msg : true; | ||
3066 | }; | ||
3067 | }, | ||
3068 | |||
3069 | regex: function( regex, msg ) { | ||
3070 | /* | ||
3071 | * Can be greatly shortened by deriving from functions validator if code size | ||
3072 | * turns out to be more important than performance. | ||
3073 | */ | ||
3074 | return function() { | ||
3075 | var value = this && this.getValue ? this.getValue() : arguments[ 0 ]; | ||
3076 | return !regex.test( value ) ? msg : true; | ||
3077 | }; | ||
3078 | }, | ||
3079 | |||
3080 | notEmpty: function( msg ) { | ||
3081 | return this.regex( notEmptyRegex, msg ); | ||
3082 | }, | ||
3083 | |||
3084 | integer: function( msg ) { | ||
3085 | return this.regex( integerRegex, msg ); | ||
3086 | }, | ||
3087 | |||
3088 | 'number': function( msg ) { | ||
3089 | return this.regex( numberRegex, msg ); | ||
3090 | }, | ||
3091 | |||
3092 | 'cssLength': function( msg ) { | ||
3093 | return this.functions( function( val ) { | ||
3094 | return cssLengthRegex.test( CKEDITOR.tools.trim( val ) ); | ||
3095 | }, msg ); | ||
3096 | }, | ||
3097 | |||
3098 | 'htmlLength': function( msg ) { | ||
3099 | return this.functions( function( val ) { | ||
3100 | return htmlLengthRegex.test( CKEDITOR.tools.trim( val ) ); | ||
3101 | }, msg ); | ||
3102 | }, | ||
3103 | |||
3104 | 'inlineStyle': function( msg ) { | ||
3105 | return this.functions( function( val ) { | ||
3106 | return inlineStyleRegex.test( CKEDITOR.tools.trim( val ) ); | ||
3107 | }, msg ); | ||
3108 | }, | ||
3109 | |||
3110 | equals: function( value, msg ) { | ||
3111 | return this.functions( function( val ) { | ||
3112 | return val == value; | ||
3113 | }, msg ); | ||
3114 | }, | ||
3115 | |||
3116 | notEqual: function( value, msg ) { | ||
3117 | return this.functions( function( val ) { | ||
3118 | return val != value; | ||
3119 | }, msg ); | ||
3120 | } | ||
3121 | }; | ||
3122 | |||
3123 | CKEDITOR.on( 'instanceDestroyed', function( evt ) { | ||
3124 | // Remove dialog cover on last instance destroy. | ||
3125 | if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) ) { | ||
3126 | var currentTopDialog; | ||
3127 | while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) ) | ||
3128 | currentTopDialog.hide(); | ||
3129 | removeCovers(); | ||
3130 | } | ||
3131 | |||
3132 | var dialogs = evt.editor._.storedDialogs; | ||
3133 | for ( var name in dialogs ) | ||
3134 | dialogs[ name ].destroy(); | ||
3135 | |||
3136 | } ); | ||
3137 | |||
3138 | } )(); | ||
3139 | |||
3140 | // Extend the CKEDITOR.editor class with dialog specific functions. | ||
3141 | CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { | ||
3142 | /** | ||
3143 | * Loads and opens a registered dialog. | ||
3144 | * | ||
3145 | * CKEDITOR.instances.editor1.openDialog( 'smiley' ); | ||
3146 | * | ||
3147 | * @member CKEDITOR.editor | ||
3148 | * @param {String} dialogName The registered name of the dialog. | ||
3149 | * @param {Function} callback The function to be invoked after dialog instance created. | ||
3150 | * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. | ||
3151 | * `null` if the dialog name is not registered. | ||
3152 | * @see CKEDITOR.dialog#add | ||
3153 | */ | ||
3154 | openDialog: function( dialogName, callback ) { | ||
3155 | var dialog = null, dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ]; | ||
3156 | |||
3157 | if ( CKEDITOR.dialog._.currentTop === null ) | ||
3158 | showCover( this ); | ||
3159 | |||
3160 | // If the dialogDefinition is already loaded, open it immediately. | ||
3161 | if ( typeof dialogDefinitions == 'function' ) { | ||
3162 | var storedDialogs = this._.storedDialogs || ( this._.storedDialogs = {} ); | ||
3163 | |||
3164 | dialog = storedDialogs[ dialogName ] || ( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) ); | ||
3165 | |||
3166 | callback && callback.call( dialog, dialog ); | ||
3167 | dialog.show(); | ||
3168 | |||
3169 | } else if ( dialogDefinitions == 'failed' ) { | ||
3170 | hideCover( this ); | ||
3171 | throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName + '" failed when loading definition.' ); | ||
3172 | } else if ( typeof dialogDefinitions == 'string' ) { | ||
3173 | |||
3174 | CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), | ||
3175 | function() { | ||
3176 | var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ]; | ||
3177 | // In case of plugin error, mark it as loading failed. | ||
3178 | if ( typeof dialogDefinition != 'function' ) | ||
3179 | CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed'; | ||
3180 | |||
3181 | this.openDialog( dialogName, callback ); | ||
3182 | }, this, 0, 1 ); | ||
3183 | } | ||
3184 | |||
3185 | CKEDITOR.skin.loadPart( 'dialog' ); | ||
3186 | |||
3187 | return dialog; | ||
3188 | } | ||
3189 | } ); | ||
3190 | } )(); | ||
3191 | |||
3192 | CKEDITOR.plugins.add( 'dialog', { | ||
3193 | requires: 'dialogui', | ||
3194 | init: function( editor ) { | ||
3195 | editor.on( 'doubleclick', function( evt ) { | ||
3196 | if ( evt.data.dialog ) | ||
3197 | editor.openDialog( evt.data.dialog ); | ||
3198 | }, null, null, 999 ); | ||
3199 | } | ||
3200 | } ); | ||
3201 | |||
3202 | // Dialog related configurations. | ||
3203 | |||
3204 | /** | ||
3205 | * The color of the dialog background cover. It should be a valid CSS color string. | ||
3206 | * | ||
3207 | * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)'; | ||
3208 | * | ||
3209 | * @cfg {String} [dialog_backgroundCoverColor='white'] | ||
3210 | * @member CKEDITOR.config | ||
3211 | */ | ||
3212 | |||
3213 | /** | ||
3214 | * The opacity of the dialog background cover. It should be a number within the | ||
3215 | * range `[0.0, 1.0]`. | ||
3216 | * | ||
3217 | * config.dialog_backgroundCoverOpacity = 0.7; | ||
3218 | * | ||
3219 | * @cfg {Number} [dialog_backgroundCoverOpacity=0.5] | ||
3220 | * @member CKEDITOR.config | ||
3221 | */ | ||
3222 | |||
3223 | /** | ||
3224 | * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened. | ||
3225 | * | ||
3226 | * config.dialog_startupFocusTab = true; | ||
3227 | * | ||
3228 | * @cfg {Boolean} [dialog_startupFocusTab=false] | ||
3229 | * @member CKEDITOR.config | ||
3230 | */ | ||
3231 | |||
3232 | /** | ||
3233 | * The distance of magnetic borders used in moving and resizing dialogs, | ||
3234 | * measured in pixels. | ||
3235 | * | ||
3236 | * config.dialog_magnetDistance = 30; | ||
3237 | * | ||
3238 | * @cfg {Number} [dialog_magnetDistance=20] | ||
3239 | * @member CKEDITOR.config | ||
3240 | */ | ||
3241 | |||
3242 | /** | ||
3243 | * The guideline to follow when generating the dialog buttons. There are 3 possible options: | ||
3244 | * | ||
3245 | * * `'OS'` - the buttons will be displayed in the default order of the user's OS; | ||
3246 | * * `'ltr'` - for Left-To-Right order; | ||
3247 | * * `'rtl'` - for Right-To-Left order. | ||
3248 | * | ||
3249 | * Example: | ||
3250 | * | ||
3251 | * config.dialog_buttonsOrder = 'rtl'; | ||
3252 | * | ||
3253 | * @since 3.5 | ||
3254 | * @cfg {String} [dialog_buttonsOrder='OS'] | ||
3255 | * @member CKEDITOR.config | ||
3256 | */ | ||
3257 | |||
3258 | /** | ||
3259 | * The dialog contents to removed. It's a string composed by dialog name and tab name with a colon between them. | ||
3260 | * | ||
3261 | * Separate each pair with semicolon (see example). | ||
3262 | * | ||
3263 | * **Note:** All names are case-sensitive. | ||
3264 | * | ||
3265 | * **Note:** Be cautious when specifying dialog tabs that are mandatory, | ||
3266 | * like `'info'`, dialog functionality might be broken because of this! | ||
3267 | * | ||
3268 | * config.removeDialogTabs = 'flash:advanced;image:Link'; | ||
3269 | * | ||
3270 | * @since 3.5 | ||
3271 | * @cfg {String} [removeDialogTabs=''] | ||
3272 | * @member CKEDITOR.config | ||
3273 | */ | ||
3274 | |||
3275 | /** | ||
3276 | * Tells if user should not be asked to confirm close, if any dialog field was modified. | ||
3277 | * By default it is set to `false` meaning that the confirmation dialog will be shown. | ||
3278 | * | ||
3279 | * config.dialog_noConfirmCancel = true; | ||
3280 | * | ||
3281 | * @since 4.3 | ||
3282 | * @cfg {Boolean} [dialog_noConfirmCancel=false] | ||
3283 | * @member CKEDITOR.config | ||
3284 | */ | ||
3285 | |||
3286 | /** | ||
3287 | * Event fired when a dialog definition is about to be used to create a dialog into | ||
3288 | * an editor instance. This event makes it possible to customize the definition | ||
3289 | * before creating it. | ||
3290 | * | ||
3291 | * Note that this event is called only the first time a specific dialog is | ||
3292 | * opened. Successive openings will use the cached dialog, and this event will | ||
3293 | * not get fired. | ||
3294 | * | ||
3295 | * @event dialogDefinition | ||
3296 | * @member CKEDITOR | ||
3297 | * @param {CKEDITOR.dialog.definition} data The dialog defination that | ||
3298 | * is being loaded. | ||
3299 | * @param {CKEDITOR.editor} editor The editor instance that will use the dialog. | ||
3300 | */ | ||
3301 | |||
3302 | /** | ||
3303 | * Event fired when a tab is going to be selected in a dialog. | ||
3304 | * | ||
3305 | * @event selectPage | ||
3306 | * @member CKEDITOR.dialog | ||
3307 | * @param data | ||
3308 | * @param {String} data.page The id of the page that it's gonna be selected. | ||
3309 | * @param {String} data.currentPage The id of the current page. | ||
3310 | */ | ||
3311 | |||
3312 | /** | ||
3313 | * Event fired when the user tries to dismiss a dialog. | ||
3314 | * | ||
3315 | * @event cancel | ||
3316 | * @member CKEDITOR.dialog | ||
3317 | * @param data | ||
3318 | * @param {Boolean} data.hide Whether the event should proceed or not. | ||
3319 | */ | ||
3320 | |||
3321 | /** | ||
3322 | * Event fired when the user tries to confirm a dialog. | ||
3323 | * | ||
3324 | * @event ok | ||
3325 | * @member CKEDITOR.dialog | ||
3326 | * @param data | ||
3327 | * @param {Boolean} data.hide Whether the event should proceed or not. | ||
3328 | */ | ||
3329 | |||
3330 | /** | ||
3331 | * Event fired when a dialog is shown. | ||
3332 | * | ||
3333 | * @event show | ||
3334 | * @member CKEDITOR.dialog | ||
3335 | */ | ||
3336 | |||
3337 | /** | ||
3338 | * Event fired when a dialog is shown. | ||
3339 | * | ||
3340 | * @event dialogShow | ||
3341 | * @member CKEDITOR.editor | ||
3342 | * @param {CKEDITOR.editor} editor This editor instance. | ||
3343 | * @param {CKEDITOR.dialog} data The opened dialog instance. | ||
3344 | */ | ||
3345 | |||
3346 | /** | ||
3347 | * Event fired when a dialog is hidden. | ||
3348 | * | ||
3349 | * @event hide | ||
3350 | * @member CKEDITOR.dialog | ||
3351 | */ | ||
3352 | |||
3353 | /** | ||
3354 | * Event fired when a dialog is hidden. | ||
3355 | * | ||
3356 | * @event dialogHide | ||
3357 | * @member CKEDITOR.editor | ||
3358 | * @param {CKEDITOR.editor} editor This editor instance. | ||
3359 | * @param {CKEDITOR.dialog} data The hidden dialog instance. | ||
3360 | */ | ||
3361 | |||
3362 | /** | ||
3363 | * Event fired when a dialog is being resized. The event is fired on | ||
3364 | * both the {@link CKEDITOR.dialog} object and the dialog instance | ||
3365 | * since 3.5.3, previously it was only available in the global object. | ||
3366 | * | ||
3367 | * @static | ||
3368 | * @event resize | ||
3369 | * @member CKEDITOR.dialog | ||
3370 | * @param data | ||
3371 | * @param {CKEDITOR.dialog} data.dialog The dialog being resized (if | ||
3372 | * it is fired on the dialog itself, this parameter is not sent). | ||
3373 | * @param {String} data.skin The skin name. | ||
3374 | * @param {Number} data.width The new width. | ||
3375 | * @param {Number} data.height The new height. | ||
3376 | */ | ||
3377 | |||
3378 | /** | ||
3379 | * Event fired when a dialog is being resized. The event is fired on | ||
3380 | * both the {@link CKEDITOR.dialog} object and the dialog instance | ||
3381 | * since 3.5.3, previously it was only available in the global object. | ||
3382 | * | ||
3383 | * @since 3.5 | ||
3384 | * @event resize | ||
3385 | * @member CKEDITOR.dialog | ||
3386 | * @param data | ||
3387 | * @param {Number} data.width The new width. | ||
3388 | * @param {Number} data.height The new height. | ||
3389 | */ | ||
3390 | |||
3391 | /** | ||
3392 | * Event fired when the dialog state changes, usually by {@link CKEDITOR.dialog#setState}. | ||
3393 | * | ||
3394 | * @since 4.5 | ||
3395 | * @event state | ||
3396 | * @member CKEDITOR.dialog | ||
3397 | * @param data | ||
3398 | * @param {Number} data The new state. Either {@link CKEDITOR#DIALOG_STATE_IDLE} or {@link CKEDITOR#DIALOG_STATE_BUSY}. | ||
3399 | */ | ||
diff --git a/sources/plugins/dialog/samples/assets/my_dialog.js b/sources/plugins/dialog/samples/assets/my_dialog.js new file mode 100644 index 0000000..6239dea --- /dev/null +++ b/sources/plugins/dialog/samples/assets/my_dialog.js | |||
@@ -0,0 +1,49 @@ | |||
1 | /** | ||
2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | ||
4 | */ | ||
5 | |||
6 | CKEDITOR.dialog.add( 'myDialog', function() { | ||
7 | return { | ||
8 | title: 'My Dialog', | ||
9 | minWidth: 400, | ||
10 | minHeight: 200, | ||
11 | contents: [ | ||
12 | { | ||
13 | id: 'tab1', | ||
14 | label: 'First Tab', | ||
15 | title: 'First Tab', | ||
16 | elements: [ | ||
17 | { | ||
18 | id: 'input1', | ||
19 | type: 'text', | ||
20 | label: 'Text Field' | ||
21 | }, | ||
22 | { | ||
23 | id: 'select1', | ||
24 | type: 'select', | ||
25 | label: 'Select Field', | ||
26 | items: [ | ||
27 | [ 'option1', 'value1' ], | ||
28 | [ 'option2', 'value2' ] | ||
29 | ] | ||
30 | } | ||
31 | ] | ||
32 | }, | ||
33 | { | ||
34 | id: 'tab2', | ||
35 | label: 'Second Tab', | ||
36 | title: 'Second Tab', | ||
37 | elements: [ | ||
38 | { | ||
39 | id: 'button1', | ||
40 | type: 'button', | ||
41 | label: 'Button Field' | ||
42 | } | ||
43 | ] | ||
44 | } | ||
45 | ] | ||
46 | }; | ||
47 | } ); | ||
48 | |||
49 | // %LEAVE_UNMINIFIED% %REMOVE_LINE% | ||
diff --git a/sources/plugins/dialog/samples/dialog.html b/sources/plugins/dialog/samples/dialog.html new file mode 100644 index 0000000..0f22a1a --- /dev/null +++ b/sources/plugins/dialog/samples/dialog.html | |||
@@ -0,0 +1,190 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <!-- | ||
3 | Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
4 | For licensing, see LICENSE.md or http://ckeditor.com/license | ||
5 | --> | ||
6 | <html> | ||
7 | <head> | ||
8 | <meta charset="utf-8"> | ||
9 | <title>Using API to Customize Dialog Windows — CKEditor Sample</title> | ||
10 | <script src="../../../ckeditor.js"></script> | ||
11 | <link rel="stylesheet" href="../../../samples/old/sample.css"> | ||
12 | <meta name="ckeditor-sample-name" content="Using the JavaScript API to customize dialog windows"> | ||
13 | <meta name="ckeditor-sample-group" content="Advanced Samples"> | ||
14 | <meta name="ckeditor-sample-description" content="Using the dialog windows API to customize dialog windows without changing the original editor code."> | ||
15 | <style> | ||
16 | |||
17 | .cke_button__mybutton_icon | ||
18 | { | ||
19 | display: none !important; | ||
20 | } | ||
21 | |||
22 | .cke_button__mybutton_label | ||
23 | { | ||
24 | display: inline !important; | ||
25 | } | ||
26 | |||
27 | </style> | ||
28 | <script> | ||
29 | |||
30 | CKEDITOR.on( 'instanceCreated', function( ev ){ | ||
31 | var editor = ev.editor; | ||
32 | |||
33 | // Listen for the "pluginsLoaded" event, so we are sure that the | ||
34 | // "dialog" plugin has been loaded and we are able to do our | ||
35 | // customizations. | ||
36 | editor.on( 'pluginsLoaded', function() { | ||
37 | |||
38 | // If our custom dialog has not been registered, do that now. | ||
39 | if ( !CKEDITOR.dialog.exists( 'myDialog' ) ) { | ||
40 | // We need to do the following trick to find out the dialog | ||
41 | // definition file URL path. In the real world, you would simply | ||
42 | // point to an absolute path directly, like "/mydir/mydialog.js". | ||
43 | var href = document.location.href.split( '/' ); | ||
44 | href.pop(); | ||
45 | href.push( 'assets/my_dialog.js' ); | ||
46 | href = href.join( '/' ); | ||
47 | |||
48 | // Finally, register the dialog. | ||
49 | CKEDITOR.dialog.add( 'myDialog', href ); | ||
50 | } | ||
51 | |||
52 | // Register the command used to open the dialog. | ||
53 | editor.addCommand( 'myDialogCmd', new CKEDITOR.dialogCommand( 'myDialog' ) ); | ||
54 | |||
55 | // Add the a custom toolbar buttons, which fires the above | ||
56 | // command.. | ||
57 | editor.ui.add( 'MyButton', CKEDITOR.UI_BUTTON, { | ||
58 | label: 'My Dialog', | ||
59 | command: 'myDialogCmd' | ||
60 | }); | ||
61 | }); | ||
62 | }); | ||
63 | |||
64 | // When opening a dialog, its "definition" is created for it, for | ||
65 | // each editor instance. The "dialogDefinition" event is then | ||
66 | // fired. We should use this event to make customizations to the | ||
67 | // definition of existing dialogs. | ||
68 | CKEDITOR.on( 'dialogDefinition', function( ev ) { | ||
69 | // Take the dialog name and its definition from the event data. | ||
70 | var dialogName = ev.data.name; | ||
71 | var dialogDefinition = ev.data.definition; | ||
72 | |||
73 | // Check if the definition is from the dialog we're | ||
74 | // interested on (the "Link" dialog). | ||
75 | if ( dialogName == 'myDialog' && ev.editor.name == 'editor2' ) { | ||
76 | // Get a reference to the "Link Info" tab. | ||
77 | var infoTab = dialogDefinition.getContents( 'tab1' ); | ||
78 | |||
79 | // Add a new text field to the "tab1" tab page. | ||
80 | infoTab.add( { | ||
81 | type: 'text', | ||
82 | label: 'My Custom Field', | ||
83 | id: 'customField', | ||
84 | 'default': 'Sample!', | ||
85 | validate: function() { | ||
86 | if ( ( /\d/ ).test( this.getValue() ) ) | ||
87 | return 'My Custom Field must not contain digits'; | ||
88 | } | ||
89 | }); | ||
90 | |||
91 | // Remove the "select1" field from the "tab1" tab. | ||
92 | infoTab.remove( 'select1' ); | ||
93 | |||
94 | // Set the default value for "input1" field. | ||
95 | var input1 = infoTab.get( 'input1' ); | ||
96 | input1[ 'default' ] = 'www.example.com'; | ||
97 | |||
98 | // Remove the "tab2" tab page. | ||
99 | dialogDefinition.removeContents( 'tab2' ); | ||
100 | |||
101 | // Add a new tab to the "Link" dialog. | ||
102 | dialogDefinition.addContents( { | ||
103 | id: 'customTab', | ||
104 | label: 'My Tab', | ||
105 | accessKey: 'M', | ||
106 | elements: [ | ||
107 | { | ||
108 | id: 'myField1', | ||
109 | type: 'text', | ||
110 | label: 'My Text Field' | ||
111 | }, | ||
112 | { | ||
113 | id: 'myField2', | ||
114 | type: 'text', | ||
115 | label: 'Another Text Field' | ||
116 | } | ||
117 | ] | ||
118 | }); | ||
119 | |||
120 | // Provide the focus handler to start initial focus in "customField" field. | ||
121 | dialogDefinition.onFocus = function() { | ||
122 | var urlField = this.getContentElement( 'tab1', 'customField' ); | ||
123 | urlField.select(); | ||
124 | }; | ||
125 | } | ||
126 | }); | ||
127 | |||
128 | var config = { | ||
129 | extraPlugins: 'dialog', | ||
130 | toolbar: [ [ 'MyButton' ] ] | ||
131 | }; | ||
132 | |||
133 | </script> | ||
134 | </head> | ||
135 | <body> | ||
136 | <h1 class="samples"> | ||
137 | <a href="../../../samples/old/index.html">CKEditor Samples</a> » Using CKEditor Dialog API | ||
138 | </h1> | ||
139 | <div class="warning deprecated"> | ||
140 | This sample is not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>. | ||
141 | </div> | ||
142 | <div class="description"> | ||
143 | <p> | ||
144 | This sample shows how to use the | ||
145 | <a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.dialog">CKEditor Dialog API</a> | ||
146 | to customize CKEditor dialog windows without changing the original editor code. | ||
147 | The following customizations are being done in the example below: | ||
148 | </p> | ||
149 | <p> | ||
150 | For details on how to create this setup check the source code of this sample page. | ||
151 | </p> | ||
152 | </div> | ||
153 | <p>A custom dialog is added to the editors using the <code>pluginsLoaded</code> event, from an external <a target="_blank" href="assets/my_dialog.js">dialog definition file</a>:</p> | ||
154 | <ol> | ||
155 | <li><strong>Creating a custom dialog window</strong> – "My Dialog" dialog window opened with the "My Dialog" toolbar button.</li> | ||
156 | <li><strong>Creating a custom button</strong> – Add button to open the dialog with "My Dialog" toolbar button.</li> | ||
157 | </ol> | ||
158 | <textarea cols="80" id="editor1" name="editor1" rows="10"><p>This is some <strong>sample text</strong>. You are using <a href="http://ckeditor.com/">CKEditor</a>.</p></textarea> | ||
159 | <script> | ||
160 | // Replace the <textarea id="editor1"> with an CKEditor instance. | ||
161 | CKEDITOR.replace( 'editor1', config ); | ||
162 | </script> | ||
163 | <p>The below editor modify the dialog definition of the above added dialog using the <code>dialogDefinition</code> event:</p> | ||
164 | <ol> | ||
165 | <li><strong>Adding dialog tab</strong> – Add new tab "My Tab" to dialog window.</li> | ||
166 | <li><strong>Removing a dialog window tab</strong> – Remove "Second Tab" page from the dialog window.</li> | ||
167 | <li><strong>Adding dialog window fields</strong> – Add "My Custom Field" to the dialog window.</li> | ||
168 | <li><strong>Removing dialog window field</strong> – Remove "Select Field" selection field from the dialog window.</li> | ||
169 | <li><strong>Setting default values for dialog window fields</strong> – Set default value of "Text Field" text field. </li> | ||
170 | <li><strong>Setup initial focus for dialog window</strong> – Put initial focus on "My Custom Field" text field. </li> | ||
171 | </ol> | ||
172 | <textarea cols="80" id="editor2" name="editor2" rows="10"><p>This is some <strong>sample text</strong>. You are using <a href="http://ckeditor.com/">CKEditor</a>.</p></textarea> | ||
173 | <script> | ||
174 | |||
175 | // Replace the <textarea id="editor1"> with an CKEditor instance. | ||
176 | CKEDITOR.replace( 'editor2', config ); | ||
177 | |||
178 | </script> | ||
179 | <div id="footer"> | ||
180 | <hr> | ||
181 | <p> | ||
182 | CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a> | ||
183 | </p> | ||
184 | <p id="copy"> | ||
185 | Copyright © 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico | ||
186 | Knabben. All rights reserved. | ||
187 | </p> | ||
188 | </div> | ||
189 | </body> | ||
190 | </html> | ||