2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
7 * @fileOverview The floating dialog plugin.
11 * No resize for this dialog.
14 CKEDITOR
.DIALOG_RESIZE_NONE
= 0;
17 * Only allow horizontal resizing for this dialog, disable vertical resizing.
20 CKEDITOR
.DIALOG_RESIZE_WIDTH
= 1;
23 * Only allow vertical resizing for this dialog, disable horizontal resizing.
26 CKEDITOR
.DIALOG_RESIZE_HEIGHT
= 2;
29 * Allow the dialog to be resized in both directions.
32 CKEDITOR
.DIALOG_RESIZE_BOTH
= 3;
36 function isTabVisible( tabId
)
38 return !!this._
.tabs
[ tabId
][ 0 ].$.offsetHeight
;
41 function getPreviousVisibleTab()
43 var tabId
= this._
.currentTabId
,
44 length
= this._
.tabIdList
.length
,
45 tabIndex
= CKEDITOR
.tools
.indexOf( this._
.tabIdList
, tabId
) + length
;
47 for ( var i
= tabIndex
- 1 ; i
> tabIndex
- length
; i
-- )
49 if ( isTabVisible
.call( this, this._
.tabIdList
[ i
% length
] ) )
50 return this._
.tabIdList
[ i
% length
];
56 function getNextVisibleTab()
58 var tabId
= this._
.currentTabId
,
59 length
= this._
.tabIdList
.length
,
60 tabIndex
= CKEDITOR
.tools
.indexOf( this._
.tabIdList
, tabId
);
62 for ( var i
= tabIndex
+ 1 ; i
< tabIndex
+ length
; i
++ )
64 if ( isTabVisible
.call( this, this._
.tabIdList
[ i
% length
] ) )
65 return this._
.tabIdList
[ i
% length
];
72 * This is the base class for runtime dialog objects. An instance of this
73 * class represents a single named dialog for a single editor instance.
74 * @param {Object} editor The editor which created the dialog.
75 * @param {String} dialogName The dialog's registered name.
78 * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' );
80 CKEDITOR
.dialog = function( editor
, dialogName
)
82 // Load the dialog definition.
83 var definition
= CKEDITOR
.dialog
._
.dialogDefinitions
[ dialogName
];
85 // Completes the definition with the default values.
86 definition
= CKEDITOR
.tools
.extend( definition( editor
), defaultDialogDefinition
);
88 // Clone a functionally independent copy for this dialog.
89 definition
= CKEDITOR
.tools
.clone( definition
);
91 // Create a complex definition object, extending it with the API
93 definition
= new definitionObject( this, definition
);
96 var doc
= CKEDITOR
.document
;
98 var themeBuilt
= editor
.theme
.buildDialog( editor
);
100 // Initialize some basic parameters.
104 element
: themeBuilt
.element
,
106 contentSize
: { width
: 0, height
: 0 },
107 size
: { width
: 0, height
: 0 },
113 // Initialize the tab and page map.
117 currentTabIndex
: null,
122 // Initialize the tab order array for input widgets.
124 currentFocusIndex
: 0,
128 this.parts
= themeBuilt
.parts
;
130 CKEDITOR
.tools
.setTimeout( function()
132 editor
.fire( 'ariaWidget', this.parts
.contents
);
136 // Set the startup styles for the dialog, avoiding it enlarging the
137 // page size on the dialog creation.
138 this.parts
.dialog
.setStyles(
140 position
: CKEDITOR
.env
.ie6Compat
? 'absolute' : 'fixed',
143 visibility
: 'hidden'
146 // Call the CKEDITOR.event constructor to initialize this instance.
147 CKEDITOR
.event
.call( this );
149 // Fire the "dialogDefinition" event, making it possible to customize
150 // the dialog definition.
151 this.definition
= definition
= CKEDITOR
.fire( 'dialogDefinition',
154 definition
: definition
156 , editor
).definition
;
157 // Initialize load, show, hide, ok and cancel events.
158 if ( definition
.onLoad
)
159 this.on( 'load', definition
.onLoad
);
161 if ( definition
.onShow
)
162 this.on( 'show', definition
.onShow
);
164 if ( definition
.onHide
)
165 this.on( 'hide', definition
.onHide
);
167 if ( definition
.onOk
)
169 this.on( 'ok', function( evt
)
171 if ( definition
.onOk
.call( this, evt
) === false )
172 evt
.data
.hide
= false;
176 if ( definition
.onCancel
)
178 this.on( 'cancel', function( evt
)
180 if ( definition
.onCancel
.call( this, evt
) === false )
181 evt
.data
.hide
= false;
187 // Iterates over all items inside all content in the dialog, calling a
188 // function for each of them.
189 var iterContents = function( func
)
191 var contents
= me
._
.contents
,
194 for ( var i
in contents
)
196 for ( var j
in contents
[i
] )
198 stop
= func
.call( this, contents
[i
][j
] );
205 this.on( 'ok', function( evt
)
207 iterContents( function( item
)
211 var isValid
= item
.validate( this );
213 if ( typeof isValid
== 'string' )
219 if ( isValid
=== false )
226 evt
.data
.hide
= false;
234 this.on( 'cancel', function( evt
)
236 iterContents( function( item
)
238 if ( item
.isChanged() )
240 if ( !confirm( editor
.lang
.common
.confirmCancel
) )
241 evt
.data
.hide
= false;
247 this.parts
.close
.on( 'click', function( evt
)
249 if ( this.fire( 'cancel', { hide
: true } ).hide
!== false )
253 // Sort focus list according to tab order definitions.
254 function setupFocus()
256 var focusList
= me
._
.focusList
;
257 focusList
.sort( function( a
, b
)
259 // Mimics browser tab order logics;
260 if ( a
.tabIndex
!= b
.tabIndex
)
261 return b
.tabIndex
- a
.tabIndex
;
262 // Sort is not stable in some browsers,
263 // fall-back the comparator to 'focusIndex';
265 return a
.focusIndex
- b
.focusIndex
;
268 var size
= focusList
.length
;
269 for ( var i
= 0; i
< size
; i
++ )
270 focusList
[ i
].focusIndex
= i
;
273 function changeFocus( forward
)
275 var focusList
= me
._
.focusList
,
276 offset
= forward
? 1 : -1;
277 if ( focusList
.length
< 1 )
280 var current
= me
._
.currentFocusIndex
;
282 // Trigger the 'blur' event of any input element before anything,
283 // since certain UI updates may depend on it.
286 focusList
[ current
].getInputElement().$.blur();
290 var startIndex
= ( current
+ offset
+ focusList
.length
) % focusList
.length
,
291 currentIndex
= startIndex
;
292 while ( !focusList
[ currentIndex
].isFocusable() )
294 currentIndex
= ( currentIndex
+ offset
+ focusList
.length
) % focusList
.length
;
295 if ( currentIndex
== startIndex
)
298 focusList
[ currentIndex
].focus();
300 // Select whole field content.
301 if ( focusList
[ currentIndex
].type
== 'text' )
302 focusList
[ currentIndex
].select();
305 this.changeFocus
= changeFocus
;
309 function focusKeydownHandler( evt
)
311 // If I'm not the top dialog, ignore.
312 if ( me
!= CKEDITOR
.dialog
._
.currentTop
)
315 var keystroke
= evt
.data
.getKeystroke();
318 if ( keystroke
== 9 || keystroke
== CKEDITOR
.SHIFT
+ 9 )
320 var shiftPressed
= ( keystroke
== CKEDITOR
.SHIFT
+ 9 );
322 // Handling Tab and Shift-Tab.
323 if ( me
._
.tabBarMode
)
326 var nextId
= shiftPressed
? getPreviousVisibleTab
.call( me
) : getNextVisibleTab
.call( me
);
327 me
.selectPage( nextId
);
328 me
._
.tabs
[ nextId
][ 0 ].focus();
332 // Change the focus of inputs.
333 changeFocus( !shiftPressed
);
338 else if ( keystroke
== CKEDITOR
.ALT
+ 121 && !me
._
.tabBarMode
&& me
.getPageCount() > 1 )
340 // Alt-F10 puts focus into the current tab item in the tab bar.
341 me
._
.tabBarMode
= true;
342 me
._
.tabs
[ me
._
.currentTabId
][ 0 ].focus();
345 else if ( ( keystroke
== 37 || keystroke
== 39 ) && me
._
.tabBarMode
)
347 // Arrow keys - used for changing tabs.
348 nextId
= ( keystroke
== 37 ? getPreviousVisibleTab
.call( me
) : getNextVisibleTab
.call( me
) );
349 me
.selectPage( nextId
);
350 me
._
.tabs
[ nextId
][ 0 ].focus();
353 else if ( ( keystroke
== 13 || keystroke
== 32 ) && me
._
.tabBarMode
)
355 this.selectPage( this._
.currentTabId
);
356 this._
.tabBarMode
= false;
357 this._
.currentFocusIndex
= -1;
365 evt
.data
.preventDefault();
369 function focusKeyPressHandler( evt
)
371 processed
&& evt
.data
.preventDefault();
374 var dialogElement
= this._
.element
;
375 // Add the dialog keyboard handlers.
376 this.on( 'show', function()
378 dialogElement
.on( 'keydown', focusKeydownHandler
, this, null, 0 );
379 // Some browsers instead, don't cancel key events in the keydown, but in the
380 // keypress. So we must do a longer trip in those cases. (#4531)
381 if ( CKEDITOR
.env
.opera
|| ( CKEDITOR
.env
.gecko
&& CKEDITOR
.env
.mac
) )
382 dialogElement
.on( 'keypress', focusKeyPressHandler
, this );
384 if ( CKEDITOR
.env
.ie6Compat
)
386 var coverDoc
= coverElement
.getChild( 0 ).getFrameDocument();
387 coverDoc
.on( 'keydown', focusKeydownHandler
, this, null, 0 );
390 this.on( 'hide', function()
392 dialogElement
.removeListener( 'keydown', focusKeydownHandler
);
393 if ( CKEDITOR
.env
.opera
|| ( CKEDITOR
.env
.gecko
&& CKEDITOR
.env
.mac
) )
394 dialogElement
.removeListener( 'keypress', focusKeyPressHandler
);
396 this.on( 'iframeAdded', function( evt
)
398 var doc
= new CKEDITOR
.dom
.document( evt
.data
.iframe
.$.contentWindow
.document
);
399 doc
.on( 'keydown', focusKeydownHandler
, this, null, 0 );
402 // Auto-focus logic in dialog.
403 this.on( 'show', function()
405 // Setup tabIndex on showing the dialog instead of on loading
406 // to allow dynamic tab order happen in dialog definition.
409 if ( editor
.config
.dialog_startupFocusTab
410 && me
._
.tabIdList
.length
> 1 )
412 me
._
.tabBarMode
= true;
413 me
._
.tabs
[ me
._
.currentTabId
][ 0 ].focus();
415 else if ( !this._
.hasFocus
)
417 this._
.currentFocusIndex
= -1;
419 // Decide where to put the initial focus.
420 if ( definition
.onFocus
)
422 var initialFocus
= definition
.onFocus
.call( this );
423 // Focus the field that the user specified.
424 initialFocus
&& initialFocus
.focus();
426 // Focus the first field in layout order.
431 * IE BUG: If the initial focus went into a non-text element (e.g. button),
432 * then IE would still leave the caret inside the editing area.
434 if ( this._
.editor
.mode
== 'wysiwyg' && CKEDITOR
.env
.ie
)
436 var $selection
= editor
.document
.$.selection
,
437 $range
= $selection
.createRange();
441 if ( $range
.parentElement
&& $range
.parentElement().ownerDocument
== editor
.document
.$
442 || $range
.item
&& $range
.item( 0 ).ownerDocument
== editor
.document
.$ )
444 var $myRange
= document
.body
.createTextRange();
445 $myRange
.moveToElementText( this.getElement().getFirst().$ );
446 $myRange
.collapse( true );
452 }, this, null, 0xffffffff );
454 // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661).
455 // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken.
456 if ( CKEDITOR
.env
.ie6Compat
)
458 this.on( 'load', function( evt
)
460 var outer
= this.getElement(),
461 inner
= outer
.getFirst();
463 inner
.appendTo( outer
);
467 initDragAndDrop( this );
468 initResizeHandles( this );
471 ( new CKEDITOR
.dom
.text( definition
.title
, CKEDITOR
.document
) ).appendTo( this.parts
.title
);
473 // Insert the tabs and contents.
474 for ( var i
= 0 ; i
< definition
.contents
.length
; i
++ )
475 this.addPage( definition
.contents
[i
] );
477 this.parts
['tabs'].on( 'click', function( evt
)
479 var target
= evt
.data
.getTarget();
480 // If we aren't inside a tab, bail out.
481 if ( target
.hasClass( 'cke_dialog_tab' ) )
483 var id
= target
.$.id
;
484 this.selectPage( id
.substr( 0, id
.lastIndexOf( '_' ) ) );
485 if ( this._
.tabBarMode
)
487 this._
.tabBarMode
= false;
488 this._
.currentFocusIndex
= -1;
491 evt
.data
.preventDefault();
496 var buttonsHtml
= [],
497 buttons
= CKEDITOR
.dialog
._
.uiElementBuilders
.hbox
.build( this,
500 className
: 'cke_dialog_footer_buttons',
502 children
: definition
.buttons
503 }, buttonsHtml
).getChild();
504 this.parts
.footer
.setHtml( buttonsHtml
.join( '' ) );
506 for ( i
= 0 ; i
< buttons
.length
; i
++ )
507 this._
.buttons
[ buttons
[i
].id
] = buttons
[i
];
510 // Focusable interface. Use it via dialog.addFocusable.
511 function Focusable( dialog
, element
, index
)
513 this.element
= element
;
514 this.focusIndex
= index
;
515 // TODO: support tabIndex for focusables.
517 this.isFocusable = function()
519 return !element
.getAttribute( 'disabled' ) && element
.isVisible();
521 this.focus = function()
523 dialog
._
.currentFocusIndex
= this.focusIndex
;
524 this.element
.focus();
527 element
.on( 'keydown', function( e
)
529 if ( e
.data
.getKeystroke() in { 32:1, 13:1 } )
530 this.fire( 'click' );
532 element
.on( 'focus', function()
534 this.fire( 'mouseover' );
536 element
.on( 'blur', function()
538 this.fire( 'mouseout' );
542 CKEDITOR
.dialog
.prototype =
545 * Resizes the dialog.
546 * @param {Number} width The width of the dialog in pixels.
547 * @param {Number} height The height of the dialog in pixels.
550 * dialogObj.resize( 800, 640 );
554 return function( width
, height
)
556 if ( this._
.contentSize
&& this._
.contentSize
.width
== width
&& this._
.contentSize
.height
== height
)
559 CKEDITOR
.dialog
.fire( 'resize',
562 skin
: this._
.editor
.skinName
,
567 this._
.contentSize
= { width
: width
, height
: height
};
568 this._
.updateSize
= true;
573 * Gets the current size of the dialog in pixels.
574 * @returns {Object} An object with "width" and "height" properties.
576 * var width = dialogObj.getSize().width;
580 if ( !this._
.updateSize
)
582 var element
= this._
.element
.getFirst();
583 var size
= this._
.size
= { width
: element
.$.offsetWidth
|| 0, height
: element
.$.offsetHeight
|| 0};
585 // If either the offsetWidth or offsetHeight is 0, the element isn't visible.
586 this._
.updateSize
= !size
.width
|| !size
.height
;
592 * Moves the dialog to an (x, y) coordinate relative to the window.
594 * @param {Number} x The target x-coordinate.
595 * @param {Number} y The target y-coordinate.
597 * dialogObj.move( 10, 40 );
602 return function( x
, y
)
604 // The dialog may be fixed positioned or absolute positioned. Ask the
605 // browser what is the current situation first.
606 var element
= this._
.element
.getFirst();
607 if ( isFixed
=== undefined )
608 isFixed
= element
.getComputedStyle( 'position' ) == 'fixed';
610 if ( isFixed
&& this._
.position
&& this._
.position
.x
== x
&& this._
.position
.y
== y
)
613 // Save the current position.
614 this._
.position
= { x
: x
, y
: y
};
616 // If not fixed positioned, add scroll position to the coordinates.
619 var scrollPosition
= CKEDITOR
.document
.getWindow().getScrollPosition();
620 x
+= scrollPosition
.x
;
621 y
+= scrollPosition
.y
;
626 'left' : ( x
> 0 ? x
: 0 ) + 'px',
627 'top' : ( y
> 0 ? y
: 0 ) + 'px'
633 * Gets the dialog's position in the window.
634 * @returns {Object} An object with "x" and "y" properties.
636 * var dialogX = dialogObj.getPosition().x;
638 getPosition : function(){ return CKEDITOR
.tools
.extend( {}, this._
.position
); },
641 * Shows the dialog box.
647 var editor
= this._
.editor
;
648 if ( editor
.mode
== 'wysiwyg' && CKEDITOR
.env
.ie
)
650 var selection
= editor
.getSelection();
651 selection
&& selection
.lock();
654 // Insert the dialog's element to the root document.
655 var element
= this._
.element
;
656 var definition
= this.definition
;
657 if ( !( element
.getParent() && element
.getParent().equals( CKEDITOR
.document
.getBody() ) ) )
658 element
.appendTo( CKEDITOR
.document
.getBody() );
662 // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8.
663 if ( CKEDITOR
.env
.gecko
&& CKEDITOR
.env
.version
< 10900 )
665 var dialogElement
= this.parts
.dialog
;
666 dialogElement
.setStyle( 'position', 'absolute' );
667 setTimeout( function()
669 dialogElement
.setStyle( 'position', 'fixed' );
674 // First, set the dialog to an appropriate size.
675 this.resize( definition
.minWidth
, definition
.minHeight
);
677 // Select the first tab by default.
678 this.selectPage( this.definition
.contents
[0].id
);
680 // Reset all inputs back to their default value.
684 if ( CKEDITOR
.dialog
._
.currentZIndex
=== null )
685 CKEDITOR
.dialog
._
.currentZIndex
= this._
.editor
.config
.baseFloatZIndex
;
686 this._
.element
.getFirst().setStyle( 'z-index', CKEDITOR
.dialog
._
.currentZIndex
+= 10 );
688 // Maintain the dialog ordering and dialog cover.
689 // Also register key handlers if first dialog.
690 if ( CKEDITOR
.dialog
._
.currentTop
=== null )
692 CKEDITOR
.dialog
._
.currentTop
= this;
693 this._
.parentDialog
= null;
694 addCover( this._
.editor
);
696 element
.on( 'keydown', accessKeyDownHandler
);
697 element
.on( CKEDITOR
.env
.opera
? 'keypress' : 'keyup', accessKeyUpHandler
);
699 // Prevent some keys from bubbling up. (#4269)
700 for ( var event
in { keyup
:1, keydown
:1, keypress
:1 } )
701 element
.on( event
, preventKeyBubbling
);
705 this._
.parentDialog
= CKEDITOR
.dialog
._
.currentTop
;
706 var parentElement
= this._
.parentDialog
.getElement().getFirst();
707 parentElement
.$.style
.zIndex
-= Math
.floor( this._
.editor
.config
.baseFloatZIndex
/ 2 );
708 CKEDITOR
.dialog
._
.currentTop
= this;
711 // Register the Esc hotkeys.
712 registerAccessKey( this, this, '\x1b', null, function()
714 this.getButton( 'cancel' ) && this.getButton( 'cancel' ).click();
717 // Reset the hasFocus state.
718 this._
.hasFocus
= false;
720 // Rearrange the dialog to the middle of the window.
721 CKEDITOR
.tools
.setTimeout( function()
723 var viewSize
= CKEDITOR
.document
.getWindow().getViewPaneSize();
724 var dialogSize
= this.getSize();
726 // We're using definition size for initial position because of
727 // offten corrupted data in offsetWidth at this point. (#4084)
728 this.move( ( viewSize
.width
- definition
.minWidth
) / 2, ( viewSize
.height
- dialogSize
.height
) / 2 );
730 this.parts
.dialog
.setStyle( 'visibility', '' );
732 // Execute onLoad for the first show.
733 this.fireOnce( 'load', {} );
734 this.fire( 'show', {} );
735 this._
.editor
.fire( 'dialogShow', this );
737 // Save the initial values of the dialog.
738 this.foreach( function( contentObj
) { contentObj
.setInitValue
&& contentObj
.setInitValue(); } );
745 * Executes a function for each UI element.
746 * @param {Function} fn Function to execute for each UI element.
747 * @returns {CKEDITOR.dialog} The current dialog object.
749 foreach : function( fn
)
751 for ( var i
in this._
.contents
)
753 for ( var j
in this._
.contents
[i
] )
754 fn( this._
.contents
[i
][j
]);
760 * Resets all input values in the dialog.
763 * @returns {CKEDITOR.dialog} The current dialog object.
767 var fn = function( widget
){ if ( widget
.reset
) widget
.reset(); };
768 return function(){ this.foreach( fn
); return this; };
771 setupContent : function()
773 var args
= arguments
;
774 this.foreach( function( widget
)
777 widget
.setup
.apply( widget
, args
);
781 commitContent : function()
783 var args
= arguments
;
784 this.foreach( function( widget
)
787 widget
.commit
.apply( widget
, args
);
792 * Hides the dialog box.
798 this.fire( 'hide', {} );
799 this._
.editor
.fire( 'dialogHide', this );
801 // Remove the dialog's element from the root document.
802 var element
= this._
.element
;
803 if ( !element
.getParent() )
807 this.parts
.dialog
.setStyle( 'visibility', 'hidden' );
809 // Unregister all access keys associated with this dialog.
810 unregisterAccessKey( this );
812 // Maintain dialog ordering and remove cover if needed.
813 if ( !this._
.parentDialog
)
817 var parentElement
= this._
.parentDialog
.getElement().getFirst();
818 parentElement
.setStyle( 'z-index', parseInt( parentElement
.$.style
.zIndex
, 10 ) + Math
.floor( this._
.editor
.config
.baseFloatZIndex
/ 2 ) );
820 CKEDITOR
.dialog
._
.currentTop
= this._
.parentDialog
;
822 // Deduct or clear the z-index.
823 if ( !this._
.parentDialog
)
825 CKEDITOR
.dialog
._
.currentZIndex
= null;
827 // Remove access key handlers.
828 element
.removeListener( 'keydown', accessKeyDownHandler
);
829 element
.removeListener( CKEDITOR
.env
.opera
? 'keypress' : 'keyup', accessKeyUpHandler
);
831 // Remove bubbling-prevention handler. (#4269)
832 for ( var event
in { keyup
:1, keydown
:1, keypress
:1 } )
833 element
.removeListener( event
, preventKeyBubbling
);
835 var editor
= this._
.editor
;
838 if ( editor
.mode
== 'wysiwyg' && CKEDITOR
.env
.ie
)
840 var selection
= editor
.getSelection();
841 selection
&& selection
.unlock( true );
845 CKEDITOR
.dialog
._
.currentZIndex
-= 10;
848 // Reset the initial values of the dialog.
849 this.foreach( function( contentObj
) { contentObj
.resetInitValue
&& contentObj
.resetInitValue(); } );
853 * Adds a tabbed page into the dialog.
854 * @param {Object} contents Content definition.
857 addPage : function( contents
)
860 titleHtml
= contents
.label
? ' title="' + CKEDITOR
.tools
.htmlEncode( contents
.label
) + '"' : '',
861 elements
= contents
.elements
,
862 vbox
= CKEDITOR
.dialog
._
.uiElementBuilders
.vbox
.build( this,
865 className
: 'cke_dialog_page_contents',
866 children
: contents
.elements
,
867 expand
: !!contents
.expand
,
868 padding
: contents
.padding
,
869 style
: contents
.style
|| 'width: 100%; height: 100%;'
872 // Create the HTML for the tab and the content block.
873 var page
= CKEDITOR
.dom
.element
.createFromHtml( pageHtml
.join( '' ) );
874 page
.setAttribute( 'role', 'tabpanel' );
876 var env
= CKEDITOR
.env
;
877 var tabId
= contents
.id
+ '_' + CKEDITOR
.tools
.getNextNumber(),
878 tab
= CKEDITOR
.dom
.element
.createFromHtml( [
879 '<a class="cke_dialog_tab"',
880 ( this._
.pageCount
> 0 ? ' cke_last' : 'cke_first' ),
882 ( !!contents
.hidden
? ' style="display:none"' : '' ),
884 env
.gecko
&& env
.version
>= 10900 && !env
.hc
? '' : ' href="javascript:void(0)"',
892 page
.setAttribute( 'aria-labelledby', tabId
);
894 // Take records for the tabs and elements created.
895 this._
.tabs
[ contents
.id
] = [ tab
, page
];
896 this._
.tabIdList
.push( contents
.id
);
897 !contents
.hidden
&& this._
.pageCount
++;
898 this._
.lastTab
= tab
;
901 var contentMap
= this._
.contents
[ contents
.id
] = {},
903 children
= vbox
.getChild();
905 while ( ( cursor
= children
.shift() ) )
907 contentMap
[ cursor
.id
] = cursor
;
908 if ( typeof( cursor
.getChild
) == 'function' )
909 children
.push
.apply( children
, cursor
.getChild() );
912 // Attach the DOM nodes.
914 page
.setAttribute( 'name', contents
.id
);
915 page
.appendTo( this.parts
.contents
);
918 this.parts
.tabs
.append( tab
);
920 // Add access key handlers if access key is defined.
921 if ( contents
.accessKey
)
923 registerAccessKey( this, this, 'CTRL+' + contents
.accessKey
,
924 tabAccessKeyDown
, tabAccessKeyUp
);
925 this._
.accessKeyMap
[ 'CTRL+' + contents
.accessKey
] = contents
.id
;
930 * Activates a tab page in the dialog by its id.
931 * @param {String} id The id of the dialog tab to be activated.
933 * dialogObj.selectPage( 'tab_1' );
935 selectPage : function( id
)
937 // Hide the non-selected tabs and pages.
938 for ( var i
in this._
.tabs
)
940 var tab
= this._
.tabs
[i
][0],
941 page
= this._
.tabs
[i
][1];
944 tab
.removeClass( 'cke_dialog_tab_selected' );
947 page
.setAttribute( 'aria-hidden', i
!= id
);
950 var selected
= this._
.tabs
[id
];
951 selected
[0].addClass( 'cke_dialog_tab_selected' );
953 this._
.currentTabId
= id
;
954 this._
.currentTabIndex
= CKEDITOR
.tools
.indexOf( this._
.tabIdList
, id
);
957 // Dialog state-specific style updates.
958 updateStyle : function()
960 // If only a single page shown, a different style is used in the central pane.
961 this.parts
.dialog
[ ( this._
.pageCount
=== 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' );
965 * Hides a page's tab away from the dialog.
966 * @param {String} id The page's Id.
968 * dialog.hidePage( 'tab_3' );
970 hidePage : function( id
)
972 var tab
= this._
.tabs
[id
] && this._
.tabs
[id
][0];
973 if ( !tab
|| this._
.pageCount
== 1 )
975 // Switch to other tab first when we're hiding the active tab.
976 else if ( id
== this._
.currentTabId
)
977 this.selectPage( getPreviousVisibleTab
.call( this ) );
985 * Unhides a page's tab.
986 * @param {String} id The page's Id.
988 * dialog.showPage( 'tab_2' );
990 showPage : function( id
)
992 var tab
= this._
.tabs
[id
] && this._
.tabs
[id
][0];
1001 * Gets the root DOM element of the dialog.
1002 * @returns {CKEDITOR.dom.element} The <span> element containing this dialog.
1004 * var dialogElement = dialogObj.getElement().getFirst();
1005 * dialogElement.setStyle( 'padding', '5px' );
1007 getElement : function()
1009 return this._
.element
;
1013 * Gets the name of the dialog.
1014 * @returns {String} The name of this dialog.
1016 * var dialogName = dialogObj.getName();
1018 getName : function()
1024 * Gets a dialog UI element object from a dialog page.
1025 * @param {String} pageId id of dialog page.
1026 * @param {String} elementId id of UI element.
1028 * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element.
1030 getContentElement : function( pageId
, elementId
)
1032 var page
= this._
.contents
[ pageId
];
1033 return page
&& page
[ elementId
];
1037 * Gets the value of a dialog UI element.
1038 * @param {String} pageId id of dialog page.
1039 * @param {String} elementId id of UI element.
1041 * @returns {Object} The value of the UI element.
1043 getValueOf : function( pageId
, elementId
)
1045 return this.getContentElement( pageId
, elementId
).getValue();
1049 * Sets the value of a dialog UI element.
1050 * @param {String} pageId id of the dialog page.
1051 * @param {String} elementId id of the UI element.
1052 * @param {Object} value The new value of the UI element.
1055 setValueOf : function( pageId
, elementId
, value
)
1057 return this.getContentElement( pageId
, elementId
).setValue( value
);
1061 * Gets the UI element of a button in the dialog's button row.
1062 * @param {String} id The id of the button.
1064 * @returns {CKEDITOR.ui.dialog.button} The button object.
1066 getButton : function( id
)
1068 return this._
.buttons
[ id
];
1072 * Simulates a click to a dialog button in the dialog's button row.
1073 * @param {String} id The id of the button.
1075 * @returns The return value of the dialog's "click" event.
1077 click : function( id
)
1079 return this._
.buttons
[ id
].click();
1083 * Disables a dialog button.
1084 * @param {String} id The id of the button.
1087 disableButton : function( id
)
1089 return this._
.buttons
[ id
].disable();
1093 * Enables a dialog button.
1094 * @param {String} id The id of the button.
1097 enableButton : function( id
)
1099 return this._
.buttons
[ id
].enable();
1103 * Gets the number of pages in the dialog.
1104 * @returns {Number} Page count.
1106 getPageCount : function()
1108 return this._
.pageCount
;
1112 * Gets the editor instance which opened this dialog.
1113 * @returns {CKEDITOR.editor} Parent editor instances.
1115 getParentEditor : function()
1117 return this._
.editor
;
1121 * Gets the element that was selected when opening the dialog, if any.
1122 * @returns {CKEDITOR.dom.element} The element that was selected, or null.
1124 getSelectedElement : function()
1126 return this.getParentEditor().getSelection().getSelectedElement();
1130 * Adds element to dialog's focusable list.
1132 * @param {CKEDITOR.dom.element} element
1133 * @param {Number} [index]
1135 addFocusable: function( element
, index
) {
1136 if ( typeof index
== 'undefined' )
1138 index
= this._
.focusList
.length
;
1139 this._
.focusList
.push( new Focusable( this, element
, index
) );
1143 this._
.focusList
.splice( index
, 0, new Focusable( this, element
, index
) );
1144 for ( var i
= index
+ 1 ; i
< this._
.focusList
.length
; i
++ )
1145 this._
.focusList
[ i
].focusIndex
++;
1150 CKEDITOR
.tools
.extend( CKEDITOR
.dialog
,
1152 * @lends CKEDITOR.dialog
1156 * Registers a dialog.
1157 * @param {String} name The dialog's name.
1158 * @param {Function|String} dialogDefinition
1159 * A function returning the dialog's definition, or the URL to the .js file holding the function.
1160 * The function should accept an argument "editor" which is the current editor instance, and
1161 * return an object conforming to {@link CKEDITOR.dialog.dialogDefinition}.
1163 * @see CKEDITOR.dialog.dialogDefinition
1165 add : function( name
, dialogDefinition
)
1167 // Avoid path registration from multiple instances override definition.
1168 if ( !this._
.dialogDefinitions
[name
]
1169 || typeof dialogDefinition
== 'function' )
1170 this._
.dialogDefinitions
[name
] = dialogDefinition
;
1173 exists : function( name
)
1175 return !!this._
.dialogDefinitions
[ name
];
1178 getCurrent : function()
1180 return CKEDITOR
.dialog
._
.currentTop
;
1184 * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds.
1190 okButton
: (function()
1192 var retval = function( editor
, override
)
1194 override
= override
|| {};
1195 return CKEDITOR
.tools
.extend( {
1198 label
: editor
.lang
.common
.ok
,
1199 'class' : 'cke_dialog_ui_button_ok',
1200 onClick : function( evt
)
1202 var dialog
= evt
.data
.dialog
;
1203 if ( dialog
.fire( 'ok', { hide
: true } ).hide
!== false )
1206 }, override
, true );
1208 retval
.type
= 'button';
1209 retval
.override = function( override
)
1211 return CKEDITOR
.tools
.extend( function( editor
){ return retval( editor
, override
); },
1212 { type
: 'button' }, true );
1218 * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed.
1224 cancelButton
: (function()
1226 var retval = function( editor
, override
)
1228 override
= override
|| {};
1229 return CKEDITOR
.tools
.extend( {
1232 label
: editor
.lang
.common
.cancel
,
1233 'class' : 'cke_dialog_ui_button_cancel',
1234 onClick : function( evt
)
1236 var dialog
= evt
.data
.dialog
;
1237 if ( dialog
.fire( 'cancel', { hide
: true } ).hide
!== false )
1240 }, override
, true );
1242 retval
.type
= 'button';
1243 retval
.override = function( override
)
1245 return CKEDITOR
.tools
.extend( function( editor
){ return retval( editor
, override
); },
1246 { type
: 'button' }, true );
1252 * Registers a dialog UI element.
1253 * @param {String} typeName The name of the UI element.
1254 * @param {Function} builder The function to build the UI element.
1257 addUIElement : function( typeName
, builder
)
1259 this._
.uiElementBuilders
[ typeName
] = builder
;
1265 uiElementBuilders
: {},
1267 dialogDefinitions
: {},
1271 currentZIndex
: null
1274 // "Inherit" (copy actually) from CKEDITOR.event.
1275 CKEDITOR
.event
.implementOn( CKEDITOR
.dialog
);
1276 CKEDITOR
.event
.implementOn( CKEDITOR
.dialog
.prototype, true );
1278 var defaultDialogDefinition
=
1280 resizable
: CKEDITOR
.DIALOG_RESIZE_BOTH
,
1283 buttons
: [ CKEDITOR
.dialog
.okButton
, CKEDITOR
.dialog
.cancelButton
]
1286 // The buttons in MacOS Apps are in reverse order #4750
1287 CKEDITOR
.env
.mac
&& defaultDialogDefinition
.buttons
.reverse();
1289 // Tool function used to return an item from an array based on its id
1291 var getById = function( array
, id
, recurse
)
1293 for ( var i
= 0, item
; ( item
= array
[ i
] ) ; i
++ )
1295 if ( item
.id
== id
)
1297 if ( recurse
&& item
[ recurse
] )
1299 var retval
= getById( item
[ recurse
], id
, recurse
) ;
1307 // Tool function used to add an item into an array.
1308 var addById = function( array
, newItem
, nextSiblingId
, recurse
, nullIfNotFound
)
1310 if ( nextSiblingId
)
1312 for ( var i
= 0, item
; ( item
= array
[ i
] ) ; i
++ )
1314 if ( item
.id
== nextSiblingId
)
1316 array
.splice( i
, 0, newItem
);
1320 if ( recurse
&& item
[ recurse
] )
1322 var retval
= addById( item
[ recurse
], newItem
, nextSiblingId
, recurse
, true );
1328 if ( nullIfNotFound
)
1332 array
.push( newItem
);
1336 // Tool function used to remove an item from an array based on its id.
1337 var removeById = function( array
, id
, recurse
)
1339 for ( var i
= 0, item
; ( item
= array
[ i
] ) ; i
++ )
1341 if ( item
.id
== id
)
1342 return array
.splice( i
, 1 );
1343 if ( recurse
&& item
[ recurse
] )
1345 var retval
= removeById( item
[ recurse
], id
, recurse
);
1354 * This class is not really part of the API. It is the "definition" property value
1355 * passed to "dialogDefinition" event handlers.
1357 * @name CKEDITOR.dialog.dialogDefinitionObject
1358 * @extends CKEDITOR.dialog.dialogDefinition
1360 * CKEDITOR.on( 'dialogDefinition', function( evt )
1362 * var definition = evt.data.definition;
1363 * var content = definition.getContents( 'page1' );
1367 var definitionObject = function( dialog
, dialogDefinition
)
1369 // TODO : Check if needed.
1370 this.dialog
= dialog
;
1372 // Transform the contents entries in contentObjects.
1373 var contents
= dialogDefinition
.contents
;
1374 for ( var i
= 0, content
; ( content
= contents
[i
] ) ; i
++ )
1375 contents
[ i
] = new contentObject( dialog
, content
);
1377 CKEDITOR
.tools
.extend( this, dialogDefinition
);
1380 definitionObject
.prototype =
1381 /** @lends CKEDITOR.dialog.dialogDefinitionObject.prototype */
1384 * Gets a content definition.
1385 * @param {String} id The id of the content definition.
1386 * @returns {CKEDITOR.dialog.contentDefinition} The content definition
1389 getContents : function( id
)
1391 return getById( this.contents
, id
);
1395 * Gets a button definition.
1396 * @param {String} id The id of the button definition.
1397 * @returns {CKEDITOR.dialog.buttonDefinition} The button definition
1400 getButton : function( id
)
1402 return getById( this.buttons
, id
);
1406 * Adds a content definition object under this dialog definition.
1407 * @param {CKEDITOR.dialog.contentDefinition} contentDefinition The
1408 * content definition.
1409 * @param {String} [nextSiblingId] The id of an existing content
1410 * definition which the new content definition will be inserted
1411 * before. Omit if the new content definition is to be inserted as
1413 * @returns {CKEDITOR.dialog.contentDefinition} The inserted content
1416 addContents : function( contentDefinition
, nextSiblingId
)
1418 return addById( this.contents
, contentDefinition
, nextSiblingId
);
1422 * Adds a button definition object under this dialog definition.
1423 * @param {CKEDITOR.dialog.buttonDefinition} buttonDefinition The
1424 * button definition.
1425 * @param {String} [nextSiblingId] The id of an existing button
1426 * definition which the new button definition will be inserted
1427 * before. Omit if the new button definition is to be inserted as
1429 * @returns {CKEDITOR.dialog.buttonDefinition} The inserted button
1432 addButton : function( buttonDefinition
, nextSiblingId
)
1434 return addById( this.buttons
, buttonDefinition
, nextSiblingId
);
1438 * Removes a content definition from this dialog definition.
1439 * @param {String} id The id of the content definition to be removed.
1440 * @returns {CKEDITOR.dialog.contentDefinition} The removed content
1443 removeContents : function( id
)
1445 removeById( this.contents
, id
);
1449 * Removes a button definition from the dialog definition.
1450 * @param {String} id The id of the button definition to be removed.
1451 * @returns {CKEDITOR.dialog.buttonDefinition} The removed button
1454 removeButton : function( id
)
1456 removeById( this.buttons
, id
);
1461 * This class is not really part of the API. It is the template of the
1462 * objects representing content pages inside the
1463 * CKEDITOR.dialog.dialogDefinitionObject.
1465 * @name CKEDITOR.dialog.contentDefinitionObject
1467 * CKEDITOR.on( 'dialogDefinition', function( evt )
1469 * var definition = evt.data.definition;
1470 * var content = definition.getContents( 'page1' );
1471 * content.remove( 'textInput1' );
1475 function contentObject( dialog
, contentDefinition
)
1482 CKEDITOR
.tools
.extend( this, contentDefinition
);
1485 contentObject
.prototype =
1486 /** @lends CKEDITOR.dialog.contentDefinitionObject.prototype */
1489 * Gets a UI element definition under the content definition.
1490 * @param {String} id The id of the UI element definition.
1491 * @returns {CKEDITOR.dialog.uiElementDefinition}
1493 get : function( id
)
1495 return getById( this.elements
, id
, 'children' );
1499 * Adds a UI element definition to the content definition.
1500 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition The
1501 * UI elemnet definition to be added.
1502 * @param {String} nextSiblingId The id of an existing UI element
1503 * definition which the new UI element definition will be inserted
1504 * before. Omit if the new button definition is to be inserted as
1506 * @returns {CKEDITOR.dialog.uiElementDefinition} The element
1507 * definition inserted.
1509 add : function( elementDefinition
, nextSiblingId
)
1511 return addById( this.elements
, elementDefinition
, nextSiblingId
, 'children' );
1515 * Removes a UI element definition from the content definition.
1516 * @param {String} id The id of the UI element definition to be
1518 * @returns {CKEDITOR.dialog.uiElementDefinition} The element
1519 * definition removed.
1522 remove : function( id
)
1524 removeById( this.elements
, id
, 'children' );
1528 function initDragAndDrop( dialog
)
1530 var lastCoords
= null,
1531 abstractDialogCoords
= null,
1532 element
= dialog
.getElement().getFirst(),
1533 editor
= dialog
.getParentEditor(),
1534 magnetDistance
= editor
.config
.dialog_magnetDistance
,
1535 margins
= editor
.skin
.margins
|| [ 0, 0, 0, 0 ];
1537 if ( typeof magnetDistance
== 'undefined' )
1538 magnetDistance
= 20;
1540 function mouseMoveHandler( evt
)
1542 var dialogSize
= dialog
.getSize(),
1543 viewPaneSize
= CKEDITOR
.document
.getWindow().getViewPaneSize(),
1544 x
= evt
.data
.$.screenX
,
1545 y
= evt
.data
.$.screenY
,
1546 dx
= x
- lastCoords
.x
,
1547 dy
= y
- lastCoords
.y
,
1550 lastCoords
= { x
: x
, y
: y
};
1551 abstractDialogCoords
.x
+= dx
;
1552 abstractDialogCoords
.y
+= dy
;
1554 if ( abstractDialogCoords
.x
+ margins
[3] < magnetDistance
)
1555 realX
= - margins
[3];
1556 else if ( abstractDialogCoords
.x
- margins
[1] > viewPaneSize
.width
- dialogSize
.width
- magnetDistance
)
1557 realX
= viewPaneSize
.width
- dialogSize
.width
+ margins
[1];
1559 realX
= abstractDialogCoords
.x
;
1561 if ( abstractDialogCoords
.y
+ margins
[0] < magnetDistance
)
1562 realY
= - margins
[0];
1563 else if ( abstractDialogCoords
.y
- margins
[2] > viewPaneSize
.height
- dialogSize
.height
- magnetDistance
)
1564 realY
= viewPaneSize
.height
- dialogSize
.height
+ margins
[2];
1566 realY
= abstractDialogCoords
.y
;
1568 dialog
.move( realX
, realY
);
1570 evt
.data
.preventDefault();
1573 function mouseUpHandler( evt
)
1575 CKEDITOR
.document
.removeListener( 'mousemove', mouseMoveHandler
);
1576 CKEDITOR
.document
.removeListener( 'mouseup', mouseUpHandler
);
1578 if ( CKEDITOR
.env
.ie6Compat
)
1580 var coverDoc
= coverElement
.getChild( 0 ).getFrameDocument();
1581 coverDoc
.removeListener( 'mousemove', mouseMoveHandler
);
1582 coverDoc
.removeListener( 'mouseup', mouseUpHandler
);
1586 dialog
.parts
.title
.on( 'mousedown', function( evt
)
1588 dialog
._
.updateSize
= true;
1590 lastCoords
= { x
: evt
.data
.$.screenX
, y
: evt
.data
.$.screenY
};
1592 CKEDITOR
.document
.on( 'mousemove', mouseMoveHandler
);
1593 CKEDITOR
.document
.on( 'mouseup', mouseUpHandler
);
1594 abstractDialogCoords
= dialog
.getPosition();
1596 if ( CKEDITOR
.env
.ie6Compat
)
1598 var coverDoc
= coverElement
.getChild( 0 ).getFrameDocument();
1599 coverDoc
.on( 'mousemove', mouseMoveHandler
);
1600 coverDoc
.on( 'mouseup', mouseUpHandler
);
1603 evt
.data
.preventDefault();
1607 function initResizeHandles( dialog
)
1609 var definition
= dialog
.definition
,
1610 minWidth
= definition
.minWidth
|| 0,
1611 minHeight
= definition
.minHeight
|| 0,
1612 resizable
= definition
.resizable
,
1613 margins
= dialog
.getParentEditor().skin
.margins
|| [ 0, 0, 0, 0 ];
1615 function topSizer( coords
, dy
)
1620 function rightSizer( coords
, dx
)
1625 function bottomSizer( coords
, dy
)
1630 function leftSizer( coords
, dx
)
1635 var lastCoords
= null,
1636 abstractDialogCoords
= null,
1637 magnetDistance
= dialog
._
.editor
.config
.magnetDistance
,
1638 parts
= [ 'tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br' ];
1640 function mouseDownHandler( evt
)
1642 var partName
= evt
.listenerData
.part
, size
= dialog
.getSize();
1643 abstractDialogCoords
= dialog
.getPosition();
1644 CKEDITOR
.tools
.extend( abstractDialogCoords
,
1646 x2
: abstractDialogCoords
.x
+ size
.width
,
1647 y2
: abstractDialogCoords
.y
+ size
.height
1649 lastCoords
= { x
: evt
.data
.$.screenX
, y
: evt
.data
.$.screenY
};
1651 CKEDITOR
.document
.on( 'mousemove', mouseMoveHandler
, dialog
, { part
: partName
} );
1652 CKEDITOR
.document
.on( 'mouseup', mouseUpHandler
, dialog
, { part
: partName
} );
1654 if ( CKEDITOR
.env
.ie6Compat
)
1656 var coverDoc
= coverElement
.getChild( 0 ).getFrameDocument();
1657 coverDoc
.on( 'mousemove', mouseMoveHandler
, dialog
, { part
: partName
} );
1658 coverDoc
.on( 'mouseup', mouseUpHandler
, dialog
, { part
: partName
} );
1661 evt
.data
.preventDefault();
1664 function mouseMoveHandler( evt
)
1666 var x
= evt
.data
.$.screenX
,
1667 y
= evt
.data
.$.screenY
,
1668 dx
= x
- lastCoords
.x
,
1669 dy
= y
- lastCoords
.y
,
1670 viewPaneSize
= CKEDITOR
.document
.getWindow().getViewPaneSize(),
1671 partName
= evt
.listenerData
.part
;
1673 if ( partName
.search( 't' ) != -1 )
1674 topSizer( abstractDialogCoords
, dy
);
1675 if ( partName
.search( 'l' ) != -1 )
1676 leftSizer( abstractDialogCoords
, dx
);
1677 if ( partName
.search( 'b' ) != -1 )
1678 bottomSizer( abstractDialogCoords
, dy
);
1679 if ( partName
.search( 'r' ) != -1 )
1680 rightSizer( abstractDialogCoords
, dx
);
1682 lastCoords
= { x
: x
, y
: y
};
1684 var realX
, realY
, realX2
, realY2
;
1686 if ( abstractDialogCoords
.x
+ margins
[3] < magnetDistance
)
1687 realX
= - margins
[3];
1688 else if ( partName
.search( 'l' ) != -1 && abstractDialogCoords
.x2
- abstractDialogCoords
.x
< minWidth
+ magnetDistance
)
1689 realX
= abstractDialogCoords
.x2
- minWidth
;
1691 realX
= abstractDialogCoords
.x
;
1693 if ( abstractDialogCoords
.y
+ margins
[0] < magnetDistance
)
1694 realY
= - margins
[0];
1695 else if ( partName
.search( 't' ) != -1 && abstractDialogCoords
.y2
- abstractDialogCoords
.y
< minHeight
+ magnetDistance
)
1696 realY
= abstractDialogCoords
.y2
- minHeight
;
1698 realY
= abstractDialogCoords
.y
;
1700 if ( abstractDialogCoords
.x2
- margins
[1] > viewPaneSize
.width
- magnetDistance
)
1701 realX2
= viewPaneSize
.width
+ margins
[1] ;
1702 else if ( partName
.search( 'r' ) != -1 && abstractDialogCoords
.x2
- abstractDialogCoords
.x
< minWidth
+ magnetDistance
)
1703 realX2
= abstractDialogCoords
.x
+ minWidth
;
1705 realX2
= abstractDialogCoords
.x2
;
1707 if ( abstractDialogCoords
.y2
- margins
[2] > viewPaneSize
.height
- magnetDistance
)
1708 realY2
= viewPaneSize
.height
+ margins
[2] ;
1709 else if ( partName
.search( 'b' ) != -1 && abstractDialogCoords
.y2
- abstractDialogCoords
.y
< minHeight
+ magnetDistance
)
1710 realY2
= abstractDialogCoords
.y
+ minHeight
;
1712 realY2
= abstractDialogCoords
.y2
;
1714 dialog
.move( realX
, realY
);
1715 dialog
.resize( realX2
- realX
, realY2
- realY
);
1717 evt
.data
.preventDefault();
1720 function mouseUpHandler( evt
)
1722 CKEDITOR
.document
.removeListener( 'mouseup', mouseUpHandler
);
1723 CKEDITOR
.document
.removeListener( 'mousemove', mouseMoveHandler
);
1725 if ( CKEDITOR
.env
.ie6Compat
)
1727 var coverDoc
= coverElement
.getChild( 0 ).getFrameDocument();
1728 coverDoc
.removeListener( 'mouseup', mouseUpHandler
);
1729 coverDoc
.removeListener( 'mousemove', mouseMoveHandler
);
1733 // TODO : Simplify the resize logic, having just a single resize grip <div>.
1734 // var widthTest = /[lr]/,
1735 // heightTest = /[tb]/;
1736 // for ( var i = 0 ; i < parts.length ; i++ )
1738 // var element = dialog.parts[ parts[i] + '_resize' ];
1739 // if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ||
1740 // resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT && widthTest.test( parts[i] ) ||
1741 // resizable == CKEDITOR.DIALOG_RESIZE_WIDTH && heightTest.test( parts[i] ) )
1746 // element.on( 'mousedown', mouseDownHandler, dialog, { part : parts[i] } );
1753 var addCover = function( editor
)
1755 var win
= CKEDITOR
.document
.getWindow();
1757 if ( !coverElement
)
1759 var backgroundColorStyle
= editor
.config
.dialog_backgroundCoverColor
|| 'white';
1762 '<div style="position: ', ( CKEDITOR
.env
.ie6Compat
? 'absolute' : 'fixed' ),
1763 '; z-index: ', editor
.config
.baseFloatZIndex
,
1764 '; top: 0px; left: 0px; ',
1765 ( !CKEDITOR
.env
.ie6Compat
? 'background-color: ' + backgroundColorStyle
: '' ),
1766 '" id="cke_dialog_background_cover">'
1770 if ( CKEDITOR
.env
.ie6Compat
)
1772 // Support for custom document.domain in IE.
1773 var isCustomDomain
= CKEDITOR
.env
.isCustomDomain(),
1774 iframeHtml
= '<html><body style=\\\'background-color:' + backgroundColorStyle
+ ';\\\'></body></html>';
1778 ' hidefocus="true"' +
1779 ' frameborder="0"' +
1780 ' id="cke_dialog_background_iframe"' +
1781 ' src="javascript:' );
1783 html
.push( 'void((function(){' +
1784 'document.open();' +
1785 ( isCustomDomain
? 'document.domain=\'' + document
.domain
+ '\';' : '' ) +
1786 'document.write( \'' + iframeHtml
+ '\' );' +
1787 'document.close();' +
1793 'position:absolute;' +
1798 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)">' +
1802 html
.push( '</div>' );
1804 coverElement
= CKEDITOR
.dom
.element
.createFromHtml( html
.join( '' ) );
1807 var element
= coverElement
;
1809 var resizeFunc = function()
1811 var size
= win
.getViewPaneSize();
1814 width
: size
.width
+ 'px',
1815 height
: size
.height
+ 'px'
1819 var scrollFunc = function()
1821 var pos
= win
.getScrollPosition(),
1822 cursor
= CKEDITOR
.dialog
._
.currentTop
;
1825 left
: pos
.x
+ 'px',
1831 var dialogPos
= cursor
.getPosition();
1832 cursor
.move( dialogPos
.x
, dialogPos
.y
);
1833 } while ( ( cursor
= cursor
._
.parentDialog
) );
1836 resizeCover
= resizeFunc
;
1837 win
.on( 'resize', resizeFunc
);
1839 if ( CKEDITOR
.env
.ie6Compat
)
1841 // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll.
1842 // So we need to invent a really funny way to make it work.
1843 var myScrollHandler = function()
1846 arguments
.callee
.prevScrollHandler
.apply( this, arguments
);
1848 win
.$.setTimeout( function()
1850 myScrollHandler
.prevScrollHandler
= window
.onscroll
|| function(){};
1851 window
.onscroll
= myScrollHandler
;
1856 var opacity
= editor
.config
.dialog_backgroundCoverOpacity
;
1857 element
.setOpacity( typeof opacity
!= 'undefined' ? opacity
: 0.5 );
1859 element
.appendTo( CKEDITOR
.document
.getBody() );
1862 var removeCover = function()
1864 if ( !coverElement
)
1867 var win
= CKEDITOR
.document
.getWindow();
1868 coverElement
.remove();
1869 win
.removeListener( 'resize', resizeCover
);
1871 if ( CKEDITOR
.env
.ie6Compat
)
1873 win
.$.setTimeout( function()
1875 var prevScrollHandler
= window
.onscroll
&& window
.onscroll
.prevScrollHandler
;
1876 window
.onscroll
= prevScrollHandler
|| null;
1882 var accessKeyProcessors
= {};
1884 var accessKeyDownHandler = function( evt
)
1886 var ctrl
= evt
.data
.$.ctrlKey
|| evt
.data
.$.metaKey
,
1887 alt
= evt
.data
.$.altKey
,
1888 shift
= evt
.data
.$.shiftKey
,
1889 key
= String
.fromCharCode( evt
.data
.$.keyCode
),
1890 keyProcessor
= accessKeyProcessors
[( ctrl
? 'CTRL+' : '' ) + ( alt
? 'ALT+' : '') + ( shift
? 'SHIFT+' : '' ) + key
];
1892 if ( !keyProcessor
|| !keyProcessor
.length
)
1895 keyProcessor
= keyProcessor
[keyProcessor
.length
- 1];
1896 keyProcessor
.keydown
&& keyProcessor
.keydown
.call( keyProcessor
.uiElement
, keyProcessor
.dialog
, keyProcessor
.key
);
1897 evt
.data
.preventDefault();
1900 var accessKeyUpHandler = function( evt
)
1902 var ctrl
= evt
.data
.$.ctrlKey
|| evt
.data
.$.metaKey
,
1903 alt
= evt
.data
.$.altKey
,
1904 shift
= evt
.data
.$.shiftKey
,
1905 key
= String
.fromCharCode( evt
.data
.$.keyCode
),
1906 keyProcessor
= accessKeyProcessors
[( ctrl
? 'CTRL+' : '' ) + ( alt
? 'ALT+' : '') + ( shift
? 'SHIFT+' : '' ) + key
];
1908 if ( !keyProcessor
|| !keyProcessor
.length
)
1911 keyProcessor
= keyProcessor
[keyProcessor
.length
- 1];
1912 if ( keyProcessor
.keyup
)
1914 keyProcessor
.keyup
.call( keyProcessor
.uiElement
, keyProcessor
.dialog
, keyProcessor
.key
);
1915 evt
.data
.preventDefault();
1919 var registerAccessKey = function( uiElement
, dialog
, key
, downFunc
, upFunc
)
1921 var procList
= accessKeyProcessors
[key
] || ( accessKeyProcessors
[key
] = [] );
1923 uiElement
: uiElement
,
1926 keyup
: upFunc
|| uiElement
.accessKeyUp
,
1927 keydown
: downFunc
|| uiElement
.accessKeyDown
1931 var unregisterAccessKey = function( obj
)
1933 for ( var i
in accessKeyProcessors
)
1935 var list
= accessKeyProcessors
[i
];
1936 for ( var j
= list
.length
- 1 ; j
>= 0 ; j
-- )
1938 if ( list
[j
].dialog
== obj
|| list
[j
].uiElement
== obj
)
1939 list
.splice( j
, 1 );
1941 if ( list
.length
=== 0 )
1942 delete accessKeyProcessors
[i
];
1946 var tabAccessKeyUp = function( dialog
, key
)
1948 if ( dialog
._
.accessKeyMap
[key
] )
1949 dialog
.selectPage( dialog
._
.accessKeyMap
[key
] );
1952 var tabAccessKeyDown = function( dialog
, key
)
1957 var preventKeyBubblingKeys
= { 27 :1, 13 :1 };
1958 var preventKeyBubbling = function( e
)
1960 if ( e
.data
.getKeystroke() in preventKeyBubblingKeys
)
1961 e
.data
.stopPropagation();
1966 CKEDITOR
.ui
.dialog
=
1969 * The base class of all dialog UI elements.
1971 * @param {CKEDITOR.dialog} dialog Parent dialog object.
1972 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element
1973 * definition. Accepted fields:
1975 * <li><strong>id</strong> (Required) The id of the UI element. See {@link
1976 * CKEDITOR.dialog#getContentElement}</li>
1977 * <li><strong>type</strong> (Required) The type of the UI element. The
1978 * value to this field specifies which UI element class will be used to
1979 * generate the final widget.</li>
1980 * <li><strong>title</strong> (Optional) The popup tooltip for the UI
1982 * <li><strong>hidden</strong> (Optional) A flag that tells if the element
1983 * should be initially visible.</li>
1984 * <li><strong>className</strong> (Optional) Additional CSS class names
1985 * to add to the UI element. Separated by space.</li>
1986 * <li><strong>style</strong> (Optional) Additional CSS inline styles
1987 * to add to the UI element. A semicolon (;) is required after the last
1988 * style declaration.</li>
1989 * <li><strong>accessKey</strong> (Optional) The alphanumeric access key
1990 * for this element. Access keys are automatically prefixed by CTRL.</li>
1991 * <li><strong>on*</strong> (Optional) Any UI element definition field that
1992 * starts with <em>on</em> followed immediately by a capital letter and
1993 * probably more letters is an event handler. Event handlers may be further
1994 * divided into registered event handlers and DOM event handlers. Please
1995 * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and
1996 * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more
1999 * @param {Array} htmlList
2000 * List of HTML code to be added to the dialog's content area.
2001 * @param {Function|String} nodeNameArg
2002 * A function returning a string, or a simple string for the node name for
2003 * the root DOM node. Default is 'div'.
2004 * @param {Function|Object} stylesArg
2005 * A function returning an object, or a simple object for CSS styles applied
2006 * to the DOM node. Default is empty object.
2007 * @param {Function|Object} attributesArg
2008 * A fucntion returning an object, or a simple object for attributes applied
2009 * to the DOM node. Default is empty object.
2010 * @param {Function|String} contentsArg
2011 * A function returning a string, or a simple string for the HTML code inside
2012 * the root DOM node. Default is empty string.
2015 uiElement : function( dialog
, elementDefinition
, htmlList
, nodeNameArg
, stylesArg
, attributesArg
, contentsArg
)
2017 if ( arguments
.length
< 4 )
2020 var nodeName
= ( nodeNameArg
.call
? nodeNameArg( elementDefinition
) : nodeNameArg
) || 'div',
2021 html
= [ '<', nodeName
, ' ' ],
2022 styles
= ( stylesArg
&& stylesArg
.call
? stylesArg( elementDefinition
) : stylesArg
) || {},
2023 attributes
= ( attributesArg
&& attributesArg
.call
? attributesArg( elementDefinition
) : attributesArg
) || {},
2024 innerHTML
= ( contentsArg
&& contentsArg
.call
? contentsArg
.call( this, dialog
, elementDefinition
) : contentsArg
) || '',
2025 domId
= this.domId
= attributes
.id
|| CKEDITOR
.tools
.getNextNumber() + '_uiElement',
2026 id
= this.id
= elementDefinition
.id
,
2029 // Set the id, a unique id is required for getElement() to work.
2030 attributes
.id
= domId
;
2032 // Set the type and definition CSS class names.
2034 if ( elementDefinition
.type
)
2035 classes
[ 'cke_dialog_ui_' + elementDefinition
.type
] = 1;
2036 if ( elementDefinition
.className
)
2037 classes
[ elementDefinition
.className
] = 1;
2038 var attributeClasses
= ( attributes
['class'] && attributes
['class'].split
) ? attributes
['class'].split( ' ' ) : [];
2039 for ( i
= 0 ; i
< attributeClasses
.length
; i
++ )
2041 if ( attributeClasses
[i
] )
2042 classes
[ attributeClasses
[i
] ] = 1;
2044 var finalClasses
= [];
2045 for ( i
in classes
)
2046 finalClasses
.push( i
);
2047 attributes
['class'] = finalClasses
.join( ' ' );
2049 // Set the popup tooltop.
2050 if ( elementDefinition
.title
)
2051 attributes
.title
= elementDefinition
.title
;
2053 // Write the inline CSS styles.
2054 var styleStr
= ( elementDefinition
.style
|| '' ).split( ';' );
2056 styleStr
.push( i
+ ':' + styles
[i
] );
2057 if ( elementDefinition
.hidden
)
2058 styleStr
.push( 'display:none' );
2059 for ( i
= styleStr
.length
- 1 ; i
>= 0 ; i
-- )
2061 if ( styleStr
[i
] === '' )
2062 styleStr
.splice( i
, 1 );
2064 if ( styleStr
.length
> 0 )
2065 attributes
.style
= ( attributes
.style
? ( attributes
.style
+ '; ' ) : '' ) + styleStr
.join( '; ' );
2067 // Write the attributes.
2068 for ( i
in attributes
)
2069 html
.push( i
+ '="' + CKEDITOR
.tools
.htmlEncode( attributes
[i
] ) + '" ');
2071 // Write the content HTML.
2072 html
.push( '>', innerHTML
, '</', nodeName
, '>' );
2074 // Add contents to the parent HTML array.
2075 htmlList
.push( html
.join( '' ) );
2077 ( this._
|| ( this._
= {} ) ).dialog
= dialog
;
2079 // Override isChanged if it is defined in element definition.
2080 if ( typeof( elementDefinition
.isChanged
) == 'boolean' )
2081 this.isChanged = function(){ return elementDefinition
.isChanged
; };
2082 if ( typeof( elementDefinition
.isChanged
) == 'function' )
2083 this.isChanged
= elementDefinition
.isChanged
;
2086 CKEDITOR
.event
.implementOn( this );
2088 this.registerEvents( elementDefinition
);
2089 if ( this.accessKeyUp
&& this.accessKeyDown
&& elementDefinition
.accessKey
)
2090 registerAccessKey( this, dialog
, 'CTRL+' + elementDefinition
.accessKey
);
2093 dialog
.on( 'load', function()
2095 if ( me
.getInputElement() )
2097 me
.getInputElement().on( 'focus', function()
2099 dialog
._
.tabBarMode
= false;
2100 dialog
._
.hasFocus
= true;
2106 // Register the object as a tab focus if it can be included.
2107 if ( this.keyboardFocusable
)
2109 this.tabIndex
= elementDefinition
.tabIndex
|| 0;
2111 this.focusIndex
= dialog
._
.focusList
.push( this ) - 1;
2112 this.on( 'focus', function()
2114 dialog
._
.currentFocusIndex
= me
.focusIndex
;
2118 // Completes this object with everything we have in the
2120 CKEDITOR
.tools
.extend( this, elementDefinition
);
2124 * Horizontal layout box for dialog UI elements, auto-expends to available width of container.
2126 * @extends CKEDITOR.ui.dialog.uiElement
2127 * @param {CKEDITOR.dialog} dialog
2128 * Parent dialog object.
2129 * @param {Array} childObjList
2130 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
2132 * @param {Array} childHtmlList
2133 * Array of HTML code that correspond to the HTML output of all the
2134 * objects in childObjList.
2135 * @param {Array} htmlList
2136 * Array of HTML code that this element will output to.
2137 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
2138 * The element definition. Accepted fields:
2140 * <li><strong>widths</strong> (Optional) The widths of child cells.</li>
2141 * <li><strong>height</strong> (Optional) The height of the layout.</li>
2142 * <li><strong>padding</strong> (Optional) The padding width inside child
2144 * <li><strong>align</strong> (Optional) The alignment of the whole layout
2149 hbox : function( dialog
, childObjList
, childHtmlList
, htmlList
, elementDefinition
)
2151 if ( arguments
.length
< 4 )
2154 this._
|| ( this._
= {} );
2156 var children
= this._
.children
= childObjList
,
2157 widths
= elementDefinition
&& elementDefinition
.widths
|| null,
2158 height
= elementDefinition
&& elementDefinition
.height
|| null,
2162 var innerHTML = function()
2164 var html
= [ '<tbody><tr class="cke_dialog_ui_hbox">' ];
2165 for ( i
= 0 ; i
< childHtmlList
.length
; i
++ )
2167 var className
= 'cke_dialog_ui_hbox_child',
2170 className
= 'cke_dialog_ui_hbox_first';
2171 if ( i
== childHtmlList
.length
- 1 )
2172 className
= 'cke_dialog_ui_hbox_last';
2173 html
.push( '<td class="', className
, '" role="presentation" ' );
2177 styles
.push( 'width:' + CKEDITOR
.tools
.cssLength( widths
[i
] ) );
2180 styles
.push( 'width:' + Math
.floor( 100 / childHtmlList
.length
) + '%' );
2182 styles
.push( 'height:' + CKEDITOR
.tools
.cssLength( height
) );
2183 if ( elementDefinition
&& elementDefinition
.padding
!= undefined )
2184 styles
.push( 'padding:' + CKEDITOR
.tools
.cssLength( elementDefinition
.padding
) );
2185 if ( styles
.length
> 0 )
2186 html
.push( 'style="' + styles
.join('; ') + '" ' );
2187 html
.push( '>', childHtmlList
[i
], '</td>' );
2189 html
.push( '</tr></tbody>' );
2190 return html
.join( '' );
2193 var attribs
= { role
: 'presentation' };
2194 elementDefinition
&& elementDefinition
.align
&& ( attribs
.align
= elementDefinition
.align
);
2196 CKEDITOR
.ui
.dialog
.uiElement
.call(
2199 elementDefinition
|| { type
: 'hbox' },
2208 * Vertical layout box for dialog UI elements.
2210 * @extends CKEDITOR.ui.dialog.hbox
2211 * @param {CKEDITOR.dialog} dialog
2212 * Parent dialog object.
2213 * @param {Array} childObjList
2214 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
2216 * @param {Array} childHtmlList
2217 * Array of HTML code that correspond to the HTML output of all the
2218 * objects in childObjList.
2219 * @param {Array} htmlList
2220 * Array of HTML code that this element will output to.
2221 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
2222 * The element definition. Accepted fields:
2224 * <li><strong>width</strong> (Optional) The width of the layout.</li>
2225 * <li><strong>heights</strong> (Optional) The heights of individual cells.
2227 * <li><strong>align</strong> (Optional) The alignment of the layout.</li>
2228 * <li><strong>padding</strong> (Optional) The padding width inside child
2230 * <li><strong>expand</strong> (Optional) Whether the layout should expand
2231 * vertically to fill its container.</li>
2235 vbox : function( dialog
, childObjList
, childHtmlList
, htmlList
, elementDefinition
)
2237 if (arguments
.length
< 3 )
2240 this._
|| ( this._
= {} );
2242 var children
= this._
.children
= childObjList
,
2243 width
= elementDefinition
&& elementDefinition
.width
|| null,
2244 heights
= elementDefinition
&& elementDefinition
.heights
|| null;
2246 var innerHTML = function()
2248 var html
= [ '<table role="presentation" cellspacing="0" border="0" ' ];
2249 html
.push( 'style="' );
2250 if ( elementDefinition
&& elementDefinition
.expand
)
2251 html
.push( 'height:100%;' );
2252 html
.push( 'width:' + CKEDITOR
.tools
.cssLength( width
|| '100%' ), ';' );
2254 html
.push( 'align="', CKEDITOR
.tools
.htmlEncode(
2255 ( elementDefinition
&& elementDefinition
.align
) || ( dialog
.getParentEditor().lang
.dir
== 'ltr' ? 'left' : 'right' ) ), '" ' );
2257 html
.push( '><tbody>' );
2258 for ( var i
= 0 ; i
< childHtmlList
.length
; i
++ )
2261 html
.push( '<tr><td role="presentation" ' );
2263 styles
.push( 'width:' + CKEDITOR
.tools
.cssLength( width
|| '100%' ) );
2265 styles
.push( 'height:' + CKEDITOR
.tools
.cssLength( heights
[i
] ) );
2266 else if ( elementDefinition
&& elementDefinition
.expand
)
2267 styles
.push( 'height:' + Math
.floor( 100 / childHtmlList
.length
) + '%' );
2268 if ( elementDefinition
&& elementDefinition
.padding
!= undefined )
2269 styles
.push( 'padding:' + CKEDITOR
.tools
.cssLength( elementDefinition
.padding
) );
2270 if ( styles
.length
> 0 )
2271 html
.push( 'style="', styles
.join( '; ' ), '" ' );
2272 html
.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList
[i
], '</td></tr>' );
2274 html
.push( '</tbody></table>' );
2275 return html
.join( '' );
2277 CKEDITOR
.ui
.dialog
.uiElement
.call( this, dialog
, elementDefinition
|| { type
: 'vbox' }, htmlList
, 'div', null, { role
: 'presentation' }, innerHTML
);
2282 CKEDITOR
.ui
.dialog
.uiElement
.prototype =
2285 * Gets the root DOM element of this dialog UI object.
2286 * @returns {CKEDITOR.dom.element} Root DOM element of UI object.
2288 * uiElement.getElement().hide();
2290 getElement : function()
2292 return CKEDITOR
.document
.getById( this.domId
);
2296 * Gets the DOM element that the user inputs values.
2297 * This function is used by setValue(), getValue() and focus(). It should
2298 * be overrided in child classes where the input element isn't the root
2300 * @returns {CKEDITOR.dom.element} The element where the user input values.
2302 * var rawValue = textInput.getInputElement().$.value;
2304 getInputElement : function()
2306 return this.getElement();
2310 * Gets the parent dialog object containing this UI element.
2311 * @returns {CKEDITOR.dialog} Parent dialog object.
2313 * var dialog = uiElement.getDialog();
2315 getDialog : function()
2317 return this._
.dialog
;
2321 * Sets the value of this dialog UI object.
2322 * @param {Object} value The new value.
2323 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2325 * uiElement.setValue( 'Dingo' );
2327 setValue : function( value
)
2329 this.getInputElement().setValue( value
);
2330 this.fire( 'change', { value
: value
} );
2335 * Gets the current value of this dialog UI object.
2336 * @returns {Object} The current value.
2338 * var myValue = uiElement.getValue();
2340 getValue : function()
2342 return this.getInputElement().getValue();
2346 * Tells whether the UI object's value has changed.
2347 * @returns {Boolean} true if changed, false if not changed.
2349 * if ( uiElement.isChanged() )
2350 * confirm( 'Value changed! Continue?' );
2352 isChanged : function()
2354 // Override in input classes.
2359 * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods.
2360 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2362 * focus : function()
2364 * this.selectParentTab();
2365 * // do something else.
2368 selectParentTab : function()
2370 var element
= this.getInputElement(),
2373 while ( ( cursor
= cursor
.getParent() ) && cursor
.$.className
.search( 'cke_dialog_page_contents' ) == -1 )
2376 // Some widgets don't have parent tabs (e.g. OK and Cancel buttons).
2380 tabId
= cursor
.getAttribute( 'name' );
2381 // Avoid duplicate select.
2382 if ( this._
.dialog
._
.currentTabId
!= tabId
)
2383 this._
.dialog
.selectPage( tabId
);
2388 * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page.
2389 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2391 * uiElement.focus();
2395 this.selectParentTab().getInputElement().focus();
2400 * Registers the on* event handlers defined in the element definition.
2401 * The default behavior of this function is:
2404 * If the on* event is defined in the class's eventProcesors list,
2405 * then the registration is delegated to the corresponding function
2406 * in the eventProcessors list.
2409 * If the on* event is not defined in the eventProcessors list, then
2410 * register the event handler under the corresponding DOM event of
2411 * the UI element's input DOM element (as defined by the return value
2412 * of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}).
2415 * This function is only called at UI element instantiation, but can
2416 * be overridded in child classes if they require more flexibility.
2417 * @param {CKEDITOR.dialog.uiElementDefinition} definition The UI element
2419 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2422 registerEvents : function( definition
)
2424 var regex
= /^on([A-Z]\w+)/,
2427 var registerDomEvent = function( uiElement
, dialog
, eventName
, func
)
2429 dialog
.on( 'load', function()
2431 uiElement
.getInputElement().on( eventName
, func
, uiElement
);
2435 for ( var i
in definition
)
2437 if ( !( match
= i
.match( regex
) ) )
2439 if ( this.eventProcessors
[i
] )
2440 this.eventProcessors
[i
].call( this, this._
.dialog
, definition
[i
] );
2442 registerDomEvent( this, this._
.dialog
, match
[1].toLowerCase(), definition
[i
] );
2449 * The event processor list used by
2450 * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element
2451 * instantiation. The default list defines three on* events:
2453 * <li>onLoad - Called when the element's parent dialog opens for the
2455 * <li>onShow - Called whenever the element's parent dialog opens.</li>
2456 * <li>onHide - Called whenever the element's parent dialog closes.</li>
2461 * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick
2462 * // handlers in the UI element's definitions.
2463 * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {},
2464 * CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
2465 * { onClick : function( dialog, func ) { this.on( 'click', func ); } },
2466 * true );
2470 onLoad : function( dialog
, func
)
2472 dialog
.on( 'load', func
, this );
2475 onShow : function( dialog
, func
)
2477 dialog
.on( 'show', func
, this );
2480 onHide : function( dialog
, func
)
2482 dialog
.on( 'hide', func
, this );
2487 * The default handler for a UI element's access key down event, which
2488 * tries to put focus to the UI element.<br />
2489 * Can be overridded in child classes for more sophisticaed behavior.
2490 * @param {CKEDITOR.dialog} dialog The parent dialog object.
2491 * @param {String} key The key combination pressed. Since access keys
2492 * are defined to always include the CTRL key, its value should always
2493 * include a 'CTRL+' prefix.
2496 accessKeyDown : function( dialog
, key
)
2502 * The default handler for a UI element's access key up event, which
2503 * does nothing.<br />
2504 * Can be overridded in child classes for more sophisticated behavior.
2505 * @param {CKEDITOR.dialog} dialog The parent dialog object.
2506 * @param {String} key The key combination pressed. Since access keys
2507 * are defined to always include the CTRL key, its value should always
2508 * include a 'CTRL+' prefix.
2511 accessKeyUp : function( dialog
, key
)
2516 * Disables a UI element.
2519 disable : function()
2521 var element
= this.getInputElement();
2522 element
.setAttribute( 'disabled', 'true' );
2523 element
.addClass( 'cke_disabled' );
2527 * Enables a UI element.
2532 var element
= this.getInputElement();
2533 element
.removeAttribute( 'disabled' );
2534 element
.removeClass( 'cke_disabled' );
2538 * Determines whether an UI element is enabled or not.
2539 * @returns {Boolean} Whether the UI element is enabled.
2542 isEnabled : function()
2544 return !this.getInputElement().getAttribute( 'disabled' );
2548 * Determines whether an UI element is visible or not.
2549 * @returns {Boolean} Whether the UI element is visible.
2552 isVisible : function()
2554 return this.getInputElement().isVisible();
2558 * Determines whether an UI element is focus-able or not.
2559 * Focus-able is defined as being both visible and enabled.
2560 * @returns {Boolean} Whether the UI element can be focused.
2563 isFocusable : function()
2565 if ( !this.isEnabled() || !this.isVisible() )
2571 CKEDITOR
.ui
.dialog
.hbox
.prototype = CKEDITOR
.tools
.extend( new CKEDITOR
.ui
.dialog
.uiElement
,
2573 * @lends CKEDITOR.ui.dialog.hbox.prototype
2577 * Gets a child UI element inside this container.
2578 * @param {Array|Number} indices An array or a single number to indicate the child's
2579 * position in the container's descendant tree. Omit to get all the children in an array.
2580 * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container
2581 * if no argument given, or the specified UI element if indices is given.
2583 * var checkbox = hbox.getChild( [0,1] );
2584 * checkbox.setValue( true );
2586 getChild : function( indices
)
2588 // If no arguments, return a clone of the children array.
2589 if ( arguments
.length
< 1 )
2590 return this._
.children
.concat();
2592 // If indices isn't array, make it one.
2593 if ( !indices
.splice
)
2594 indices
= [ indices
];
2596 // Retrieve the child element according to tree position.
2597 if ( indices
.length
< 2 )
2598 return this._
.children
[ indices
[0] ];
2600 return ( this._
.children
[ indices
[0] ] && this._
.children
[ indices
[0] ].getChild
) ?
2601 this._
.children
[ indices
[0] ].getChild( indices
.slice( 1, indices
.length
) ) :
2606 CKEDITOR
.ui
.dialog
.vbox
.prototype = new CKEDITOR
.ui
.dialog
.hbox();
2612 var commonBuilder
= {
2613 build : function( dialog
, elementDefinition
, output
)
2615 var children
= elementDefinition
.children
,
2619 for ( var i
= 0 ; ( i
< children
.length
&& ( child
= children
[i
] ) ) ; i
++ )
2622 childHtmlList
.push( childHtml
);
2623 childObjList
.push( CKEDITOR
.dialog
._
.uiElementBuilders
[ child
.type
].build( dialog
, child
, childHtml
) );
2625 return new CKEDITOR
.ui
.dialog
[elementDefinition
.type
]( dialog
, childObjList
, childHtmlList
, output
, elementDefinition
);
2629 CKEDITOR
.dialog
.addUIElement( 'hbox', commonBuilder
);
2630 CKEDITOR
.dialog
.addUIElement( 'vbox', commonBuilder
);
2634 * Generic dialog command. It opens a specific dialog when executed.
2636 * @augments CKEDITOR.commandDefinition
2637 * @param {string} dialogName The name of the dialog to open when executing
2640 * // Register the "link" command, which opens the "link" dialog.
2641 * editor.addCommand( 'link', <b>new CKEDITOR.dialogCommand( 'link' )</b> );
2643 CKEDITOR
.dialogCommand = function( dialogName
)
2645 this.dialogName
= dialogName
;
2648 CKEDITOR
.dialogCommand
.prototype =
2651 exec : function( editor
)
2653 editor
.openDialog( this.dialogName
);
2656 // Dialog commands just open a dialog ui, thus require no undo logic,
2657 // undo support should dedicate to specific dialog implementation.
2660 editorFocus
: CKEDITOR
.env
.ie
2665 var notEmptyRegex
= /^([a]|[^a])+$/,
2666 integerRegex
= /^\d*$/,
2667 numberRegex
= /^\d*(?:\.\d+)?$/;
2669 CKEDITOR
.VALIDATE_OR
= 1;
2670 CKEDITOR
.VALIDATE_AND
= 2;
2672 CKEDITOR
.dialog
.validate
=
2674 functions : function()
2679 * It's important for validate functions to be able to accept the value
2680 * as argument in addition to this.getValue(), so that it is possible to
2681 * combine validate functions together to make more sophisticated
2684 var value
= this && this.getValue
? this.getValue() : arguments
[0];
2686 var msg
= undefined,
2687 relation
= CKEDITOR
.VALIDATE_AND
,
2690 for ( i
= 0 ; i
< arguments
.length
; i
++ )
2692 if ( typeof( arguments
[i
] ) == 'function' )
2693 functions
.push( arguments
[i
] );
2698 if ( i
< arguments
.length
&& typeof( arguments
[i
] ) == 'string' )
2704 if ( i
< arguments
.length
&& typeof( arguments
[i
]) == 'number' )
2705 relation
= arguments
[i
];
2707 var passed
= ( relation
== CKEDITOR
.VALIDATE_AND
? true : false );
2708 for ( i
= 0 ; i
< functions
.length
; i
++ )
2710 if ( relation
== CKEDITOR
.VALIDATE_AND
)
2711 passed
= passed
&& functions
[i
]( value
);
2713 passed
= passed
|| functions
[i
]( value
);
2718 if ( msg
!== undefined )
2720 if ( this && ( this.select
|| this.focus
) )
2721 ( this.select
|| this.focus
)();
2729 regex : function( regex
, msg
)
2732 * Can be greatly shortened by deriving from functions validator if code size
2733 * turns out to be more important than performance.
2737 var value
= this && this.getValue
? this.getValue() : arguments
[0];
2738 if ( !regex
.test( value
) )
2740 if ( msg
!== undefined )
2742 if ( this && ( this.select
|| this.focus
) )
2755 notEmpty : function( msg
)
2757 return this.regex( notEmptyRegex
, msg
);
2760 integer : function( msg
)
2762 return this.regex( integerRegex
, msg
);
2765 'number' : function( msg
)
2767 return this.regex( numberRegex
, msg
);
2770 equals : function( value
, msg
)
2772 return this.functions( function( val
){ return val
== value
; }, msg
);
2775 notEqual : function( value
, msg
)
2777 return this.functions( function( val
){ return val
!= value
; }, msg
);
2783 // Extend the CKEDITOR.editor class with dialog specific functions.
2784 CKEDITOR
.tools
.extend( CKEDITOR
.editor
.prototype,
2785 /** @lends CKEDITOR.editor.prototype */
2788 * Loads and opens a registered dialog.
2789 * @param {String} dialogName The registered name of the dialog.
2790 * @param {Function} callback The function to be invoked after dialog instance created.
2791 * @see CKEDITOR.dialog.add
2793 * CKEDITOR.instances.editor1.openDialog( 'smiley' );
2794 * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered.
2796 openDialog : function( dialogName
, callback
)
2798 var dialogDefinitions
= CKEDITOR
.dialog
._
.dialogDefinitions
[ dialogName
],
2799 dialogSkin
= this.skin
.dialog
;
2801 // If the dialogDefinition is already loaded, open it immediately.
2802 if ( typeof dialogDefinitions
== 'function' && dialogSkin
._isLoaded
)
2804 var storedDialogs
= this._
.storedDialogs
||
2805 ( this._
.storedDialogs
= {} );
2807 var dialog
= storedDialogs
[ dialogName
] ||
2808 ( storedDialogs
[ dialogName
] = new CKEDITOR
.dialog( this, dialogName
) );
2810 callback
&& callback
.call( dialog
, dialog
);
2815 else if ( dialogDefinitions
== 'failed' )
2816 throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName
+ '" failed when loading definition.' );
2818 // Not loaded? Load the .js file first.
2819 var body
= CKEDITOR
.document
.getBody(),
2820 cursor
= body
.$.style
.cursor
,
2823 body
.setStyle( 'cursor', 'wait' );
2825 function onDialogFileLoaded( success
)
2827 var dialogDefinition
= CKEDITOR
.dialog
._
.dialogDefinitions
[ dialogName
],
2828 skin
= me
.skin
.dialog
;
2830 // Check if both skin part and definition is loaded.
2831 if ( !skin
._isLoaded
|| loadDefinition
&& typeof success
== 'undefined' )
2834 // In case of plugin error, mark it as loading failed.
2835 if ( typeof dialogDefinition
!= 'function' )
2836 CKEDITOR
.dialog
._
.dialogDefinitions
[ dialogName
] = 'failed';
2838 me
.openDialog( dialogName
, callback
);
2839 body
.setStyle( 'cursor', cursor
);
2842 if ( typeof dialogDefinitions
== 'string' )
2844 var loadDefinition
= 1;
2845 CKEDITOR
.scriptLoader
.load( CKEDITOR
.getUrl( dialogDefinitions
), onDialogFileLoaded
);
2848 CKEDITOR
.skins
.load( this, 'dialog', onDialogFileLoaded
);
2854 CKEDITOR
.plugins
.add( 'dialog',
2856 requires
: [ 'dialogui' ]
2859 // Dialog related configurations.
2862 * The color of the dialog background cover. It should be a valid CSS color
2864 * @name CKEDITOR.config.dialog_backgroundCoverColor
2868 * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)';
2872 * The opacity of the dialog background cover. It should be a number within the
2874 * @name CKEDITOR.config.dialog_backgroundCoverOpacity
2878 * config.dialog_backgroundCoverOpacity = 0.7;
2882 * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened.
2883 * @name CKEDITOR.config.dialog_startupFocusTab
2887 * config.dialog_startupFocusTab = true;
2891 * The distance of magnetic borders used in moving and resizing dialogs,
2892 * measured in pixels.
2893 * @name CKEDITOR.config.dialog_magnetDistance
2897 * config.dialog_magnetDistance = 30;
2901 * Fired when a dialog definition is about to be used to create a dialog into
2902 * an editor instance. This event makes it possible to customize the definition
2903 * before creating it.
2904 * <p>Note that this event is called only the first time a specific dialog is
2905 * opened. Successive openings will use the cached dialog, and this event will
2906 * not get fired.</p>
2907 * @name CKEDITOR#dialogDefinition
2909 * @param {CKEDITOR.dialog.dialogDefinition} data The dialog defination that
2911 * @param {CKEDITOR.editor} editor The editor instance that will use the