1 /*
  2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5
  6 /**
  7  * @fileOverview The floating dialog plugin.
  8  */
  9
 10 CKEDITOR.plugins.add( 'dialog',
 11 	{
 12 		requires : [ 'dialogui' ]
 13 	});
 14
 15 /**
 16  * No resize for this dialog.
 17  * @constant
 18  */
 19 CKEDITOR.DIALOG_RESIZE_NONE = 0;
 20
 21 /**
 22  * Only allow horizontal resizing for this dialog, disable vertical resizing.
 23  * @constant
 24  */
 25 CKEDITOR.DIALOG_RESIZE_WIDTH = 1;
 26
 27 /**
 28  * Only allow vertical resizing for this dialog, disable horizontal resizing.
 29  * @constant
 30  */
 31 CKEDITOR.DIALOG_RESIZE_HEIGHT = 2;
 32
 33 /*
 34  * Allow the dialog to be resized in both directions.
 35  * @constant
 36  */
 37 CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 38
 39 (function()
 40 {
 41 	/**
 42 	 * This is the base class for runtime dialog objects. An instance of this
 43 	 * class represents a single named dialog for a single editor instance.
 44 	 * @param {Object} editor The editor which created the dialog.
 45 	 * @param {String} dialogName The dialog's registered name.
 46 	 * @constructor
 47 	 * @example
 48 	 * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' );
 49 	 */
 50 	CKEDITOR.dialog = function( editor, dialogName )
 51 	{
 52 		// Load the dialog definition.
 53 		var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ];
 54 		if ( !definition )
 55 		{
 56 			console.log( 'Error: The dialog "' + dialogName + '" is not defined.' );
 57 			return;
 58 		}
 59
 60 		// Completes the definition with the default values.
 61 		definition = CKEDITOR.tools.extend( definition( editor ), defaultDialogDefinition );
 62
 63 		// Create a complex definition object, extending it with the API
 64 		// functions.
 65 		definition = new definitionObject( this, definition );
 66
 67 		// Fire the "dialogDefinition" event, making it possible to customize
 68 		// the dialog definition.
 69 		this.definition = definition = CKEDITOR.fire( 'dialogDefinition',
 70 			{
 71 				name : dialogName,
 72 				definition : definition
 73 			}
 74 			, editor ).definition;
 75
 76 		var themeBuilt = editor.theme.buildDialog( editor );
 77
 78 		// Initialize some basic parameters.
 79 		this._ =
 80 		{
 81 			editor : editor,
 82 			element : themeBuilt.element,
 83 			name : dialogName,
 84 			size : { width : 0, height : 0 },
 85 			contents : {},
 86 			buttons : {},
 87 			accessKeyMap : {},
 88
 89 			// Initialize the tab and page map.
 90 			tabs : {},
 91 			pageCount : 0,
 92 			lastTab : null
 93 		};
 94
 95 		/**
 96 		 * An associative map of elements in the dialog. It has the following members:
 97 		 * <ul>
 98 		 * 	<li>tl - top left corner.</li>
 99 		 * 	<li>tl_resize - resize handle at the top left corner.</li>
100 		 * 	<li>t - top side.</li>
101 		 * 	<li>t_resize - resize handle at the top.</li>
102 		 * 	<li>tr - top right corner.</li>
103 		 * 	<li>tr_resize - resize handle at the top right.</li>
104 		 * 	<li>l - left side.</li>
105 		 * 	<li>l_resize - resize handle at the left side.</li>
106 		 * 	<li>c - center area.</li>
107 		 * 	<li>r - right side.</li>
108 		 * 	<li>r_resize - resize handle at the right side.</li>
109 		 * 	<li>bl - bottom left corner.</li>
110 		 * 	<li>bl_resize - resize handle at the bottom left.</li>
111 		 * 	<li>b - bottom side.</li>
112 		 * 	<li>b_resize - resize handle at the bottom.</li>
113 		 * 	<li>br - bottom right corner.</li>
114 		 * 	<li>br_resize - resize handle at the bottom right.</li>
115 		 * 	<li>title - title area.</li>
116 		 * 	<li>close - close button.</li>
117 		 * 	<li>tabs - tabs area.</li>
118 		 * 	<li>contents - the content page area.</li>
119 		 * 	<li>footer - the footer area.</li>
120 		 * </ul>
121 		 * @type Object
122 		 * @field
123 		 */
124 		this.parts = {
125 			'tl' : [0,0],
126 			'tl_resize' : [0,0,0],
127 			't' : [0,1],
128 			't_resize' : [0,1,0],
129 			'tr' : [0,2],
130 			'tr_resize' : [0,2,0],
131 			'l' : [1,0],
132 			'l_resize' : [1,0,0],
133 			'c' : [1,1],
134 			'r' : [1,2],
135 			'r_resize' : [1,2,0],
136 			'bl' : [2,0],
137 			'bl_resize' : [2,0,0],
138 			'b' : [2,1],
139 			'b_resize' : [2,1,0],
140 			'br' : [2,2],
141 			'br_resize' : [2,2,0],
142 			'title' : [1,1,0],
143 			'close' : [1,1,0,0],
144 			'tabs' : [1,1,1,0,0],
145 			'tabs_table' : [1,1,1],
146 			'contents' : [1,1,2],
147 			'footer' : [1,1,3]
148 		};
149
150 		// Initialize the parts map.
151 		var element = this._.element.getFirst();
152 		for ( var i in this.parts )
153 			this.parts[i] = element.getChild( this.parts[i] );
154
155 		// Call the CKEDITOR.event constructor to initialize this instance.
156 		CKEDITOR.event.call( this );
157
158 		// Initialize load, show, hide, ok and cancel events.
159 		if ( definition.onLoad )
160 			this.on( 'load', definition.onLoad );
161
162 		if ( definition.onShow )
163 			this.on( 'show', definition.onShow );
164
165 		if ( definition.onHide )
166 			this.on( 'hide', definition.onHide );
167
168 		if ( definition.onOk )
169 		{
170 			this.on( 'ok', function( evt )
171 				{
172 					if ( definition.onOk.call( this, evt ) === false )
173 						evt.data.hide = false;
174 				});
175 		}
176
177 		if ( definition.onCancel )
178 		{
179 			this.on( 'cancel', function( evt )
180 				{
181 					if ( definition.onCancel.call( this, evt ) === false )
182 						evt.data.hide = false;
183 				});
184 		}
185
186 		var me = this;
187
188 		// Iterates over all items inside all content in the dialog, calling a
189 		// function for each of them.
190 		var iterContents = function( func )
191 		{
192 			var contents = me._.contents,
193 				stop = false;
194
195 			for ( var i in contents )
196 			{
197 				for ( var j in contents[i] )
198 				{
199 					stop = func.call( this, contents[i][j] );
200 					if ( stop )
201 						return;
202 				}
203 			}
204 		};
205
206 		this.on( 'ok', function( evt )
207 			{
208 				iterContents( function( item )
209 					{
210 						if ( item.validate )
211 						{
212 							var isValid = item.validate( this );
213
214 							if ( typeof isValid == 'string' )
215 							{
216 								alert( isValid );
217 								isValid = false;
218 							}
219
220 							if ( isValid === false )
221 							{
222 								if ( item.select )
223 									item.select();
224 								else
225 									item.focus();
226
227 								evt.data.hide = false;
228 								evt.stop();
229 								return true;
230 							}
231 						}
232 					});
233 			}, this, null, 0 );
234
235 		this.on( 'cancel', function( evt )
236 			{
237 				iterContents( function( item )
238 					{
239 						if ( item.isChanged() )
240 						{
241 							if ( !confirm( editor.lang.common.confirmCancel ) )
242 								evt.data.hide = false;
243 							return true;
244 						}
245 					});
246 			}, this, null, 0 );
247
248 		this.parts.close.on( 'click', function( evt )
249 				{
250 					if ( this.fire( 'cancel', { hide : true } ).hide !== false )
251 						this.hide();
252 				}, this );
253
254 		// IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661).
255 		// This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken.
256 		if ( CKEDITOR.env.ie6Compat )
257 		{
258 			this.on( 'load', function( evt )
259 					{
260 						var outer = this.getElement(),
261 							inner = outer.getFirst();
262 						inner.remove();
263 						inner.appendTo( outer );
264 					}, this );
265 		}
266
267 		initDragAndDrop( this );
268 		initResizeHandles( this );
269
270 		// Insert the title.
271 		( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title );
272
273 		// Insert the tabs and contents.
274 		for ( i = 0 ; i < definition.contents.length ; i++ )
275 			this.addPage( definition.contents[i] );
276
277 		var tabRegex = /cke_dialog_tab(\s|$|_)/,
278 			tabOuterRegex = /cke_dialog_tab(\s|$)/;
279 		this.parts['tabs'].on( 'click', function( evt )
280 				{
281 					var target = evt.data.getTarget(), firstNode = target, id, page;
282
283 					// If we aren't inside a tab, bail out.
284 					if ( !tabRegex.test( target.$.className ) )
285 						return;
286
287 					// Find the outer <td> container of the tab.
288 					while ( target.getName() != 'td' || !tabOuterRegex.test( target.$.className ) )
289 					{
290 						target = target.getParent();
291 					}
292 					id = target.$.id.substr( 0, target.$.id.lastIndexOf( '_' ) );
293 					this.selectPage( id );
294 				}, this );
295
296 		// Insert buttons.
297 		var buttonsHtml = [],
298 			buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this,
299 				{
300 					type : 'hbox',
301 					className : 'cke_dialog_footer_buttons',
302 					widths : [],
303 					children : definition.buttons
304 				}, buttonsHtml ).getChild();
305 		this.parts.footer.setHtml( buttonsHtml.join( '' ) );
306
307 		for ( i = 0 ; i < buttons.length ; i++ )
308 			this._.buttons[ buttons[i].id ] = buttons[i];
309
310 		// Insert dummy text box for grabbing focus away from the editing area.
311 		this._.dummyText = CKEDITOR.dom.element.createFromHtml( '<input type="text" style="position: absolute; left: -100000px; top: -100000px" />' );
312 		this._.dummyText.appendTo( element );
313
314 		CKEDITOR.skins.load( editor.config.skin, 'dialog' );
315 	};
316
317 	CKEDITOR.dialog.prototype =
318 	{
319 		/**
320 		 * Resizes the dialog.
321 		 * @param {Number} width The width of the dialog in pixels.
322 		 * @param {Number} height The height of the dialog in pixels.
323 		 * @function
324 		 * @example
325 		 * dialogObj.resize( 800, 640 );
326 		 */
327 		resize : (function()
328 		{
329 			return function( width, height )
330 			{
331 				if ( this._.size && this._.size.width == width && this._.size.height == height )
332 					return;
333
334 				CKEDITOR.dialog.fire( 'resize',
335 					{
336 						dialog : this,
337 						skin : this._.editor.config.skin,
338 						width : width,
339 						height : height
340 					}, this._.editor );
341
342 				this._.size = { width : width, height : height };
343 			};
344 		})(),
345
346 		/**
347 		 * Gets the current size of the dialog in pixels.
348 		 * @returns {Object} An object with "width" and "height" properties.
349 		 * @example
350 		 * var width = dialogObj.getSize().width;
351 		 */
352 		getSize : function()
353 		{
354 			return CKEDITOR.tools.extend( {}, this._.size );
355 		},
356
357 		/**
358 		 * Moves the dialog to an (x, y) coordinate relative to the window.
359 		 * @function
360 		 * @param {Number} x The target x-coordinate.
361 		 * @param {Number} y The target y-coordinate.
362 		 * @example
363 		 * dialogObj.move( 10, 40 );
364 		 */
365 		move : (function()
366 		{
367 			var isFixed;
368 			return function( x, y )
369 			{
370 				// The dialog may be fixed positioned or absolute positioned. Ask the
371 				// browser what is the current situation first.
372 				if ( isFixed === undefined )
373 					isFixed = this._.element.getFirst().getComputedStyle( 'position' ) == 'fixed';
374
375 				if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y )
376 					return;
377
378 				// Save the current position.
379 				this._.position = { x : x, y : y };
380
381 				// If not fixed positioned, add scroll position to the coordinates.
382 				if ( !isFixed )
383 				{
384 					var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition();
385 					x += scrollPosition.x;
386 					y += scrollPosition.y;
387 				}
388
389 				this._.element.getFirst().setStyles(
390 						{
391 							'left' : x + 'px',
392 							'top' : y + 'px'
393 						});
394 			};
395 		})(),
396
397 		/**
398 		 * Gets the dialog's position in the window.
399 		 * @returns {Object} An object with "x" and "y" properties.
400 		 * @example
401 		 * var dialogX = dialogObj.getPosition().x;
402 		 */
403 		getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); },
404
405 		/**
406 		 * Shows the dialog box.
407 		 * @example
408 		 * dialogObj.show();
409 		 */
410 		show : function()
411 		{
412 			// Insert the dialog's element to the root document.
413 			var element = this._.element;
414 			var definition = this.definition;
415 			if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) )
416 				element.appendTo( CKEDITOR.document.getBody() );
417 			else
418 				return;
419
420 			// First, set the dialog to an appropriate size.
421 			this.resize( definition.minWidth, definition.minHeight );
422
423 			// Rearrange the dialog to the middle of the window.
424 			var viewSize = CKEDITOR.document.getWindow().getViewPaneSize();
425 			this.move( ( viewSize.width - this._.size.width ) / 2, ( viewSize.height - this._.size.height ) / 2 );
426
427 			// Select the first tab by default.
428 			this.selectPage( this.definition.contents[0].id );
429
430 			// Reset all inputs back to their default value.
431 			this.reset();
432
433 			// Set z-index.
434 			if ( CKEDITOR.dialog._.currentZIndex === null )
435 				CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex;
436 			this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 );
437
438 			// Maintain the dialog ordering and dialog cover.
439 			// Also register key handlers if first dialog.
440 			if ( CKEDITOR.dialog._.currentTop === null )
441 			{
442 				CKEDITOR.dialog._.currentTop = this;
443 				this._.parentDialog = null;
444 				addCover( this._.editor );
445
446 				CKEDITOR.document.on( 'keydown', accessKeyDownHandler );
447 				CKEDITOR.document.on( 'keyup', accessKeyUpHandler );
448 			}
449 			else
450 			{
451 				this._.parentDialog = CKEDITOR.dialog._.currentTop;
452 				var parentElement = this._.parentDialog.getElement().getFirst();
453 				parentElement.$.style.zIndex  -= Math.floor( this._.editor.config.baseFloatZIndex / 2 );
454 				CKEDITOR.dialog._.currentTop = this;
455 			}
456
457 			// Register the Esc hotkeys.
458 			registerAccessKey( this, this, '\x1b', null, function()
459 					{
460 						this.getButton( 'cancel' ) && this.getButton( 'cancel' ).click();
461 					} );
462
463 			// Save editor selection and grab the focus.
464 			if ( !this._.parentDialog )
465 				this.saveSelection();
466 			this._.dummyText.focus();
467 			this._.dummyText.$.select();
468
469 			// Execute onLoad for the first show.
470 			this.fireOnce( 'load', {} );
471 			this.fire( 'show', {} );
472 		},
473
474 		/**
475 		 * Executes a function for each UI element.
476 		 * @param {Function} fn Function to execute for each UI element.
477 		 * @returns {CKEDITOR.dialog} The current dialog object.
478 		 */
479 		foreach : function( fn )
480 		{
481 			for ( var i in this._.contents )
482 			{
483 				for ( var j in this._.contents[i] )
484 					fn( this._.contents[i][j]);
485 			}
486 			return this;
487 		},
488
489 		/**
490 		 * Resets all input values in the dialog.
491 		 * @example
492 		 * dialogObj.reset();
493 		 * @returns {CKEDITOR.dialog} The current dialog object.
494 		 */
495 		reset : (function()
496 		{
497 			var fn = function( widget ){ if ( widget.reset ) widget.reset(); };
498 			return function(){ this.foreach( fn ); return this; };
499 		})(),
500
501 		/**
502 		 * Pushes the current values of all inputs in the dialog into the default stack.
503 		 * @example
504 		 * dialogObj.pushDefault();
505 		 * @returns {CKEDITOR.dialog} The current dialog object.
506 		 */
507 		pushDefault : (function()
508 		{
509 			var fn = function( widget ){ if ( widget.pushDefault ) widget.pushDefault(); };
510 			return function(){ this.foreach( fn ); return this; };
511 		})(),
512
513 		/**
514 		 * Pops the current default values of all inputs in the dialog.
515 		 * @example
516 		 * dialogObj.popDefault();
517 		 * @returns {CKEDITOR.dialog} The current dialog object.
518 		 */
519 		popDefault : (function()
520 		{
521 			var fn = function( widget ){ if ( widget.popDefault ) widget.popDefault(); };
522 			return function(){ this.foreach( fn ); return this; };
523 		})(),
524
525 		setupContent : function()
526 		{
527 			var args = arguments;
528 			this.foreach( function( widget )
529 				{
530 					if ( widget.setup )
531 						widget.setup.apply( widget, args );
532 				});
533 		},
534
535 		commitContent : function()
536 		{
537 			var args = arguments;
538 			this.foreach( function( widget )
539 				{
540 					if ( widget.commit )
541 						widget.commit.apply( widget, args );
542 				});
543 		},
544
545 		/**
546 		 * Hides the dialog box.
547 		 * @example
548 		 * dialogObj.hide();
549 		 */
550 		hide : function()
551 		{
552 			// Remove the dialog's element from the root document.
553 			var element = this._.element;
554 			if ( !element.getParent() )
555 				return;
556 			element.remove();
557
558 			// Unregister all access keys associated with this dialog.
559 			unregisterAccessKey( this );
560
561 			// Maintain dialog ordering and remove cover if needed.
562 			if ( !this._.parentDialog )
563 				removeCover();
564 			else
565 			{
566 				var parentElement = this._.parentDialog.getElement().getFirst();
567 				parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) );
568 			}
569 			CKEDITOR.dialog._.currentTop = this._.parentDialog;
570
571 			// Deduct or clear the z-index.
572 			if ( !this._.parentDialog )
573 			{
574 				CKEDITOR.dialog._.currentZIndex = null;
575
576 				// Remove access key handlers.
577 				CKEDITOR.document.removeListener( 'keydown', accessKeyDownHandler );
578 				CKEDITOR.document.removeListener( 'keyup', accessKeyUpHandler );
579
580 				// Restore focus and (if not already restored) selection in the editing area.
581 				this.restoreSelection();
582 				this._.editor.focus();
583 			}
584 			else
585 				CKEDITOR.dialog._.currentZIndex -= 10;
586
587 			this.fire( 'hide', {} );
588 		},
589
590 		/**
591 		 * Adds a tabbed page into the dialog.
592 		 * @param {Object} contents Content definition.
593 		 * @example
594 		 */
595 		addPage : function( contents )
596 		{
597 			var pageHtml = [],
598 				titleHtml = contents.title ? 'title="' + CKEDITOR.tools.htmlEncode( contents.title ) + '" ' : '',
599 				elements = contents.elements,
600 				vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this,
601 						{
602 							type : 'vbox',
603 							className : 'cke_dialog_page_contents',
604 							children : contents.elements,
605 							expand : !!contents.expand
606 						}, pageHtml );
607
608 			// Create the HTML for the tab and the content block.
609 			var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) );
610 			var tab = CKEDITOR.dom.element.createFromHtml( [
611 					'<table><tbody><tr><td class="cke_dialog_tab" ', titleHtml, '>',
612 					'<table border="0" cellspacing="0" cellpadding="0"><tbody><tr>',
613 						'<td class="cke_dialog_tab_left"></td>',
614 						'<td class="cke_dialog_tab_center">',
615 							CKEDITOR.tools.htmlEncode( contents.label.replace( / /g, '\xa0' ) ),
616 						'</td>',
617 						'<td class="cke_dialog_tab_right"></td>',
618 					'</tr></tbody></table></td></tr></tbody></table>'
619 				].join( '' ) );
620 			tab = tab.getChild( [0,0,0] );
621
622 			// First and last tab styles classes.
623 			if ( this._.lastTab )
624 				this._.lastTab.removeClass( 'last' );
625 			tab.addClass( this._.pageCount > 0 ? 'last' : 'first' );
626
627 			// If only a single page exist, a different style is used in the central pane.
628 			if ( this._.pageCount === 0 )
629 				this.parts.c.addClass( 'single_page' );
630 			else
631 				this.parts.c.removeClass( 'single_page' );
632
633 			// Take records for the tabs and elements created.
634 			this._.tabs[ contents.id ] = [ tab, page ];
635 			this._.pageCount++;
636 			this._.lastTab = tab;
637 			var contentMap = this._.contents[ contents.id ] = {},
638 				cursor,
639 				children = vbox.getChild();
640 			while ( ( cursor = children.shift() ) )
641 			{
642 				contentMap[ cursor.id ] = cursor;
643 				if ( typeof( cursor.getChild ) == 'function' )
644 					children.push.apply( children, cursor.getChild() );
645 			}
646
647 			// Attach the DOM nodes.
648 			tab.unselectable();
649 			page.appendTo( this.parts.contents );
650 			tab.insertBefore( this.parts.tabs.getChild( this.parts.tabs.getChildCount() - 1 ) );
651 			tab.setAttribute( 'id', contents.id + '_' + CKEDITOR.tools.getNextNumber() );
652 			page.setAttribute( 'name', contents.id );
653
654 			// Add access key handlers if access key is defined.
655 			if ( contents.accessKey )
656 			{
657 				registerAccessKey( this, this, 'CTRL+' + contents.accessKey,
658 					tabAccessKeyDown, tabAccessKeyUp );
659 				this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id;
660 			}
661 		},
662
663 		/**
664 		 * Activates a tab page in the dialog by its id.
665 		 * @param {String} id The id of the dialog tab to be activated.
666 		 * @example
667 		 * dialogObj.selectPage( 'tab_1' );
668 		 */
669 		selectPage : function( id )
670 		{
671 			// Hide the non-selected tabs and pages.
672 			for ( var i in this._.tabs )
673 			{
674 				var tab = this._.tabs[i][0],
675 					page = this._.tabs[i][1];
676 				if ( i != id )
677 				{
678 					tab.removeClass( 'cke_dialog_tab_selected' );
679 					page.hide();
680 				}
681 			}
682
683 			var selected = this._.tabs[id];
684 			selected[0].addClass( 'cke_dialog_tab_selected' );
685 			selected[1].show();
686 			var me = this;
687 		},
688
689 		/**
690 		 * Hides a page's tab away from the dialog.
691 		 * @param {String} id The page's Id.
692 		 * @example
693 		 * dialog.hidePage( 'tab_3' );
694 		 */
695 		hidePage : function( id )
696 		{
697 			var tab = this._.tabs[id] && this._.tabs[id][0];
698 			if ( !tab )
699 				return;
700 			tab.hide();
701 		},
702
703 		/**
704 		 * Unhides a page's tab.
705 		 * @param {String} id The page's Id.
706 		 * @example
707 		 * dialog.showPage( 'tab_2' );
708 		 */
709 		showPage : function( id )
710 		{
711 			var tab = this._.tabs[id] && this._.tabs[id][0];
712 			if ( !tab )
713 				return;
714 			tab.show();
715 		},
716
717 		/**
718 		 * Gets the root DOM element of the dialog.
719 		 * @returns {CKEDITOR.dom.element} The <span> element containing this dialog.
720 		 * @example
721 		 * var dialogElement = dialogObj.getElement().getFirst();
722 		 * dialogElement.setStyle( 'padding', '5px' );
723 		 */
724 		getElement : function()
725 		{
726 			return this._.element;
727 		},
728
729 		/**
730 		 * Gets a dialog UI element object from a dialog page.
731 		 * @param {String} pageId id of dialog page.
732 		 * @param {String} elementId id of UI element.
733 		 * @example
734 		 * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element.
735 		 */
736 		getContentElement : function( pageId, elementId )
737 		{
738 			return this._.contents[pageId][elementId];
739 		},
740
741 		/**
742 		 * Gets the value of a dialog UI element.
743 		 * @param {String} pageId id of dialog page.
744 		 * @param {String} elementId id of UI element.
745 		 * @example
746 		 * @returns {Object} The value of the UI element.
747 		 */
748 		getValueOf : function( pageId, elementId )
749 		{
750 			return this.getContentElement( pageId, elementId ).getValue();
751 		},
752
753 		/**
754 		 * Sets the value of a dialog UI element.
755 		 * @param {String} pageId id of the dialog page.
756 		 * @param {String} elementId id of the UI element.
757 		 * @param {Object} value The new value of the UI element.
758 		 * @example
759 		 */
760 		setValueOf : function( pageId, elementId, value )
761 		{
762 			return this.getContentElement( pageId, elementId ).setValue( value );
763 		},
764
765 		/**
766 		 * Gets the UI element of a button in the dialog's button row.
767 		 * @param {String} id The id of the button.
768 		 * @example
769 		 * @returns {CKEDITOR.ui.dialog.button} The button object.
770 		 */
771 		getButton : function( id )
772 		{
773 			return this._.buttons[ id ];
774 		},
775
776 		/**
777 		 * Simulates a click to a dialog button in the dialog's button row.
778 		 * @param {String} id The id of the button.
779 		 * @example
780 		 * @returns The return value of the dialog's "click" event.
781 		 */
782 		click : function( id )
783 		{
784 			return this._.buttons[ id ].click();
785 		},
786
787 		/**
788 		 * Disables a dialog button.
789 		 * @param {String} id The id of the button.
790 		 * @example
791 		 */
792 		disableButton : function( id )
793 		{
794 			return this._.buttons[ id ].disable();
795 		},
796
797 		/**
798 		 * Enables a dialog button.
799 		 * @param {String} id The id of the button.
800 		 * @example
801 		 */
802 		enableButton : function( id )
803 		{
804 			return this._.buttons[ id ].enable();
805 		},
806
807 		/**
808 		 * Gets the number of pages in the dialog.
809 		 * @returns {Number} Page count.
810 		 */
811 		getPageCount : function()
812 		{
813 			return this._.pageCount;
814 		},
815
816 		/**
817 		 * Gets the editor instance which opened this dialog.
818 		 * @returns {CKEDITOR.editor} Parent editor instances.
819 		 */
820 		getParentEditor : function()
821 		{
822 			return this._.editor;
823 		},
824
825 		/**
826 		 * Saves the current selection position in the editor.
827 		 * This function is automatically called when a non-nested dialog is opened,
828 		 * but it may also be called by event handlers in dialog definition.
829 		 * @example
830 		 */
831 		saveSelection : function()
832 		{
833 			if ( this._.editor.mode )
834 			{
835 				var selection = new CKEDITOR.dom.selection( this._.editor.document );
836 				this._.selectedRanges = selection.getRanges();
837 			}
838 		},
839
840 		/**
841 		 * Clears the saved selection in the dialog object.
842 		 * This function should be called if the dialog's code has already changed the
843 		 * current selection position because the dialog closed. (e.g. at onOk())
844 		 * @example
845 		 */
846 		clearSavedSelection : function()
847 		{
848 			delete this._.selectedRanges;
849 		},
850
851 		/**
852 		 * Restores the editor's selection from the previously saved position in this
853 		 * dialog.
854 		 * This function is automatically called when a non-nested dialog is closed,
855 		 * but it may also be called by event handlers in dialog definition.
856 		 * @example
857 		 */
858 		restoreSelection : function()
859 		{
860 			if ( this._.editor.mode && this._.selectedRanges )
861 				( new CKEDITOR.dom.selection( this._.editor.document ) ).selectRanges( this._.selectedRanges );
862 		}
863 	};
864
865 	CKEDITOR.tools.extend( CKEDITOR.dialog,
866 		/**
867 		 * @lends CKEDITOR.dialog
868 		 */
869 		{
870 			/**
871 			 * Registers a dialog.
872 			 * @param {String} name The dialog's name.
873 			 * @param {Function|String} dialogDefinition
874 			 * A function returning the dialog's definition, or the URL to the .js file holding the function.
875 			 * The function should accept an argument "editor" which is the current editor instance, and
876 			 * return an object conforming to {@link CKEDITOR.dialog.dialogDefinition}.
877 			 * @example
878 			 * @see CKEDITOR.dialog.dialogDefinition
879 			 */
880 			add : function( name, dialogDefinition )
881 			{
882 				this._.dialogDefinitions[name] = dialogDefinition;
883 			},
884
885 			exists : function( name )
886 			{
887 				return !!this._.dialogDefinitions[ name ];
888 			},
889
890 			/**
891 			 * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds.
892 			 * @static
893 			 * @field
894 			 * @example
895 			 * @type Function
896 			 */
897 			okButton : (function()
898 			{
899 				var retval = function( editor, override )
900 				{
901 					override = override || {};
902 					return CKEDITOR.tools.extend( {
903 						id : 'ok',
904 						type : 'button',
905 						label : editor.lang.common.ok,
906 						style : 'width: 60px',
907 						onClick : function( evt )
908 						{
909 							var dialog = evt.data.dialog;
910 							if ( dialog.fire( 'ok', { hide : true } ).hide !== false )
911 								dialog.hide();
912 						}
913 					}, override, true );
914 				};
915 				retval.type = 'button';
916 				retval.override = function( override )
917 				{
918 					return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); },
919 							{ type : 'button' }, true );
920 				};
921 				return retval;
922 			})(),
923
924 			/**
925 			 * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed.
926 			 * @static
927 			 * @field
928 			 * @example
929 			 * @type Function
930 			 */
931 			cancelButton : (function()
932 			{
933 				var retval = function( editor, override )
934 				{
935 					override = override || {};
936 					return CKEDITOR.tools.extend( {
937 						id : 'cancel',
938 						type : 'button',
939 						label : editor.lang.common.cancel,
940 						style : 'width: 60px',
941 						onClick : function( evt )
942 						{
943 							var dialog = evt.data.dialog;
944 							if ( dialog.fire( 'cancel', { hide : true } ).hide !== false )
945 								dialog.hide();
946 						}
947 					}, override, true );
948 				};
949 				retval.type = 'button';
950 				retval.override = function( override )
951 				{
952 					return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); },
953 							{ type : 'button' }, true );
954 				};
955 				return retval;
956 			})(),
957
958 			/**
959 			 * Registers a dialog UI element.
960 			 * @param {String} typeName The name of the UI element.
961 			 * @param {Function} builder The function to build the UI element.
962 			 * @example
963 			 */
964 			addUIElement : function( typeName, builder )
965 			{
966 				this._.uiElementBuilders[typeName] = builder;
967 			},
968
969 			/**
970 			 * Sets the width of margins of dialogs, which is used for the dialog moving and resizing logic.
971 			 * The margin here means the area between the dialog's container <div> and the visual boundary of the dialog.
972 			 * Typically this area is used for dialog shadows.
973 			 * This function is typically called in a skin's JavaScript files.
974 			 * @param {Number} top The top margin in pixels.
975 			 * @param {Number} right The right margin in pixels.
976 			 * @param {Number} bottom The bottom margin in pixels.
977 			 * @param {Number} left The left margin in pixels.
978 			 * @example
979 			 */
980 			setMargins : function( top, right, bottom, left )
981 			{
982 				this._.margins = [ top, right, bottom, left ];
983 			}
984 		});
985
986 	CKEDITOR.dialog._ =
987 	{
988 		uiElementBuilders : {},
989
990 		dialogDefinitions : {},
991
992 		currentTop : null,
993
994 		currentZIndex : null,
995
996 		margins : [0, 0, 0, 0]
997 	};
998
999 	// "Inherit" (copy actually) from CKEDITOR.event.
1000 	CKEDITOR.event.implementOn( CKEDITOR.dialog );
1001 	CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype );
1002
1003 	var defaultDialogDefinition =
1004 	{
1005 		resizable : CKEDITOR.DIALOG_RESIZE_NONE,
1006 		minWidth : 600,
1007 		minHeight : 400,
1008 		buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]
1009 	};
1010
1011 	// Tool function used to return an item from an array based on its id
1012 	// property.
1013 	var getById = function( array, id, recurse )
1014 	{
1015 		for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
1016 		{
1017 			if ( item.id == id )
1018 				return item;
1019 			if ( recurse && item[ recurse ] )
1020 			{
1021 				var retval = getById( item[ recurse ], id, recurse ) ;
1022 				if ( retval )
1023 					return retval;
1024 			}
1025 		}
1026 		return null;
1027 	};
1028
1029 	// Tool function used to add an item into an array.
1030 	var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound )
1031 	{
1032 		if ( nextSiblingId )
1033 		{
1034 			for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
1035 			{
1036 				if ( item.id == nextSiblingId )
1037 				{
1038 					array.splice( i, 0, newItem );
1039 					return newItem;
1040 				}
1041
1042 				if ( recurse && item[ recurse ] )
1043 				{
1044 					var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true );
1045 					if ( retval )
1046 						return retval;
1047 				}
1048 			}
1049
1050 			if ( nullIfNotFound )
1051 				return null;
1052 		}
1053
1054 		array.push( newItem );
1055 		return newItem;
1056 	};
1057
1058 	// Tool function used to remove an item from an array based on its id.
1059 	var removeById = function( array, id, recurse )
1060 	{
1061 		for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
1062 		{
1063 			if ( item.id == id )
1064 				return array.splice( i, 1 );
1065 			if ( recurse && item[ recurse ] )
1066 			{
1067 				var retval = removeById( item[ recurse ], id, recurse );
1068 				if ( retval )
1069 					return retval;
1070 			}
1071 		}
1072 		return null;
1073 	};
1074
1075 	/**
1076 	 * This class is not really part of the API. It is the "definition" property value
1077 	 * passed to "dialogDefinition" event handlers.
1078 	 * @constructor
1079 	 * @name CKEDITOR.dialog.dialogDefinitionObject
1080 	 * @extends CKEDITOR.dialog.dialogDefinition
1081 	 * @example
1082 	 * CKEDITOR.on( 'dialogDefinition', function( evt )
1083 	 * 	{
1084 	 * 		var definition = evt.data.definition;
1085 	 * 		var content = definition.getContents( 'page1' );
1086 	 * 		...
1087 	 * 	} );
1088 	 */
1089 	var definitionObject = function( dialog, dialogDefinition )
1090 	{
1091 		// TODO : Check if needed.
1092 		this.dialog = dialog;
1093
1094 		// Transform the contents entries in contentObjects.
1095 		var contents = dialogDefinition.contents;
1096 		for ( var i = 0, content ; ( content = contents[i] ) ; i++ )
1097 			contents[ i ] = new contentObject( dialog, content );
1098
1099 		CKEDITOR.tools.extend( this, dialogDefinition );
1100 	};
1101
1102 	definitionObject.prototype =
1103 	/** @lends CKEDITOR.dialog.dialogDefinitionObject.prototype */
1104 	{
1105 		/**
1106 		 * Gets a content definition.
1107 		 * @param {String} id The id of the content definition.
1108 		 * @returns {CKEDITOR.dialog.contentDefinition} The content definition
1109 		 *		matching id.
1110 		 */
1111 		getContents : function( id )
1112 		{
1113 			return getById( this.contents, id );
1114 		},
1115
1116 		/**
1117 		 * Gets a button definition.
1118 		 * @param {String} id The id of the button definition.
1119 		 * @returns {CKEDITOR.dialog.buttonDefinition} The button definition
1120 		 *		matching id.
1121 		 */
1122 		getButton : function( id )
1123 		{
1124 			return getById( this.buttons, id );
1125 		},
1126
1127 		/**
1128 		 * Adds a content definition object under this dialog definition.
1129 		 * @param {CKEDITOR.dialog.contentDefinition} contentDefinition The
1130 		 *		content definition.
1131 		 * @param {String} [nextSiblingId] The id of an existing content
1132 		 *		definition which the new content definition will be inserted
1133 		 *		before. Omit if the new content definition is to be inserted as
1134 		 *		the last item.
1135 		 * @returns {CKEDITOR.dialog.contentDefinition} The inserted content
1136 		 *		definition.
1137 		 */
1138 		addContents : function( contentDefinition, nextSiblingId )
1139 		{
1140 			return addById( this.contents, contentDefinition, nextSiblingId );
1141 		},
1142
1143 		/**
1144 		 * Adds a button definition object under this dialog definition.
1145 		 * @param {CKEDITOR.dialog.buttonDefinition} buttonDefinition The
1146 		 *		button definition.
1147 		 * @param {String} [nextSiblingId] The id of an existing button
1148 		 *		definition which the new button definition will be inserted
1149 		 *		before. Omit if the new button definition is to be inserted as
1150 		 *		the last item.
1151 		 * @returns {CKEDITOR.dialog.buttonDefinition} The inserted button
1152 		 *		definition.
1153 		 */
1154 		addButton : function( buttonDefinition, nextSiblingId )
1155 		{
1156 			return addById( this.buttons, buttonDefinition, nextSiblingId );
1157 		},
1158
1159 		/**
1160 		 * Removes a content definition from this dialog definition.
1161 		 * @param {String} id The id of the content definition to be removed.
1162 		 * @returns {CKEDITOR.dialog.contentDefinition} The removed content
1163 		 *		definition.
1164 		 */
1165 		removeContents : function( id )
1166 		{
1167 			removeById( this.contents, id );
1168 		},
1169
1170 		/**
1171 		 * Removes a button definition from the dialog definition.
1172 		 * @param {String} id The id of the button definition to be removed.
1173 		 * @returns {CKEDITOR.dialog.buttonDefinition} The removed button
1174 		 *		definition.
1175 		 */
1176 		removeButton : function( id )
1177 		{
1178 			removeById( this.buttons, id );
1179 		}
1180 	};
1181
1182 	/**
1183 	 * This class is not really part of the API. It is the template of the
1184 	 * objects representing content pages inside the
1185 	 * CKEDITOR.dialog.dialogDefinitionObject.
1186 	 * @constructor
1187 	 * @name CKEDITOR.dialog.contentDefinitionObject
1188 	 * @example
1189 	 * CKEDITOR.on( 'dialogDefinition', function( evt )
1190 	 * 	{
1191 	 * 		var definition = evt.data.definition;
1192 	 * 		var content = definition.getContents( 'page1' );
1193 	 *		content.remove( 'textInput1' );
1194 	 * 		...
1195 	 * 	} );
1196 	 */
1197 	var contentObject = function( dialog, contentDefinition )
1198 	{
1199 		this._ =
1200 		{
1201 			dialog : dialog
1202 		};
1203
1204 		CKEDITOR.tools.extend( this, contentDefinition );
1205 	};
1206
1207 	contentObject.prototype =
1208 	/** @lends CKEDITOR.dialog.contentDefinitionObject.prototype */
1209 	{
1210 		/**
1211 		 * Gets a UI element definition under the content definition.
1212 		 * @param {String} id The id of the UI element definition.
1213 		 * @returns {CKEDITOR.dialog.uiElementDefinition}
1214 		 */
1215 		get : function( id )
1216 		{
1217 			return getById( this.elements, id, 'children' );
1218 		},
1219
1220 		/**
1221 		 * Adds a UI element definition to the content definition.
1222 		 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition The
1223 		 *		UI elemnet definition to be added.
1224 		 * @param {String} nextSiblingId The id of an existing UI element
1225 		 *		definition which the new UI element definition will be inserted
1226 		 *		before. Omit if the new button definition is to be inserted as
1227 		 *		the last item.
1228 		 * @returns {CKEDITOR.dialog.uiElementDefinition} The element
1229 		 *		definition inserted.
1230 		 */
1231 		add : function( elementDefinition, nextSiblingId )
1232 		{
1233 			return addById( this.elements, elementDefinition, nextSiblingId, 'children' );
1234 		},
1235
1236 		/**
1237 		 * Removes a UI element definition from the content definition.
1238 		 * @param {String} id The id of the UI element definition to be
1239 		 *		removed.
1240 		 * @returns {CKEDITOR.dialog.uiElementDefinition} The element
1241 		 *		definition removed.
1242 		 * @example
1243 		 */
1244 		remove : function( id )
1245 		{
1246 			removeById( this.elements, id, 'children' );
1247 		}
1248 	};
1249
1250 	var initDragAndDrop = function( dialog )
1251 	{
1252 		var lastCoords = null,
1253 			abstractDialogCoords = null,
1254 			element = dialog.getElement().getFirst(),
1255 			magnetDistance = dialog._.editor.config.dialog_magnetDistance,
1256 			mouseMoveHandler = function( evt )
1257 			{
1258 				var dialogSize = dialog.getSize(),
1259 					viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(),
1260 					x = evt.data.$.screenX,
1261 					y = evt.data.$.screenY,
1262 					dx = x - lastCoords.x,
1263 					dy = y - lastCoords.y,
1264 					realX, realY;
1265
1266 				lastCoords = { x : x, y : y };
1267 				abstractDialogCoords.x += dx;
1268 				abstractDialogCoords.y += dy;
1269
1270 				if ( abstractDialogCoords.x + CKEDITOR.dialog._.margins[3] < magnetDistance )
1271 					realX = - CKEDITOR.dialog._.margins[3];
1272 				else if ( abstractDialogCoords.x - CKEDITOR.dialog._.margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance )
1273 					realX = viewPaneSize.width - dialogSize.width + CKEDITOR.dialog._.margins[1];
1274 				else
1275 					realX = abstractDialogCoords.x;
1276
1277 				if ( abstractDialogCoords.y + CKEDITOR.dialog._.margins[0] < magnetDistance )
1278 					realY = - CKEDITOR.dialog._.margins[0];
1279 				else if ( abstractDialogCoords.y - CKEDITOR.dialog._.margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance )
1280 					realY = viewPaneSize.height - dialogSize.height + CKEDITOR.dialog._.margins[2];
1281 				else
1282 					realY = abstractDialogCoords.y;
1283
1284 				dialog.move( realX, realY );
1285
1286 				evt.data.preventDefault();
1287 			},
1288 			mouseUpHandler = function( evt )
1289 			{
1290 				CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler );
1291 				CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler );
1292
1293 				if ( CKEDITOR.env.ie6Compat )
1294 				{
1295 					var coverDoc = new CKEDITOR.dom.document( frames( 'cke_dialog_background_iframe' ).document );
1296 					coverDoc.removeListener( 'mousemove', mouseMoveHandler );
1297 					coverDoc.removeListener( 'mouseup', mouseUpHandler );
1298 				}
1299 			};
1300
1301 		dialog.parts.title.on( 'mousedown', function( evt )
1302 				{
1303 					lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY };
1304
1305 					CKEDITOR.document.on( 'mousemove', mouseMoveHandler );
1306 					CKEDITOR.document.on( 'mouseup', mouseUpHandler );
1307 					abstractDialogCoords = dialog.getPosition();
1308
1309 					if ( CKEDITOR.env.ie6Compat )
1310 					{
1311 						var coverDoc = new CKEDITOR.dom.document( frames( 'cke_dialog_background_iframe' ).document );
1312 						coverDoc.on( 'mousemove', mouseMoveHandler );
1313 						coverDoc.on( 'mouseup', mouseUpHandler );
1314 					}
1315
1316 					evt.data.preventDefault();
1317 				}, dialog );
1318 	};
1319
1320 	var initResizeHandles = function( dialog )
1321 	{
1322 		var definition = dialog.definition,
1323 			minWidth = definition.minWidth || 0,
1324 			minHeight = definition.minHeight || 0,
1325 			resizable = definition.resizable,
1326 			topSizer = function( coords, dy )
1327 			{
1328 				coords.y += dy;
1329 			},
1330 			rightSizer = function( coords, dx )
1331 			{
1332 				coords.x2 += dx;
1333 			},
1334 			bottomSizer = function( coords, dy )
1335 			{
1336 				coords.y2 += dy;
1337 			},
1338 			leftSizer = function( coords, dx )
1339 			{
1340 				coords.x += dx;
1341 			},
1342 			lastCoords = null,
1343 			abstractDialogCoords = null,
1344 			magnetDistance = dialog._.editor.config.magnetDistance,
1345 			parts = [ 'tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br' ],
1346 			mouseDownHandler = function( evt )
1347 			{
1348 				var partName = evt.listenerData.part, size = dialog.getSize();
1349 				abstractDialogCoords = dialog.getPosition();
1350 				CKEDITOR.tools.extend( abstractDialogCoords,
1351 					{
1352 						x2 : abstractDialogCoords.x + size.width,
1353 						y2 : abstractDialogCoords.y + size.height
1354 					} );
1355 				lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY };
1356
1357 				CKEDITOR.document.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );
1358 				CKEDITOR.document.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );
1359
1360 				if ( CKEDITOR.env.ie6Compat )
1361 				{
1362 					var coverDoc = new CKEDITOR.dom.document( frames( 'cke_dialog_background_iframe' ).document );
1363 					coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );
1364 					coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );
1365 				}
1366
1367 				evt.data.preventDefault();
1368 			},
1369 			mouseMoveHandler = function( evt )
1370 			{
1371 				var x = evt.data.$.screenX,
1372 					y = evt.data.$.screenY,
1373 					dx = x - lastCoords.x,
1374 					dy = y - lastCoords.y,
1375 					viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(),
1376 					partName = evt.listenerData.part;
1377
1378 				if ( partName.search( 't' ) != -1 )
1379 					topSizer( abstractDialogCoords, dy );
1380 				if ( partName.search( 'l' ) != -1 )
1381 					leftSizer( abstractDialogCoords, dx );
1382 				if ( partName.search( 'b' ) != -1 )
1383 					bottomSizer( abstractDialogCoords, dy );
1384 				if ( partName.search( 'r' ) != -1 )
1385 					rightSizer( abstractDialogCoords, dx );
1386
1387 				lastCoords = { x : x, y : y };
1388
1389 				var realX, realY, realX2, realY2;
1390
1391 				if ( abstractDialogCoords.x + CKEDITOR.dialog._.margins[3] < magnetDistance )
1392 					realX = - CKEDITOR.dialog._.margins[3];
1393 				else if ( partName.search( 'l' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance )
1394 					realX = abstractDialogCoords.x2 - minWidth;
1395 				else
1396 					realX = abstractDialogCoords.x;
1397
1398 				if ( abstractDialogCoords.y + CKEDITOR.dialog._.margins[0] < magnetDistance )
1399 					realY = - CKEDITOR.dialog._.margins[0];
1400 				else if ( partName.search( 't' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance )
1401 					realY = abstractDialogCoords.y2 - minHeight;
1402 				else
1403 					realY = abstractDialogCoords.y;
1404
1405 				if ( abstractDialogCoords.x2 - CKEDITOR.dialog._.margins[1] > viewPaneSize.width - magnetDistance )
1406 					realX2 = viewPaneSize.width + CKEDITOR.dialog._.margins[1] ;
1407 				else if ( partName.search( 'r' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance )
1408 					realX2 = abstractDialogCoords.x + minWidth;
1409 				else
1410 					realX2 = abstractDialogCoords.x2;
1411
1412 				if ( abstractDialogCoords.y2 - CKEDITOR.dialog._.margins[2] > viewPaneSize.height - magnetDistance )
1413 					realY2= viewPaneSize.height + CKEDITOR.dialog._.margins[2] ;
1414 				else if ( partName.search( 'b' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance )
1415 					realY2 = abstractDialogCoords.y + minHeight;
1416 				else
1417 					realY2 = abstractDialogCoords.y2 ;
1418
1419 				dialog.move( realX, realY );
1420 				dialog.resize( realX2 - realX, realY2 - realY );
1421
1422 				evt.data.preventDefault();
1423 			},
1424 			mouseUpHandler = function( evt )
1425 			{
1426 				CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler );
1427 				CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler );
1428
1429 				if ( CKEDITOR.env.ie6Compat )
1430 				{
1431 					var coverDoc = new CKEDITOR.dom.document( frames( 'cke_dialog_background_iframe' ).document );
1432 					coverDoc.removeListener( 'mouseup', mouseUpHandler );
1433 					coverDoc.removeListener( 'mousemove', mouseMoveHandler );
1434 				}
1435 			};
1436
1437 		var widthTest = /[lr]/,
1438 			heightTest = /[tb]/;
1439 		for ( var i = 0 ; i < parts.length ; i++ )
1440 		{
1441 			var element = dialog.parts[ parts[i] + '_resize' ];
1442 			if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ||
1443 					resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT && widthTest.test( parts[i] ) ||
1444 			  		resizable == CKEDITOR.DIALOG_RESIZE_WIDTH && heightTest.test( parts[i] ) )
1445 			{
1446 				element.hide();
1447 				continue;
1448 			}
1449 			element.on( 'mousedown', mouseDownHandler, dialog, { part : parts[i] } );
1450 		}
1451 	};
1452
1453 	var resizeCover;
1454
1455 	var addCover = function( editor )
1456 	{
1457 		var win = CKEDITOR.document.getWindow();
1458
1459 		var html = [
1460 				'<div style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ),
1461 				'; z-index: ', editor.config.baseFloatZIndex,
1462 				'; top: 0px; left: 0px; ',
1463 				'background-color: ', editor.config.dialog_backgroundCoverColor,
1464 				'" id="cke_dialog_background_cover">'
1465 			];
1466
1467 		if ( CKEDITOR.env.ie6Compat )
1468 		{
1469 			html.push( '<iframe hidefocus="true" frameborder="0" name="cke_dialog_background_iframe" src="javascript: \'\'" ',
1470 					'style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; ',
1471 					'progid:DXImageTransform.Microsoft.Alpha(opacity=0)" ></iframe>' );
1472 		}
1473
1474 		html.push( '</div>' );
1475
1476 		var element = CKEDITOR.dom.element.createFromHtml( html.join( '' ) );
1477
1478 		var resizeFunc = function()
1479 		{
1480 			var size = win.getViewPaneSize();
1481 			element.setStyles(
1482 					{
1483 						width : size.width + 'px',
1484 						height : size.height + 'px'
1485 					});
1486 		};
1487
1488 		var scrollFunc = function()
1489 		{
1490 			var pos = win.getScrollPosition(),
1491 				cursor = CKEDITOR.dialog._.currentTop;
1492 			element.setStyles(
1493 					{
1494 						left : pos.x + 'px',
1495 						top : pos.y + 'px'
1496 					});
1497
1498 			do
1499 			{
1500 				var dialogPos = cursor.getPosition();
1501 				cursor.move( dialogPos.x, dialogPos.y );
1502 			} while( ( cursor = cursor._.parentDialog ) );
1503 		};
1504
1505 		resizeCover = resizeFunc;
1506 		win.on( 'resize', resizeFunc );
1507 		resizeFunc();
1508 		if ( CKEDITOR.env.ie6Compat )
1509 		{
1510 			// IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll.
1511 			// So we need to invent a really funny way to make it work.
1512 			var myScrollHandler = function()
1513 				{
1514 					scrollFunc();
1515 					arguments.callee.prevScrollHandler.apply( this, arguments );
1516 				};
1517 			win.$.setTimeout( function()
1518 				{
1519 					myScrollHandler.prevScrollHandler = window.onscroll || function(){};
1520 					window.onscroll = myScrollHandler;
1521 				}, 0 );
1522 			scrollFunc();
1523 		}
1524 		element.setOpacity( editor.config.dialog_backgroundCoverOpacity );
1525 		element.appendTo( CKEDITOR.document.getBody() );
1526 	};
1527
1528 	var removeCover = function()
1529 	{
1530 		var element = CKEDITOR.document.getById( 'cke_dialog_background_cover' ),
1531 			win = CKEDITOR.document.getWindow();
1532 		if ( element )
1533 		{
1534 			element.remove();
1535 			win.removeListener( 'resize', resizeCover );
1536
1537 			if ( CKEDITOR.env.ie6Compat )
1538 			{
1539 				win.$.setTimeout( function()
1540 					{
1541 						var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler;
1542 						window.onscroll = prevScrollHandler || null;
1543 					}, 0 );
1544 			}
1545 			resizeCover = null;
1546 		}
1547 	};
1548
1549 	var accessKeyProcessors = {};
1550
1551 	var accessKeyDownHandler = function( evt )
1552 	{
1553 		var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey,
1554 			alt = evt.data.$.altKey,
1555 			shift = evt.data.$.shiftKey,
1556 			key = String.fromCharCode( evt.data.$.keyCode ),
1557 			keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key];
1558
1559 		if ( !keyProcessor || !keyProcessor.length )
1560 			return;
1561
1562 		keyProcessor = keyProcessor[keyProcessor.length - 1];
1563 		keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key );
1564 		evt.data.preventDefault();
1565 	};
1566
1567 	var accessKeyUpHandler = function( evt )
1568 	{
1569 		var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey,
1570 			alt = evt.data.$.altKey,
1571 			shift = evt.data.$.shiftKey,
1572 			key = String.fromCharCode( evt.data.$.keyCode ),
1573 			keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key];
1574
1575 		if ( !keyProcessor || !keyProcessor.length )
1576 			return;
1577
1578 		keyProcessor = keyProcessor[keyProcessor.length - 1];
1579 		keyProcessor.keyup && keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key );
1580 		evt.data.preventDefault();
1581 	};
1582
1583 	var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc )
1584 	{
1585 		var procList = accessKeyProcessors[key] || ( accessKeyProcessors[key] = [] );
1586 		procList.push( {
1587 				uiElement : uiElement,
1588 				dialog : dialog,
1589 				key : key,
1590 				keyup : upFunc || uiElement.accessKeyUp,
1591 				keydown : downFunc || uiElement.accessKeyDown
1592 			} );
1593 	};
1594
1595 	var unregisterAccessKey = function( obj )
1596 	{
1597 		for ( var i in accessKeyProcessors )
1598 		{
1599 			var list = accessKeyProcessors[i];
1600 			for ( var j = list.length - 1 ; j >= 0 ; j-- )
1601 			{
1602 				if ( list[j].dialog == obj || list[j].uiElement == obj )
1603 					list.splice( j, 1 );
1604 			}
1605 			if ( list.length === 0 )
1606 				delete accessKeyProcessors[i];
1607 		}
1608 	};
1609
1610 	var tabAccessKeyUp = function( dialog, key )
1611 	{
1612 		if ( dialog._.accessKeyMap[key] )
1613 			dialog.selectPage( dialog._.accessKeyMap[key] );
1614 	};
1615
1616 	var tabAccessKeyDown = function( dialog, key )
1617 	{
1618 	};
1619
1620 	(function()
1621 	{
1622 		var decimalRegex = /^\d+(?:\.\d+)?$/,
1623 			fixLength = function( length )
1624 			{
1625 				return length + ( decimalRegex.test( length ) ? 'px' : '' );
1626 			};
1627
1628 		CKEDITOR.ui.dialog =
1629 		{
1630 			/**
1631 			 * The base class of all dialog UI elements.
1632 			 * @constructor
1633 			 * @param {CKEDITOR.dialog} dialog Parent dialog object.
1634 			 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element
1635 			 * definition. Accepted fields:
1636 			 * <ul>
1637 			 * 	<li><strong>id</strong> (Required) The id of the UI element. See {@link
1638 			 * 	CKEDITOR.dialog#getContentElement}</li>
1639 			 * 	<li><strong>type</strong> (Required) The type of the UI element. The
1640 			 * 	value to this field specifies which UI element class will be used to
1641 			 * 	generate the final widget.</li>
1642 			 * 	<li><strong>title</strong> (Optional) The popup tooltip for the UI
1643 			 * 	element.</li>
1644 			 * 	<li><strong>className</strong> (Optional) Additional CSS class names
1645 			 * 	to add to the UI element. Separated by space.</li>
1646 			 * 	<li><strong>style</strong> (Optional) Additional CSS inline styles
1647 			 * 	to add to the UI element. A semicolon (;) is required after the last
1648 			 * 	style declaration.</li>
1649 			 * 	<li><strong>accessKey</strong> (Optional) The alphanumeric access key
1650 			 * 	for this element. Access keys are automatically prefixed by CTRL.</li>
1651 			 * 	<li><strong>on*</strong> (Optional) Any UI element definition field that
1652 			 * 	starts with <em>on</em> followed immediately by a capital letter and
1653 			 * 	probably more letters is an event handler. Event handlers may be further
1654 			 * 	divided into registered event handlers and DOM event handlers. Please
1655 			 * 	refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and
1656 			 * 	{@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more
1657 			 * 	information.</li>
1658 			 * </ul>
1659 			 * @param {Array} htmlList
1660 			 * List of HTML code to be added to the dialog's content area.
1661 			 * @param {Function|String} nodeNameArg
1662 			 * A function returning a string, or a simple string for the node name for
1663 			 * the root DOM node. Default is 'div'.
1664 			 * @param {Function|Object} stylesArg
1665 			 * A function returning an object, or a simple object for CSS styles applied
1666 			 * to the DOM node. Default is empty object.
1667 			 * @param {Function|Object} attributesArg
1668 			 * A fucntion returning an object, or a simple object for attributes applied
1669 			 * to the DOM node. Default is empty object.
1670 			 * @param {Function|String} contentsArg
1671 			 * A function returning a string, or a simple string for the HTML code inside
1672 			 * the root DOM node. Default is empty string.
1673 			 * @example
1674 			 */
1675 			uiElement : function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg )
1676 			{
1677 				if (arguments.length < 4 )
1678 					return;
1679
1680 				var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div',
1681 					html = [ '<', nodeName, ' ' ],
1682 					styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {},
1683 					attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {},
1684 					innerHTML = ( contentsArg && contentsArg.call ? contentsArg( dialog, elementDefinition ) : contentsArg ) || '',
1685 					domId = this.domId = attributes.id || CKEDITOR.tools.getNextNumber() + '_uiElement',
1686 					id = this.id = elementDefinition.id,
1687 					i;
1688
1689 				// Set the id, a unique id is required for getElement() to work.
1690 				attributes.id = domId;
1691
1692 				// Set the type and definition CSS class names.
1693 				var classes = {};
1694 				if ( elementDefinition.type )
1695 					classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1;
1696 				if ( elementDefinition.className )
1697 					classes[ elementDefinition.className ] = 1;
1698 				var attributeClasses = ( attributes['class'] && attributes['class'].split ) ? attributes['class'].split( ' ' ) : [];
1699 				for ( i = 0 ; i < attributeClasses.length ; i++ )
1700 				{
1701 					if ( attributeClasses[i] )
1702 						classes[ attributeClasses[i] ] = 1;
1703 				}
1704 				var finalClasses = [];
1705 				for ( i in classes )
1706 					finalClasses.push( i );
1707 				attributes['class'] = finalClasses.join( ' ' );
1708
1709 				// Set the popup tooltop.
1710 				if ( elementDefinition.title )
1711 					attributes.title = elementDefinition.title;
1712
1713 				// Write the inline CSS styles.
1714 				var styleStr = ( elementDefinition.style || '' ).split( ';' );
1715 				for ( i in styles )
1716 					styleStr.push( i + ':' + styles[i] );
1717 				for ( i = styleStr.length - 1 ; i >= 0 ; i-- )
1718 				{
1719 					if ( styleStr[i] === '' )
1720 						styleStr.splice( i, 1 );
1721 				}
1722 				if ( styleStr.length > 0 )
1723 					attributes.style = ( attributes.style || '' ) + styleStr.join( '; ' );
1724
1725 				// Write the attributes.
1726 				for ( i in attributes )
1727 					html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ');
1728
1729 				// Write the content HTML.
1730 				html.push( '>', innerHTML, '</', nodeName, '>' );
1731
1732 				// Add contents to the parent HTML array.
1733 				htmlList.push( html.join( '' ) );
1734
1735 				( this._ || ( this._ = {} ) ).dialog = dialog;
1736
1737 				// Override isChanged if it is defined in element definition.
1738 				if ( typeof( elementDefinition.isChanged ) == 'boolean' )
1739 					this.isChanged = function(){ return elementDefinition.isChanged; };
1740 				if ( typeof( elementDefinition.isChanged ) == 'function' )
1741 					this.isChanged = elementDefinition.isChanged;
1742
1743 				// Add events.
1744 				CKEDITOR.event.implementOn( this );
1745
1746 				this.registerEvents( elementDefinition );
1747 				if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey )
1748 					registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey );
1749
1750 				// Completes this object with everything we have in the
1751 				// definition.
1752 				CKEDITOR.tools.extend( this, elementDefinition );
1753 			},
1754
1755 			/**
1756 			 * Horizontal layout box for dialog UI elements, auto-expends to available width of container.
1757 			 * @constructor
1758 			 * @extends CKEDITOR.ui.dialog.uiElement
1759 			 * @param {CKEDITOR.dialog} dialog
1760 			 * Parent dialog object.
1761 			 * @param {Array} childObjList
1762 			 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
1763 			 * container.
1764 			 * @param {Array} childHtmlList
1765 			 * Array of HTML code that correspond to the HTML output of all the
1766 			 * objects in childObjList.
1767 			 * @param {Array} htmlList
1768 			 * Array of HTML code that this element will output to.
1769 			 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
1770 			 * The element definition. Accepted fields:
1771 			 * <ul>
1772 			 * 	<li><strong>widths</strong> (Optional) The widths of child cells.</li>
1773 			 * 	<li><strong>height</strong> (Optional) The height of the layout.</li>
1774 			 * 	<li><strong>padding</strong> (Optional) The padding width inside child
1775 			 * 	 cells.</li>
1776 			 * 	<li><strong>align</strong> (Optional) The alignment of the whole layout
1777 			 * 	</li>
1778 			 * </ul>
1779 			 * @example
1780 			 */
1781 			hbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
1782 			{
1783 				if ( arguments.length < 4 )
1784 					return;
1785
1786 				this._ || ( this._ = {} );
1787
1788 				var children = this._.children = childObjList,
1789 					widths = elementDefinition && elementDefinition.widths || null,
1790 					height = elementDefinition && elementDefinition.height || null,
1791 					styles = {},
1792 					i;
1793 				/** @ignore */
1794 				var innerHTML = function()
1795 				{
1796 					var html = [ '<tbody><tr class="cke_dialog_ui_hbox">' ];
1797 					for ( i = 0 ; i < childHtmlList.length ; i++ )
1798 					{
1799 						var className = 'cke_dialog_ui_hbox_child',
1800 							styles = [];
1801 						if ( i === 0 )
1802 							className = 'cke_dialog_ui_hbox_first';
1803 						if ( i == childHtmlList.length - 1 )
1804 							className = 'cke_dialog_ui_hbox_last';
1805 						html.push( '<td class="', className, '" ' );
1806 						if ( widths )
1807 						{
1808 							if ( widths[i] )
1809 								styles.push( 'width:' + fixLength( widths[i] ) );
1810 						}
1811 						else
1812 							styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' );
1813 						if ( height )
1814 							styles.push( 'height:' + fixLength( height ) );
1815 						if ( elementDefinition && elementDefinition.padding != undefined )
1816 							styles.push( 'padding:' + fixLength( elementDefinition.padding ) );
1817 						if ( styles.length > 0 )
1818 							html.push( 'style="' + styles.join('; ') + '" ' );
1819 						html.push( '>', childHtmlList[i], '</td>' );
1820 					}
1821 					html.push( '</tr></tbody>' );
1822 					return html.join( '' );
1823 				};
1824
1825 				CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'hbox' }, htmlList, 'table', styles,
1826 						{ align : ( elementDefinition && elementDefinition.align ) ||
1827 							( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) }, innerHTML );
1828 			},
1829
1830 			/**
1831 			 * Vertical layout box for dialog UI elements.
1832 			 * @constructor
1833 			 * @extends CKEDITOR.ui.dialog.hbox
1834 			 * @param {CKEDITOR.dialog} dialog
1835 			 * Parent dialog object.
1836 			 * @param {Array} childObjList
1837 			 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
1838 			 * container.
1839 			 * @param {Array} childHtmlList
1840 			 * Array of HTML code that correspond to the HTML output of all the
1841 			 * objects in childObjList.
1842 			 * @param {Array} htmlList
1843 			 * Array of HTML code that this element will output to.
1844 			 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
1845 			 * The element definition. Accepted fields:
1846 			 * <ul>
1847 			 * 	<li><strong>width</strong> (Optional) The width of the layout.</li>
1848 			 * 	<li><strong>heights</strong> (Optional) The heights of individual cells.
1849 			 * 	</li>
1850 			 * 	<li><strong>align</strong> (Optional) The alignment of the layout.</li>
1851 			 * 	<li><strong>padding</strong> (Optional) The padding width inside child
1852 			 * 	cells.</li>
1853 			 * 	<li><strong>expand</strong> (Optional) Whether the layout should expand
1854 			 * 	vertically to fill its container.</li>
1855 			 * </ul>
1856 			 * @example
1857 			 */
1858 			vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
1859 			{
1860 				if (arguments.length < 3 )
1861 					return;
1862
1863 				this._ || ( this._ = {} );
1864
1865 				var children = this._.children = childObjList,
1866 					width = elementDefinition && elementDefinition.width || null,
1867 					heights = elementDefinition && elementDefinition.heights || null;
1868 				/** @ignore */
1869 				var innerHTML = function()
1870 				{
1871 					var html = [ '<table cellspacing="0" border="0" ' ];
1872 					html.push( 'style="' );
1873 					if ( elementDefinition && elementDefinition.expand )
1874 						html.push( 'height:100%;' );
1875 					html.push( 'width:' + fixLength( width || '100%' ), ';' );
1876 					html.push( '"' );
1877 					html.push( 'align="', CKEDITOR.tools.htmlEncode(
1878 						( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' );
1879
1880 					html.push( '><tbody>' );
1881 					for ( var i = 0 ; i < childHtmlList.length ; i++ )
1882 					{
1883 						var styles = [];
1884 						html.push( '<tr><td ' );
1885 						if ( width )
1886 							styles.push( 'width:' + fixLength( width || '100%' ) );
1887 						if ( heights )
1888 							styles.push( 'height:' + fixLength( heights[i] ) );
1889 						else if ( elementDefinition && elementDefinition.expand )
1890 							styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' );
1891 						if ( elementDefinition && elementDefinition.padding != undefined )
1892 							styles.push( 'padding:' + fixLength( elementDefinition.padding ) );
1893 						if ( styles.length > 0 )
1894 							html.push( 'style="', styles.join( '; ' ), '" ' );
1895 						html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '</td></tr>' );
1896 					}
1897 					html.push( '</tbody></table>' );
1898 					return html.join( '' );
1899 				};
1900 				CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, null, innerHTML );
1901 			}
1902 		};
1903 	})();
1904
1905 	CKEDITOR.ui.dialog.uiElement.prototype =
1906 	{
1907 		/**
1908 		 * Gets the root DOM element of this dialog UI object.
1909 		 * @returns {CKEDITOR.dom.element} Root DOM element of UI object.
1910 		 * @example
1911 		 * uiElement.getElement().hide();
1912 		 */
1913 		getElement : function()
1914 		{
1915 			return CKEDITOR.document.getById( this.domId );
1916 		},
1917
1918 		/**
1919 		 * Gets the DOM element that the user inputs values.
1920 		 * This function is used by setValue(), getValue() and focus(). It should
1921 		 * be overrided in child classes where the input element isn't the root
1922 		 * element.
1923 		 * @returns {CKEDITOR.dom.element} The element where the user input values.
1924 		 * @example
1925 		 * var rawValue = textInput.getInputElement().$.value;
1926 		 */
1927 		getInputElement : function()
1928 		{
1929 			return this.getElement();
1930 		},
1931
1932 		/**
1933 		 * Gets the parent dialog object containing this UI element.
1934 		 * @returns {CKEDITOR.dialog} Parent dialog object.
1935 		 * @example
1936 		 * var dialog = uiElement.getDialog();
1937 		 */
1938 		getDialog : function()
1939 		{
1940 			return this._.dialog;
1941 		},
1942
1943 		/**
1944 		 * Sets the value of this dialog UI object.
1945 		 * @param {Object} value The new value.
1946 		 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
1947 		 * @example
1948 		 * uiElement.setValue( 'Dingo' );
1949 		 */
1950 		setValue : function( value )
1951 		{
1952 			this.getInputElement().setValue( value );
1953 			this.fire( 'change', { value : value } );
1954 			return this;
1955 		},
1956
1957 		/**
1958 		 * Gets the current value of this dialog UI object.
1959 		 * @returns {Object} The current value.
1960 		 * @example
1961 		 * var myValue = uiElement.getValue();
1962 		 */
1963 		getValue : function()
1964 		{
1965 			return this.getInputElement().getValue();
1966 		},
1967
1968 		/**
1969 		 * Tells whether the UI object's value has changed.
1970 		 * @returns {Boolean} true if changed, false if not changed.
1971 		 * @example
1972 		 * if ( uiElement.isChanged() )
1973 		 *   confirm( 'Value changed! Continue?' );
1974 		 */
1975 		isChanged : function()
1976 		{
1977 			// Override in input classes.
1978 			return false;
1979 		},
1980
1981 		/**
1982 		 * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods.
1983 		 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
1984 		 * @example
1985 		 * focus : function()
1986 		 * {
1987 		 * 		this.selectParentTab();
1988 		 * 		// do something else.
1989 		 * }
1990 		 */
1991 		selectParentTab : function()
1992 		{
1993 			var element = this.getInputElement(),
1994 				cursor = element,
1995 				tabId;
1996 			while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 )
1997 			{ /*jsl:pass*/ }
1998
1999 			tabId = cursor.getAttribute( 'name' );
2000
2001 			this._.dialog.selectPage( tabId );
2002 			return this;
2003 		},
2004
2005 		/**
2006 		 * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page.
2007 		 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2008 		 * @example
2009 		 * uiElement.focus();
2010 		 */
2011 		focus : function()
2012 		{
2013 			this.selectParentTab().getInputElement().focus();
2014 			return this;
2015 		},
2016
2017 		/**
2018 		 * Registers the on* event handlers defined in the element definition.
2019 		 * The default behavior of this function is:
2020 		 * <ol>
2021 		 *  <li>
2022 		 *  	If the on* event is defined in the class's eventProcesors list,
2023 		 *  	then the registration is delegated to the corresponding function
2024 		 *  	in the eventProcessors list.
2025 		 *  </li>
2026 		 *  <li>
2027 		 *  	If the on* event is not defined in the eventProcessors list, then
2028 		 *  	register the event handler under the corresponding DOM event of
2029 		 *  	the UI element's input DOM element (as defined by the return value
2030 		 *  	of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}).
2031 		 *  </li>
2032 		 * </ol>
2033 		 * This function is only called at UI element instantiation, but can
2034 		 * be overridded in child classes if they require more flexibility.
2035 		 * @param {CKEDITOR.dialog.uiElementDefinition} definition The UI element
2036 		 * definition.
2037 		 * @returns {CKEDITOR.dialog.uiElement} The current UI element.
2038 		 * @example
2039 		 */
2040 		registerEvents : function( definition )
2041 		{
2042 			var regex = /^on([A-Z]\w+)/,
2043 				match;
2044
2045 			var registerDomEvent = function( uiElement, dialog, eventName, func )
2046 			{
2047 				dialog.on( 'load', function()
2048 				{
2049 					uiElement.getInputElement().on( eventName, func, uiElement );
2050 				});
2051 			};
2052
2053 			for ( var i in definition )
2054 			{
2055 				if ( !( match = i.match( regex ) ) )
2056 					continue;
2057 				if ( this.eventProcessors[i] )
2058 					this.eventProcessors[i].call( this, this._.dialog, definition[i] );
2059 				else
2060 					registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] );
2061 			}
2062
2063 			return this;
2064 		},
2065
2066 		/**
2067 		 * The event processor list used by
2068 		 * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element
2069 		 * instantiation. The default list defines three on* events:
2070 		 * <ol>
2071 		 *  <li>onLoad - Called when the element's parent dialog opens for the
2072 		 *  first time</li>
2073 		 *  <li>onShow - Called whenever the element's parent dialog opens.</li>
2074 		 *  <li>onHide - Called whenever the element's parent dialog closes.</li>
2075 		 * </ol>
2076 		 * @field
2077 		 * @type Object
2078 		 * @example
2079 		 * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick
2080 		 * // handlers in the UI element's definitions.
2081 		 * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {},
2082 		 *   CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
2083 		 *   { onClick : function( dialog, func ) { this.on( 'click', func ); } },
2084 		 *   true );
2085 		 */
2086 		eventProcessors :
2087 		{
2088 			onLoad : function( dialog, func )
2089 			{
2090 				dialog.on( 'load', func );
2091 			},
2092
2093 			onShow : function( dialog, func )
2094 			{
2095 				dialog.on( 'show', func );
2096 			},
2097
2098 			onHide : function( dialog, func )
2099 			{
2100 				dialog.on( 'hide', func );
2101 			}
2102 		},
2103
2104 		/**
2105 		 * The default handler for a UI element's access key down event, which
2106 		 * tries to put focus to the UI element.<br />
2107 		 * Can be overridded in child classes for more sophisticaed behavior.
2108 		 * @param {CKEDITOR.dialog} dialog The parent dialog object.
2109 		 * @param {String} key The key combination pressed. Since access keys
2110 		 * are defined to always include the CTRL key, its value should always
2111 		 * include a 'CTRL+' prefix.
2112 		 * @example
2113 		 */
2114 		accessKeyDown : function( dialog, key )
2115 		{
2116 			this.focus();
2117 		},
2118
2119 		/**
2120 		 * The default handler for a UI element's access key up event, which
2121 		 * does nothing.<br />
2122 		 * Can be overridded in child classes for more sophisticated behavior.
2123 		 * @param {CKEDITOR.dialog} dialog The parent dialog object.
2124 		 * @param {String} key The key combination pressed. Since access keys
2125 		 * are defined to always include the CTRL key, its value should always
2126 		 * include a 'CTRL+' prefix.
2127 		 * @example
2128 		 */
2129 		accessKeyUp : function( dialog, key )
2130 		{
2131 		},
2132
2133 		/**
2134 		 * Disables a UI element.
2135 		 * @example
2136 		 */
2137 		disable : function()
2138 		{
2139 			var element = this.getInputElement();
2140 			element.setAttribute( 'disabled', 'true' );
2141 			element.addClass( 'cke_disabled' );
2142 		},
2143
2144 		/**
2145 		 * Enables a UI element.
2146 		 * @example
2147 		 */
2148 		enable : function()
2149 		{
2150 			var element = this.getInputElement();
2151 			element.removeAttribute( 'disabled' );
2152 			element.removeClass( 'cke_disabled' );
2153 		}
2154 	};
2155
2156 	CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
2157 		/**
2158 		 * @lends CKEDITOR.ui.dialog.hbox.prototype
2159 		 */
2160 		{
2161 			/**
2162 			 * Gets a child UI element inside this container.
2163 			 * @param {Array|Number} indices An array or a single number to indicate the child's
2164 			 * position in the container's descendant tree. Omit to get all the children in an array.
2165 			 * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container
2166 			 * if no argument given, or the specified UI element if indices is given.
2167 			 * @example
2168 			 * var checkbox = hbox.getChild( [0,1] );
2169 			 * checkbox.setValue( true );
2170 			 */
2171 			getChild : function( indices )
2172 			{
2173 				// If no arguments, return a clone of the children array.
2174 				if ( arguments.length < 1 )
2175 					return this._.children.concat();
2176
2177 				// If indices isn't array, make it one.
2178 				if ( !indices.splice )
2179 					indices = [ indices ];
2180
2181 				// Retrieve the child element according to tree position.
2182 				if ( indices.length < 2 )
2183 					return this._.children[ indices[0] ];
2184 				else
2185 					return ( this._.children[ indices[0] ] && this._.children[ indices[0] ].getChild ) ?
2186 						this._.children[ indices[0] ].getChild( indices.slice( 1, indices.length ) ) :
2187 						null;
2188 			}
2189 		}, true );
2190
2191 	CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox();
2192
2193
2194
2195 	(function()
2196 	{
2197 		var commonBuilder = {
2198 			build : function( dialog, elementDefinition, output )
2199 			{
2200 				var children = elementDefinition.children,
2201 					child,
2202 					childHtmlList = [],
2203 					childObjList = [];
2204 				for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ )
2205 				{
2206 					var childHtml = [];
2207 					childHtmlList.push( childHtml );
2208 					childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) );
2209 				}
2210 				return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, childObjList, childHtmlList, output, elementDefinition );
2211 			}
2212 		};
2213
2214 		CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder );
2215 		CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder );
2216 	})();
2217
2218 	/**
2219 	 * Generic dialog command. It opens a specific dialog when executed.
2220 	 * @constructor
2221 	 * @augments CKEDITOR.commandDefinition
2222 	 * @param {string} dialogName The name of the dialog to open when executing
2223 	 *		this command.
2224 	 * @example
2225 	 * // Register the "link" command, which opens the "link" dialog.
2226 	 * editor.addCommand( 'link', <b>new CKEDITOR.dialogCommand( 'link' )</b> );
2227 	 */
2228 	CKEDITOR.dialogCommand = function( dialogName )
2229 	{
2230 		this.dialogName = dialogName;
2231 	};
2232
2233 	CKEDITOR.dialogCommand.prototype =
2234 	{
2235 		/** @ignore */
2236 		exec : function( editor )
2237 		{
2238 			editor.openDialog( this.dialogName );
2239 		}
2240 	};
2241
2242 	(function()
2243 	{
2244 		var notEmptyRegex = /^([a]|[^a])+$/,
2245 			integerRegex = /^\d*$/,
2246 			numberRegex = /^\d*(?:\.\d+)?$/;
2247
2248 		CKEDITOR.VALIDATE_OR = 1;
2249 		CKEDITOR.VALIDATE_AND = 2;
2250
2251 		CKEDITOR.dialog.validate =
2252 		{
2253 			functions : function()
2254 			{
2255 				return function()
2256 				{
2257 					/**
2258 					 * It's important for validate functions to be able to accept the value
2259 					 * as argument in addition to this.getValue(), so that it is possible to
2260 					 * combine validate functions together to make more sophisticated
2261 					 * validators.
2262 					 */
2263 					var value = this && this.getValue ? this.getValue() : arguments[0];
2264
2265 					var msg = undefined,
2266 						relation = CKEDITOR.VALIDATE_AND,
2267 						functions = [], i;
2268
2269 					for ( i = 0 ; i < arguments.length ; i++ )
2270 					{
2271 						if ( typeof( arguments[i] ) == 'function' )
2272 							functions.push( arguments[i] );
2273 						else
2274 							break;
2275 					}
2276
2277 					if ( i < arguments.length && typeof( arguments[i] ) == 'string' )
2278 					{
2279 						msg = arguments[i];
2280 						i++;
2281 					}
2282
2283 					if ( i < arguments.length && typeof( arguments[i]) == 'number' )
2284 						relation = arguments[i];
2285
2286 					var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false );
2287 					for ( i = 0 ; i < functions.length ; i++ )
2288 					{
2289 						if ( relation == CKEDITOR.VALIDATE_AND )
2290 							passed = passed && functions[i]( value );
2291 						else
2292 							passed = passed || functions[i]( value );
2293 					}
2294
2295 					if ( !passed )
2296 					{
2297 						if ( msg !== undefined )
2298 							alert( msg );
2299 						if ( this && ( this.select || this.focus ) )
2300 							( this.select || this.focus )();
2301 						return false;
2302 					}
2303
2304 					return true;
2305 				};
2306 			},
2307
2308 			regex : function( regex, msg )
2309 			{
2310 				/*
2311 				 * Can be greatly shortened by deriving from functions validator if code size
2312 				 * turns out to be more important than performance.
2313 				 */
2314 				return function()
2315 				{
2316 					var value = this && this.getValue ? this.getValue() : arguments[0];
2317 					if ( !regex.test( value ) )
2318 					{
2319 						if ( msg !== undefined )
2320 							alert( msg );
2321 						if ( this && ( this.select || this.focus ) )
2322 						{
2323 							if ( this.select )
2324 								this.select();
2325 							else
2326 								this.focus();
2327 						}
2328 						return false;
2329 					}
2330 					return true;
2331 				};
2332 			},
2333
2334 			notEmpty : function( msg )
2335 			{
2336 				return this.regex( notEmptyRegex, msg );
2337 			},
2338
2339 			integer : function( msg )
2340 			{
2341 				return this.regex( integerRegex, msg );
2342 			},
2343
2344 			'number' : function( msg )
2345 			{
2346 				return this.regex( numberRegex, msg );
2347 			},
2348
2349 			equals : function( value, msg )
2350 			{
2351 				return this.functions( function( val ){ return val == value; }, msg );
2352 			},
2353
2354 			notEqual : function( value, msg )
2355 			{
2356 				return this.functions( function( val ){ return val != value; }, msg );
2357 			}
2358 		};
2359 	})();
2360
2361 })();
2362
2363 // Extend the CKEDITOR.editor class with dialog specific functions.
2364 CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
2365 	/** @lends CKEDITOR.editor.prototype */
2366 	{
2367 		/**
2368 		 * Loads and opens a registered dialog.
2369 		 * @param {String} dialogName The registered name of the dialog.
2370 		 * @see CKEDITOR.dialog.add
2371 		 * @example
2372 		 * CKEDITOR.instances.editor1.openDialog( 'smiley' );
2373 		 * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered.
2374 		 */
2375 		openDialog : function( dialogName )
2376 		{
2377 			var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ];
2378
2379 			// If the dialogDefinition is already loaded, open it immediately.
2380 			if ( typeof dialogDefinitions == 'function' )
2381 			{
2382 				var storedDialogs = this._.storedDialogs ||
2383 					( this._.storedDialogs = {} );
2384
2385 				var dialog = storedDialogs[ dialogName ] ||
2386 					( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) );
2387
2388 				dialog.show();
2389
2390 				return dialog;
2391 			}
2392
2393 			// Not loaded? Load the .js file first.
2394 			var body = CKEDITOR.document.getBody(),
2395 				cursor = body.$.style.cursor,
2396 				me = this;
2397
2398 			body.setStyle( 'cursor', 'wait' );
2399 			CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), function()
2400 				{
2401 					me.openDialog( dialogName );
2402 					body.setStyle( 'cursor', cursor );
2403 				} );
2404
2405 			return null;
2406 		}
2407 	});
2408
2409 // Dialog related configurations.
2410
2411 /**
2412  * The color of the dialog background cover. It should be a valid CSS color
2413  * string.
2414  * @type String
2415  * @default white
2416  * @example
2417  * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)';
2418  */
2419 CKEDITOR.config.dialog_backgroundCoverColor = 'white';
2420
2421 /**
2422  * The opacity of the dialog background cover. It should be a number within the
2423  * range [0.0, 1.0].
2424  * @type Number
2425  * @default 0.5
2426  * @example
2427  * config.dialog_backgroundCoverOpacity = 0.7;
2428  */
2429 CKEDITOR.config.dialog_backgroundCoverOpacity = 0.5;
2430
2431 /**
2432  * The distance of magnetic borders used in moving and resizing dialogs,
2433  * measured in pixels.
2434  * @type Number
2435  * @default 20
2436  * @example
2437  * config.dialog_magnetDistance = 30;
2438  */
2439 CKEDITOR.config.dialog_magnetDistance = 20;
2440