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