JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.5
[ckeditor.git] / _source / plugins / dialog / plugin.js
index 6b5c2e4..b5aab81 100644 (file)
@@ -33,6 +33,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
 (function()\r
 {\r
+       var cssLength = CKEDITOR.tools.cssLength;\r
        function isTabVisible( tabId )\r
        {\r
                return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight;\r
@@ -68,6 +69,30 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                return null;\r
        }\r
 \r
+\r
+       function clearOrRecoverTextInputValue( container, isRecover )\r
+       {\r
+               var inputs = container.$.getElementsByTagName( 'input' );\r
+               for ( var i = 0, length = inputs.length; i < length ; i++ )\r
+               {\r
+                       var item = new CKEDITOR.dom.element( inputs[ i ] );\r
+\r
+                       if ( item.getAttribute( 'type' ).toLowerCase() == 'text' )\r
+                       {\r
+                               if ( isRecover )\r
+                               {\r
+                                       item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' );\r
+                                       item.removeCustomData( 'fake_value' );\r
+                               }\r
+                               else\r
+                               {\r
+                                       item.setCustomData( 'fake_value', item.getAttribute( 'value' ) );\r
+                                       item.setAttribute( 'value', '' );\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
        /**\r
         * This is the base class for runtime dialog objects. An instance of this\r
         * class represents a single named dialog for a single editor instance.\r
@@ -80,10 +105,19 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
        CKEDITOR.dialog = function( editor, dialogName )\r
        {\r
                // Load the dialog definition.\r
-               var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ];\r
+               var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],\r
+                       defaultDefinition = CKEDITOR.tools.clone( defaultDialogDefinition ),\r
+                       buttonsOrder = editor.config.dialog_buttonsOrder || 'OS',\r
+                       dir = editor.lang.dir;\r
+\r
+                       if ( ( buttonsOrder == 'OS' && CKEDITOR.env.mac ) ||    // The buttons in MacOS Apps are in reverse order (#4750)\r
+                               ( buttonsOrder == 'rtl' && dir == 'ltr' ) ||\r
+                               ( buttonsOrder == 'ltr' && dir == 'rtl' ) )\r
+                                       defaultDefinition.buttons.reverse();\r
+\r
 \r
                // Completes the definition with the default values.\r
-               definition = CKEDITOR.tools.extend( definition( editor ), defaultDialogDefinition );\r
+               definition = CKEDITOR.tools.extend( definition( editor ), defaultDefinition );\r
 \r
                // Clone a functionally independent copy for this dialog.\r
                definition = CKEDITOR.tools.clone( definition );\r
@@ -92,7 +126,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                // functions.\r
                definition = new definitionObject( this, definition );\r
 \r
-\r
                var doc = CKEDITOR.document;\r
 \r
                var themeBuilt = editor.theme.buildDialog( editor );\r
@@ -105,7 +138,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        name : dialogName,\r
                        contentSize : { width : 0, height : 0 },\r
                        size : { width : 0, height : 0 },\r
-                       updateSize : false,\r
                        contents : {},\r
                        buttons : {},\r
                        accessKeyMap : {},\r
@@ -154,6 +186,34 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                definition : definition\r
                        }\r
                        , editor ).definition;\r
+\r
+               var tabsToRemove = {};\r
+               // Cache tabs that should be removed.\r
+               if ( !( 'removeDialogTabs' in editor._ ) && editor.config.removeDialogTabs )\r
+               {\r
+                       var removeContents = editor.config.removeDialogTabs.split( ';' );\r
+\r
+                       for ( i = 0; i < removeContents.length; i++ )\r
+                       {\r
+                               var parts = removeContents[ i ].split( ':' );\r
+                               if ( parts.length == 2 )\r
+                               {\r
+                                       var removeDialogName = parts[ 0 ];\r
+                                       if ( !tabsToRemove[ removeDialogName ] )\r
+                                               tabsToRemove[ removeDialogName ] = [];\r
+                                       tabsToRemove[ removeDialogName ].push( parts[ 1 ] );\r
+                               }\r
+                       }\r
+                       editor._.removeDialogTabs = tabsToRemove;\r
+               }\r
+\r
+               // Remove tabs of this dialog.\r
+               if ( editor._.removeDialogTabs && ( tabsToRemove = editor._.removeDialogTabs[ dialogName ] ) )\r
+               {\r
+                       for ( i = 0; i < tabsToRemove.length; i++ )\r
+                               definition.removeContents( tabsToRemove[ i ] );\r
+               }\r
+\r
                // Initialize load, show, hide, ok and cancel events.\r
                if ( definition.onLoad )\r
                        this.on( 'load', definition.onLoad );\r
@@ -168,6 +228,9 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                {\r
                        this.on( 'ok', function( evt )\r
                                {\r
+                                       // Dialog confirm might probably introduce content changes (#5415).\r
+                                       editor.fire( 'saveSnapshot' );\r
+                                       setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 );\r
                                        if ( definition.onOk.call( this, evt ) === false )\r
                                                evt.data.hide = false;\r
                                });\r
@@ -248,6 +311,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                {\r
                                        if ( this.fire( 'cancel', { hide : true } ).hide !== false )\r
                                                this.hide();\r
+                                       evt.data.preventDefault();\r
                                }, this );\r
 \r
                // Sort focus list according to tab order definitions.\r
@@ -312,7 +376,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        if ( me != CKEDITOR.dialog._.currentTop )\r
                                return;\r
 \r
-                       var keystroke = evt.data.getKeystroke();\r
+                       var keystroke = evt.data.getKeystroke(),\r
+                               rtl = editor.lang.dir == 'rtl';\r
 \r
                        processed = 0;\r
                        if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 )\r
@@ -335,7 +400,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                                processed = 1;\r
                        }\r
-                       else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode )\r
+                       else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 )\r
                        {\r
                                // Alt-F10 puts focus into the current tab item in the tab bar.\r
                                me._.tabBarMode = true;\r
@@ -345,7 +410,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode )\r
                        {\r
                                // Arrow keys - used for changing tabs.\r
-                               nextId = ( keystroke == 37 ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );\r
+                               nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );\r
                                me.selectPage( nextId );\r
                                me._.tabs[ nextId ][ 0 ].focus();\r
                                processed = 1;\r
@@ -381,11 +446,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )\r
                                        dialogElement.on( 'keypress', focusKeyPressHandler, this );\r
 \r
-                               if ( CKEDITOR.env.ie6Compat )\r
-                               {\r
-                                       var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
-                                       coverDoc.on( 'keydown', focusKeydownHandler, this, null, 0 );\r
-                               }\r
                        } );\r
                this.on( 'hide', function()\r
                        {\r
@@ -407,7 +467,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                setupFocus();\r
 \r
                                if ( editor.config.dialog_startupFocusTab\r
-                                       && me._.tabIdList.length > 1 )\r
+                                       && me._.pageCount > 1 )\r
                                {\r
                                        me._.tabBarMode = true;\r
                                        me._.tabs[ me._.currentTabId ][ 0 ].focus();\r
@@ -472,16 +532,21 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                // Insert the tabs and contents.\r
                for ( var i = 0 ; i < definition.contents.length ; i++ )\r
-                       this.addPage( definition.contents[i] );\r
+               {\r
+                       var page = definition.contents[i];\r
+                       page && this.addPage( page );\r
+               }\r
 \r
-               this.parts['tabs'].on( 'click', function( evt )\r
+               this.parts[ 'tabs' ].on( 'click', function( evt )\r
                                {\r
                                        var target = evt.data.getTarget();\r
                                        // If we aren't inside a tab, bail out.\r
                                        if ( target.hasClass( 'cke_dialog_tab' ) )\r
                                        {\r
+                                               // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix.\r
                                                var id = target.$.id;\r
-                                               this.selectPage( id.substr( 0, id.lastIndexOf( '_' ) ) );\r
+                                               this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) );\r
+\r
                                                if ( this._.tabBarMode )\r
                                                {\r
                                                        this._.tabBarMode = false;\r
@@ -505,8 +570,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                for ( i = 0 ; i < buttons.length ; i++ )\r
                        this._.buttons[ buttons[i].id ] = buttons[i];\r
-\r
-               CKEDITOR.skins.load( editor, 'dialog' );\r
        };\r
 \r
        // Focusable interface. Use it via dialog.addFocusable.\r
@@ -543,6 +606,12 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
        CKEDITOR.dialog.prototype =\r
        {\r
+               destroy : function()\r
+               {\r
+                       this.hide();\r
+                       this._.element.remove();\r
+               },\r
+\r
                /**\r
                 * Resizes the dialog.\r
                 * @param {Number} width The width of the dialog in pixels.\r
@@ -567,7 +636,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                        }, this._.editor );\r
 \r
                                this._.contentSize = { width : width, height : height };\r
-                               this._.updateSize = true;\r
                        };\r
                })(),\r
 \r
@@ -579,15 +647,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                 */\r
                getSize : function()\r
                {\r
-                       if ( !this._.updateSize )\r
-                               return this._.size;\r
                        var element = this._.element.getFirst();\r
-                       var size = this._.size = { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0};\r
-\r
-                       // If either the offsetWidth or offsetHeight is 0, the element isn't visible.\r
-                       this._.updateSize = !size.width || !size.height;\r
-\r
-                       return size;\r
+                       return { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0};\r
                },\r
 \r
                /**\r
@@ -595,13 +656,14 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                 * @function\r
                 * @param {Number} x The target x-coordinate.\r
                 * @param {Number} y The target y-coordinate.\r
+                * @param {Boolean} save Flag indicate whether the dialog position should be remembered on next open up.\r
                 * @example\r
                 * dialogObj.move( 10, 40 );\r
                 */\r
                move : (function()\r
                {\r
                        var isFixed;\r
-                       return function( x, y )\r
+                       return function( x, y, save )\r
                        {\r
                                // The dialog may be fixed positioned or absolute positioned. Ask the\r
                                // browser what is the current situation first.\r
@@ -628,6 +690,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                                        'left'  : ( x > 0 ? x : 0 ) + 'px',\r
                                                        'top'   : ( y > 0 ? y : 0 ) + 'px'\r
                                                });\r
+\r
+                               save && ( this._.moved = 1 );\r
                        };\r
                })(),\r
 \r
@@ -659,7 +723,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) )\r
                                element.appendTo( CKEDITOR.document.getBody() );\r
                        else\r
-                               return;\r
+                               element.setStyle( 'display', 'block' );\r
 \r
                        // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8.\r
                        if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )\r
@@ -674,14 +738,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
 \r
                        // First, set the dialog to an appropriate size.\r
-                       this.resize( definition.minWidth, definition.minHeight );\r
-\r
-                       // Select the first tab by default.\r
-                       this.selectPage( this.definition.contents[0].id );\r
+                       this.resize( this._.contentSize && this._.contentSize.width || definition.minWidth,\r
+                                       this._.contentSize && this._.contentSize.height || definition.minHeight );\r
 \r
                        // Reset all inputs back to their default value.\r
                        this.reset();\r
 \r
+                       // Select the first tab by default.\r
+                       this.selectPage( this.definition.contents[0].id );\r
+\r
                        // Set z-index.\r
                        if ( CKEDITOR.dialog._.currentZIndex === null )\r
                                CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex;\r
@@ -693,7 +758,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        {\r
                                CKEDITOR.dialog._.currentTop = this;\r
                                this._.parentDialog = null;\r
-                               addCover( this._.editor );\r
+                               showCover( this._.editor );\r
 \r
                                element.on( 'keydown', accessKeyDownHandler );\r
                                element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler );\r
@@ -719,20 +784,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        // Reset the hasFocus state.\r
                        this._.hasFocus = false;\r
 \r
-                       // Rearrange the dialog to the middle of the window.\r
                        CKEDITOR.tools.setTimeout( function()\r
                                {\r
-                                       var viewSize = CKEDITOR.document.getWindow().getViewPaneSize();\r
-                                       var dialogSize = this.getSize();\r
-\r
-                                       // We're using definition size for initial position because of\r
-                                       // offten corrupted data in offsetWidth at this point. (#4084)\r
-                                       this.move( ( viewSize.width - definition.minWidth ) / 2, ( viewSize.height - dialogSize.height ) / 2 );\r
-\r
+                                       this.layout();\r
                                        this.parts.dialog.setStyle( 'visibility', '' );\r
 \r
                                        // Execute onLoad for the first show.\r
                                        this.fireOnce( 'load', {} );\r
+                                       CKEDITOR.ui.fire( 'ready', this );\r
+\r
                                        this.fire( 'show', {} );\r
                                        this._.editor.fire( 'dialogShow', this );\r
 \r
@@ -744,6 +804,19 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                },\r
 \r
                /**\r
+                * Rearrange the dialog to its previous position or the middle of the window.\r
+                * @since 3.5\r
+                */\r
+               layout : function()\r
+               {\r
+                       var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(),\r
+                                       dialogSize = this.getSize();\r
+\r
+                       this.move( this._.moved ? this._.position.x : ( viewSize.width - dialogSize.width ) / 2,\r
+                                       this._.moved ? this._.position.y : ( viewSize.height - dialogSize.height ) / 2 );\r
+               },\r
+\r
+               /**\r
                 * Executes a function for each UI element.\r
                 * @param {Function} fn Function to execute for each UI element.\r
                 * @returns {CKEDITOR.dialog} The current dialog object.\r
@@ -753,7 +826,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        for ( var i in this._.contents )\r
                        {\r
                                for ( var j in this._.contents[i] )\r
-                                       fn( this._.contents[i][j]);\r
+                                       fn( this._.contents[i][j] );\r
                        }\r
                        return this;\r
                },\r
@@ -766,7 +839,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                 */\r
                reset : (function()\r
                {\r
-                       var fn = function( widget ){ if ( widget.reset ) widget.reset(); };\r
+                       var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); };\r
                        return function(){ this.foreach( fn ); return this; };\r
                })(),\r
 \r
@@ -797,23 +870,24 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                 */\r
                hide : function()\r
                {\r
+                       if ( !this.parts.dialog.isVisible() )\r
+                               return;\r
+\r
                        this.fire( 'hide', {} );\r
                        this._.editor.fire( 'dialogHide', this );\r
-\r
-                       // Remove the dialog's element from the root document.\r
                        var element = this._.element;\r
-                       if ( !element.getParent() )\r
-                               return;\r
-\r
-                       element.remove();\r
+                       element.setStyle( 'display', 'none' );\r
                        this.parts.dialog.setStyle( 'visibility', 'hidden' );\r
-\r
                        // Unregister all access keys associated with this dialog.\r
                        unregisterAccessKey( this );\r
 \r
+                       // Close any child(top) dialogs first.\r
+                       while( CKEDITOR.dialog._.currentTop != this )\r
+                               CKEDITOR.dialog._.currentTop.hide();\r
+\r
                        // Maintain dialog ordering and remove cover if needed.\r
                        if ( !this._.parentDialog )\r
-                               removeCover();\r
+                               hideCover();\r
                        else\r
                        {\r
                                var parentElement = this._.parentDialog.getElement().getFirst();\r
@@ -846,7 +920,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        else\r
                                CKEDITOR.dialog._.currentZIndex -= 10;\r
 \r
-\r
+                       delete this._.parentDialog;\r
                        // Reset the initial values of the dialog.\r
                        this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } );\r
                },\r
@@ -868,7 +942,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                                        children : contents.elements,\r
                                                        expand : !!contents.expand,\r
                                                        padding : contents.padding,\r
-                                                       style : contents.style || 'width: 100%;' + ( CKEDITOR.env.ie6Compat ? '' : 'height: 100%;' )\r
+                                                       style : contents.style || 'width: 100%;'\r
                                                }, pageHtml );\r
 \r
                        // Create the HTML for the tab and the content block.\r
@@ -876,7 +950,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        page.setAttribute( 'role', 'tabpanel' );\r
 \r
                        var env = CKEDITOR.env;\r
-                       var tabId = contents.id + '_' + CKEDITOR.tools.getNextNumber(),\r
+                       var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(),\r
                                 tab = CKEDITOR.dom.element.createFromHtml( [\r
                                        '<a class="cke_dialog_tab"',\r
                                                ( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ),\r
@@ -893,17 +967,12 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                        page.setAttribute( 'aria-labelledby', tabId );\r
 \r
-                       // If only a single page exist, a different style is used in the central pane.\r
-                       if ( this._.pageCount === 0 )\r
-                               this.parts.dialog.addClass( 'cke_single_page' );\r
-                       else\r
-                               this.parts.dialog.removeClass( 'cke_single_page' );\r
-\r
                        // Take records for the tabs and elements created.\r
                        this._.tabs[ contents.id ] = [ tab, page ];\r
                        this._.tabIdList.push( contents.id );\r
-                       this._.pageCount++;\r
+                       !contents.hidden && this._.pageCount++;\r
                        this._.lastTab = tab;\r
+                       this.updateStyle();\r
 \r
                        var contentMap = this._.contents[ contents.id ] = {},\r
                                cursor,\r
@@ -941,6 +1010,13 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                 */\r
                selectPage : function( id )\r
                {\r
+                       if ( this._.currentTabId == id )\r
+                               return;\r
+\r
+                       // Returning true means that the event has been canceled\r
+                       if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true )\r
+                               return;\r
+\r
                        // Hide the non-selected tabs and pages.\r
                        for ( var i in this._.tabs )\r
                        {\r
@@ -954,13 +1030,35 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                page.setAttribute( 'aria-hidden', i != id );\r
                        }\r
 \r
-                       var selected = this._.tabs[id];\r
-                       selected[0].addClass( 'cke_dialog_tab_selected' );\r
-                       selected[1].show();\r
+                       var selected = this._.tabs[ id ];\r
+                       selected[ 0 ].addClass( 'cke_dialog_tab_selected' );\r
+\r
+                       // [IE] an invisible input[type='text'] will enlarge it's width\r
+                       // if it's value is long when it shows, so we clear it's value\r
+                       // before it shows and then recover it (#5649)\r
+                       if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat )\r
+                       {\r
+                               clearOrRecoverTextInputValue( selected[ 1 ] );\r
+                               selected[ 1 ].show();\r
+                               setTimeout( function()\r
+                               {\r
+                                       clearOrRecoverTextInputValue( selected[ 1 ], 1 );\r
+                               }, 0 );\r
+                       }\r
+                       else\r
+                               selected[ 1 ].show();\r
+\r
                        this._.currentTabId = id;\r
                        this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id );\r
                },\r
 \r
+               // Dialog state-specific style updates.\r
+               updateStyle : function()\r
+               {\r
+                       // If only a single page shown, a different style is used in the central pane.\r
+                       this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' );\r
+               },\r
+\r
                /**\r
                 * Hides a page's tab away from the dialog.\r
                 * @param {String} id The page's Id.\r
@@ -970,9 +1068,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                hidePage : function( id )\r
                {\r
                        var tab = this._.tabs[id] && this._.tabs[id][0];\r
-                       if ( !tab )\r
+                       if ( !tab || this._.pageCount == 1 || !tab.isVisible() )\r
                                return;\r
+                       // Switch to other tab first when we're hiding the active tab.\r
+                       else if ( id == this._.currentTabId )\r
+                               this.selectPage( getPreviousVisibleTab.call( this ) );\r
+\r
                        tab.hide();\r
+                       this._.pageCount--;\r
+                       this.updateStyle();\r
                },\r
 \r
                /**\r
@@ -987,6 +1091,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        if ( !tab )\r
                                return;\r
                        tab.show();\r
+                       this._.pageCount++;\r
+                       this.updateStyle();\r
                },\r
 \r
                /**\r
@@ -1269,7 +1375,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
        var defaultDialogDefinition =\r
        {\r
-               resizable : CKEDITOR.DIALOG_RESIZE_NONE,\r
+               resizable : CKEDITOR.DIALOG_RESIZE_BOTH,\r
                minWidth : 600,\r
                minHeight : 400,\r
                buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]\r
@@ -1361,7 +1467,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                // Transform the contents entries in contentObjects.\r
                var contents = dialogDefinition.contents;\r
                for ( var i = 0, content ; ( content = contents[i] ) ; i++ )\r
-                       contents[ i ] = new contentObject( dialog, content );\r
+                       contents[ i ] = content && new contentObject( dialog, content );\r
 \r
                CKEDITOR.tools.extend( this, dialogDefinition );\r
        };\r
@@ -1543,7 +1649,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        if ( abstractDialogCoords.x + margins[3] < magnetDistance )\r
                                realX = - margins[3];\r
                        else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance )\r
-                               realX = viewPaneSize.width - dialogSize.width + margins[1];\r
+                               realX = viewPaneSize.width - dialogSize.width + ( editor.lang.dir == 'rtl' ? 0 : margins[1] );\r
                        else\r
                                realX = abstractDialogCoords.x;\r
 \r
@@ -1554,7 +1660,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        else\r
                                realY = abstractDialogCoords.y;\r
 \r
-                       dialog.move( realX, realY );\r
+                       dialog.move( realX, realY, 1 );\r
 \r
                        evt.data.preventDefault();\r
                }\r
@@ -1566,7 +1672,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                        if ( CKEDITOR.env.ie6Compat )\r
                        {\r
-                               var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+                               var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
                                coverDoc.removeListener( 'mousemove', mouseMoveHandler );\r
                                coverDoc.removeListener( 'mouseup', mouseUpHandler );\r
                        }\r
@@ -1574,8 +1680,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                dialog.parts.title.on( 'mousedown', function( evt )\r
                        {\r
-                               dialog._.updateSize = true;\r
-\r
                                lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY };\r
 \r
                                CKEDITOR.document.on( 'mousemove', mouseMoveHandler );\r
@@ -1584,7 +1688,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
                                if ( CKEDITOR.env.ie6Compat )\r
                                {\r
-                                       var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+                                       var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
                                        coverDoc.on( 'mousemove', mouseMoveHandler );\r
                                        coverDoc.on( 'mouseup', mouseUpHandler );\r
                                }\r
@@ -1595,167 +1699,166 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
 \r
        function initResizeHandles( dialog )\r
        {\r
-               var definition = dialog.definition,\r
-                       minWidth = definition.minWidth || 0,\r
-                       minHeight = definition.minHeight || 0,\r
-                       resizable = definition.resizable,\r
-                       margins = dialog.getParentEditor().skin.margins || [ 0, 0, 0, 0 ];\r
+               var def = dialog.definition,\r
+                       resizable = def.resizable;\r
 \r
-               function topSizer( coords, dy )\r
-               {\r
-                       coords.y += dy;\r
-               }\r
+               if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE )\r
+                       return;\r
 \r
-               function rightSizer( coords, dx )\r
-               {\r
-                       coords.x2 += dx;\r
-               }\r
+               var editor = dialog.getParentEditor();\r
+               var wrapperWidth, wrapperHeight, viewSize, origin, startSize;\r
 \r
-               function bottomSizer( coords, dy )\r
+               function positionDialog( right )\r
                {\r
-                       coords.y2 += dy;\r
+                       // Maintain righthand sizing in RTL.\r
+                       if ( dialog._.moved && editor.lang.dir == 'rtl' )\r
+                       {\r
+                               var element = dialog._.element.getFirst();\r
+                               element.setStyle( 'right', right + "px" );\r
+                               element.removeStyle( 'left' );\r
+                       }\r
+                       else if ( !dialog._.moved )\r
+                               dialog.layout();\r
                }\r
 \r
-               function leftSizer( coords, dx )\r
+               var mouseDownFn = CKEDITOR.tools.addFunction( function( $event )\r
                {\r
-                       coords.x += dx;\r
-               }\r
+                       startSize = dialog.getSize();\r
 \r
-               var lastCoords = null,\r
-                       abstractDialogCoords = null,\r
-                       magnetDistance = dialog._.editor.config.magnetDistance,\r
-                       parts = [ 'tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br' ];\r
+                       // Calculate the offset between content and chrome size.\r
+                       wrapperHeight = startSize.height - dialog.parts.contents.getSize( 'height',  ! ( CKEDITOR.env.gecko || CKEDITOR.env.opera || CKEDITOR.env.ie && CKEDITOR.env.quirks ) );\r
+                       wrapperWidth = startSize.width - dialog.parts.contents.getSize( 'width', 1 );\r
 \r
-               function mouseDownHandler( evt )\r
-               {\r
-                       var partName = evt.listenerData.part, size = dialog.getSize();\r
-                       abstractDialogCoords = dialog.getPosition();\r
-                       CKEDITOR.tools.extend( abstractDialogCoords,\r
-                               {\r
-                                       x2 : abstractDialogCoords.x + size.width,\r
-                                       y2 : abstractDialogCoords.y + size.height\r
-                               } );\r
-                       lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY };\r
+                       origin = { x : $event.screenX, y : $event.screenY };\r
+\r
+                       viewSize = CKEDITOR.document.getWindow().getViewPaneSize();\r
 \r
-                       CKEDITOR.document.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );\r
-                       CKEDITOR.document.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );\r
+                       CKEDITOR.document.on( 'mousemove', mouseMoveHandler );\r
+                       CKEDITOR.document.on( 'mouseup', mouseUpHandler );\r
 \r
                        if ( CKEDITOR.env.ie6Compat )\r
                        {\r
-                               var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
-                               coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );\r
-                               coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );\r
+                               var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
+                               coverDoc.on( 'mousemove', mouseMoveHandler );\r
+                               coverDoc.on( 'mouseup', mouseUpHandler );\r
                        }\r
 \r
-                       evt.data.preventDefault();\r
-               }\r
+                       $event.preventDefault && $event.preventDefault();\r
+               });\r
+\r
+               // Prepend the grip to the dialog.\r
+               dialog.on( 'load', function()\r
+               {\r
+                       var direction = '';\r
+                       if ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH )\r
+                               direction = ' cke_resizer_horizontal';\r
+                       else if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT )\r
+                               direction = ' cke_resizer_vertical';\r
+                       var resizer = CKEDITOR.dom.element.createFromHtml( '<div class="cke_resizer' + direction + '"' +\r
+                                       ' title="' + CKEDITOR.tools.htmlEncode( editor.lang.resize ) + '"' +\r
+                                       ' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event )"></div>' );\r
+                       dialog.parts.footer.append( resizer, 1 );\r
+               });\r
+               editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } );\r
 \r
                function mouseMoveHandler( evt )\r
                {\r
-                       var x = evt.data.$.screenX,\r
-                               y = evt.data.$.screenY,\r
-                               dx = x - lastCoords.x,\r
-                               dy = y - lastCoords.y,\r
-                               viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(),\r
-                               partName = evt.listenerData.part;\r
+                       var rtl = editor.lang.dir == 'rtl',\r
+                               dx = ( evt.data.$.screenX - origin.x ) * ( rtl ? -1 : 1 ),\r
+                               dy = evt.data.$.screenY - origin.y,\r
+                               width = startSize.width,\r
+                               height = startSize.height,\r
+                               internalWidth = width + dx * ( dialog._.moved ? 1 : 2 ),\r
+                               internalHeight = height + dy * ( dialog._.moved ? 1 : 2 ),\r
+                               element = dialog._.element.getFirst(),\r
+                               right = rtl && element.getComputedStyle( 'right' ),\r
+                               position = dialog.getPosition();\r
 \r
-                       if ( partName.search( 't' ) != -1 )\r
-                               topSizer( abstractDialogCoords, dy );\r
-                       if ( partName.search( 'l' ) != -1 )\r
-                               leftSizer( abstractDialogCoords, dx );\r
-                       if ( partName.search( 'b' ) != -1 )\r
-                               bottomSizer( abstractDialogCoords, dy );\r
-                       if ( partName.search( 'r' ) != -1 )\r
-                               rightSizer( abstractDialogCoords, dx );\r
+                       // IE might return "auto", we need exact position.\r
+                       if ( right )\r
+                               right = right == 'auto' ? viewSize.width - ( position.x || 0 ) - element.getSize( 'width' ) : parseInt( right, 10 );\r
 \r
-                       lastCoords = { x : x, y : y };\r
+                       if ( position.y + internalHeight > viewSize.height )\r
+                               internalHeight = viewSize.height - position.y;\r
 \r
-                       var realX, realY, realX2, realY2;\r
+                       if ( ( rtl ? right : position.x ) + internalWidth > viewSize.width )\r
+                               internalWidth = viewSize.width - ( rtl ? right : position.x );\r
 \r
-                       if ( abstractDialogCoords.x + margins[3] < magnetDistance )\r
-                               realX = - margins[3];\r
-                       else if ( partName.search( 'l' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance )\r
-                               realX = abstractDialogCoords.x2 - minWidth;\r
-                       else\r
-                               realX = abstractDialogCoords.x;\r
+                       // Make sure the dialog will not be resized to the wrong side when it's in the leftmost position for RTL.\r
+                       if ( ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) && !( rtl && dx > 0 && !position.x ) )\r
+                               width = Math.max( def.minWidth || 0, internalWidth - wrapperWidth );\r
 \r
-                       if ( abstractDialogCoords.y + margins[0] < magnetDistance )\r
-                               realY = - margins[0];\r
-                       else if ( partName.search( 't' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance )\r
-                               realY = abstractDialogCoords.y2 - minHeight;\r
-                       else\r
-                               realY = abstractDialogCoords.y;\r
+                       if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT || resizable == CKEDITOR.DIALOG_RESIZE_BOTH )\r
+                               height = Math.max( def.minHeight || 0, internalHeight - wrapperHeight );\r
 \r
-                       if ( abstractDialogCoords.x2 - margins[1] > viewPaneSize.width - magnetDistance )\r
-                               realX2 = viewPaneSize.width + margins[1] ;\r
-                       else if ( partName.search( 'r' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance )\r
-                               realX2 = abstractDialogCoords.x + minWidth;\r
-                       else\r
-                               realX2 = abstractDialogCoords.x2;\r
-\r
-                       if ( abstractDialogCoords.y2 - margins[2] > viewPaneSize.height - magnetDistance )\r
-                               realY2= viewPaneSize.height + margins[2] ;\r
-                       else if ( partName.search( 'b' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance )\r
-                               realY2 = abstractDialogCoords.y + minHeight;\r
-                       else\r
-                               realY2 = abstractDialogCoords.y2 ;\r
-\r
-                       dialog.move( realX, realY );\r
-                       dialog.resize( realX2 - realX, realY2 - realY );\r
+                       dialog.resize( width, height );\r
+                       // The right property might get broken during resizing, so computing it before the resizing.\r
+                       positionDialog( right );\r
 \r
                        evt.data.preventDefault();\r
                }\r
 \r
-               function mouseUpHandler( evt )\r
+               function mouseUpHandler()\r
                {\r
                        CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler );\r
                        CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler );\r
 \r
                        if ( CKEDITOR.env.ie6Compat )\r
                        {\r
-                               var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+                               var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
                                coverDoc.removeListener( 'mouseup', mouseUpHandler );\r
                                coverDoc.removeListener( 'mousemove', mouseMoveHandler );\r
                        }\r
-               }\r
 \r
-// TODO : Simplify the resize logic, having just a single resize grip <div>.\r
-//             var widthTest = /[lr]/,\r
-//                     heightTest = /[tb]/;\r
-//             for ( var i = 0 ; i < parts.length ; i++ )\r
-//             {\r
-//                     var element = dialog.parts[ parts[i] + '_resize' ];\r
-//                     if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ||\r
-//                                     resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT && widthTest.test( parts[i] ) ||\r
-//                                     resizable == CKEDITOR.DIALOG_RESIZE_WIDTH && heightTest.test( parts[i] ) )\r
-//                     {\r
-//                             element.hide();\r
-//                             continue;\r
-//                     }\r
-//                     element.on( 'mousedown', mouseDownHandler, dialog, { part : parts[i] } );\r
-//             }\r
+                       // Switch back to use the left property, if RTL is used.\r
+                       if ( editor.lang.dir == 'rtl' )\r
+                       {\r
+                               var element = dialog._.element.getFirst(),\r
+                                       left = element.getComputedStyle( 'left' );\r
+\r
+                               // IE might return "auto", we need exact position.\r
+                               if ( left == 'auto' )\r
+                                       left = viewSize.width - parseInt( element.getStyle( 'right' ), 10 ) - dialog.getSize().width;\r
+                               else\r
+                                       left = parseInt( left, 10 );\r
+\r
+                               element.removeStyle( 'right' );\r
+                               // Make sure the left property gets applied, even if it is the same as previously.\r
+                               dialog._.position.x += 1;\r
+                               dialog.move( left, dialog._.position.y );\r
+                       }\r
+               }\r
        }\r
 \r
        var resizeCover;\r
-       var coverElement;\r
+       // Caching resuable covers and allowing only one cover\r
+       // on screen.\r
+       var covers = {},\r
+               currentCover;\r
 \r
-       var addCover = function( editor )\r
+       function showCover( editor )\r
        {\r
                var win = CKEDITOR.document.getWindow();\r
+               var config = editor.config,\r
+                       backgroundColorStyle = config.dialog_backgroundCoverColor || 'white',\r
+                       backgroundCoverOpacity = config.dialog_backgroundCoverOpacity,\r
+                       baseFloatZIndex = config.baseFloatZIndex,\r
+                       coverKey = CKEDITOR.tools.genKey(\r
+                                       backgroundColorStyle,\r
+                                       backgroundCoverOpacity,\r
+                                       baseFloatZIndex ),\r
+                       coverElement = covers[ coverKey ];\r
 \r
                if ( !coverElement )\r
                {\r
-                       var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white';\r
-\r
                        var html = [\r
                                        '<div style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ),\r
-                                       '; z-index: ', editor.config.baseFloatZIndex,\r
+                                       '; z-index: ', baseFloatZIndex,\r
                                        '; top: 0px; left: 0px; ',\r
                                        ( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ),\r
-                                       '" id="cke_dialog_background_cover">'\r
+                                       '" class="cke_dialog_background_cover">'\r
                                ];\r
 \r
-\r
                        if ( CKEDITOR.env.ie6Compat )\r
                        {\r
                                // Support for custom document.domain in IE.\r
@@ -1791,14 +1894,19 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                        html.push( '</div>' );\r
 \r
                        coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) );\r
-               }\r
+                       coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 );\r
 \r
-               var element = coverElement;\r
+                       coverElement.appendTo( CKEDITOR.document.getBody() );\r
+                       covers[ coverKey ] = coverElement;\r
+               }\r
+               else\r
+                       coverElement.   show();\r
 \r
+               currentCover = coverElement;\r
                var resizeFunc = function()\r
                {\r
                        var size = win.getViewPaneSize();\r
-                       element.setStyles(\r
+                       coverElement.setStyles(\r
                                {\r
                                        width : size.width + 'px',\r
                                        height : size.height + 'px'\r
@@ -1809,7 +1917,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                {\r
                        var pos = win.getScrollPosition(),\r
                                cursor = CKEDITOR.dialog._.currentTop;\r
-                       element.setStyles(\r
+                       coverElement.setStyles(\r
                                        {\r
                                                left : pos.x + 'px',\r
                                                top : pos.y + 'px'\r
@@ -1841,20 +1949,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                }, 0 );\r
                        scrollFunc();\r
                }\r
+       }\r
 \r
-               var opacity = editor.config.dialog_backgroundCoverOpacity;\r
-               element.setOpacity( typeof opacity != 'undefined' ? opacity : 0.5 );\r
-\r
-               element.appendTo( CKEDITOR.document.getBody() );\r
-       };\r
-\r
-       var removeCover = function()\r
+       function hideCover()\r
        {\r
-               if ( !coverElement )\r
+               if ( !currentCover )\r
                        return;\r
 \r
                var win = CKEDITOR.document.getWindow();\r
-               coverElement.remove();\r
+               currentCover.hide();\r
                win.removeListener( 'resize', resizeCover );\r
 \r
                if ( CKEDITOR.env.ie6Compat )\r
@@ -1866,7 +1969,14 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                }, 0 );\r
                }\r
                resizeCover = null;\r
-       };\r
+       }\r
+\r
+       function removeCovers()\r
+       {\r
+               for ( var coverId in covers )\r
+                       covers[ coverId ].remove();\r
+               covers = {};\r
+       }\r
 \r
        var accessKeyProcessors = {};\r
 \r
@@ -2011,7 +2121,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                        styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {},\r
                                        attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {},\r
                                        innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '',\r
-                                       domId = this.domId = attributes.id || CKEDITOR.tools.getNextNumber() + '_uiElement',\r
+                                       domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement',\r
                                        id = this.id = elementDefinition.id,\r
                                        i;\r
 \r
@@ -2163,14 +2273,14 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                                if ( widths )\r
                                                {\r
                                                        if ( widths[i] )\r
-                                                               styles.push( 'width:' + CKEDITOR.tools.cssLength( widths[i] ) );\r
+                                                               styles.push( 'width:' + cssLength( widths[i] ) );\r
                                                }\r
                                                else\r
                                                        styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' );\r
                                                if ( height )\r
-                                                       styles.push( 'height:' + CKEDITOR.tools.cssLength( height ) );\r
+                                                       styles.push( 'height:' + cssLength( height ) );\r
                                                if ( elementDefinition && elementDefinition.padding != undefined )\r
-                                                       styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) );\r
+                                                       styles.push( 'padding:' + cssLength( elementDefinition.padding ) );\r
                                                if ( styles.length > 0 )\r
                                                        html.push( 'style="' + styles.join('; ') + '" ' );\r
                                                html.push( '>', childHtmlList[i], '</td>' );\r
@@ -2223,7 +2333,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                         */\r
                        vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )\r
                        {\r
-                               if (arguments.length < 3 )\r
+                               if ( arguments.length < 3 )\r
                                        return;\r
 \r
                                this._ || ( this._ = {} );\r
@@ -2238,7 +2348,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                        html.push( 'style="' );\r
                                        if ( elementDefinition && elementDefinition.expand )\r
                                                html.push( 'height:100%;' );\r
-                                       html.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ), ';' );\r
+                                       html.push( 'width:' + cssLength( width || '100%' ), ';' );\r
                                        html.push( '"' );\r
                                        html.push( 'align="', CKEDITOR.tools.htmlEncode(\r
                                                ( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' );\r
@@ -2249,13 +2359,13 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                                var styles = [];\r
                                                html.push( '<tr><td role="presentation" ' );\r
                                                if ( width )\r
-                                                       styles.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ) );\r
+                                                       styles.push( 'width:' + cssLength( width || '100%' ) );\r
                                                if ( heights )\r
-                                                       styles.push( 'height:' + CKEDITOR.tools.cssLength( heights[i] ) );\r
+                                                       styles.push( 'height:' + cssLength( heights[i] ) );\r
                                                else if ( elementDefinition && elementDefinition.expand )\r
                                                        styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' );\r
                                                if ( elementDefinition && elementDefinition.padding != undefined )\r
-                                                       styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) );\r
+                                                       styles.push( 'padding:' + cssLength( elementDefinition.padding ) );\r
                                                if ( styles.length > 0 )\r
                                                        html.push( 'style="', styles.join( '; ' ), '" ' );\r
                                                html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '</td></tr>' );\r
@@ -2309,14 +2419,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                /**\r
                 * Sets the value of this dialog UI object.\r
                 * @param {Object} value The new value.\r
+                * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.\r
                 * @returns {CKEDITOR.dialog.uiElement} The current UI element.\r
                 * @example\r
                 * uiElement.setValue( 'Dingo' );\r
                 */\r
-               setValue : function( value )\r
+               setValue : function( value, noChangeEvent )\r
                {\r
                        this.getInputElement().setValue( value );\r
-                       this.fire( 'change', { value : value } );\r
+                       !noChangeEvent && this.fire( 'change', { value : value } );\r
                        return this;\r
                },\r
 \r
@@ -2646,7 +2757,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                // undo support should dedicate to specific dialog implementation.\r
                canUndo: false,\r
 \r
-               editorFocus : CKEDITOR.env.ie\r
+               editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit\r
        };\r
 \r
        (function()\r
@@ -2766,6 +2877,24 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
                                return this.functions( function( val ){ return val != value; }, msg );\r
                        }\r
                };\r
+\r
+       CKEDITOR.on( 'instanceDestroyed', function( evt )\r
+       {\r
+               // Remove dialog cover on last instance destroy.\r
+               if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) )\r
+               {\r
+                       var currentTopDialog;\r
+                       while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) )\r
+                               currentTopDialog.hide();\r
+                       removeCovers();\r
+               }\r
+\r
+               var dialogs = evt.editor._.storedDialogs;\r
+               for ( var name in dialogs )\r
+                       dialogs[ name ].destroy();\r
+\r
+       });\r
+\r
        })();\r
 })();\r
 \r
@@ -2784,10 +2913,11 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
                 */\r
                openDialog : function( dialogName, callback )\r
                {\r
-                       var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ];\r
+                       var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],\r
+                                       dialogSkin = this.skin.dialog;\r
 \r
                        // If the dialogDefinition is already loaded, open it immediately.\r
-                       if ( typeof dialogDefinitions == 'function' )\r
+                       if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded )\r
                        {\r
                                var storedDialogs = this._.storedDialogs ||\r
                                        ( this._.storedDialogs = {} );\r
@@ -2809,14 +2939,31 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
                                me = this;\r
 \r
                        body.setStyle( 'cursor', 'wait' );\r
-                       CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), function()\r
-                               {\r
-                                       // In case of plugin error, mark it as loading failed.\r
-                                       if ( typeof CKEDITOR.dialog._.dialogDefinitions[ dialogName ] != 'function' )\r
-                                                       CKEDITOR.dialog._.dialogDefinitions[ dialogName ] =  'failed';\r
-                                       me.openDialog( dialogName, callback );\r
-                                       body.setStyle( 'cursor', cursor );\r
-                               } );\r
+\r
+                       function onDialogFileLoaded( success )\r
+                       {\r
+                               var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],\r
+                                               skin = me.skin.dialog;\r
+\r
+                               // Check if both skin part and definition is loaded.\r
+                               if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' )\r
+                                       return;\r
+\r
+                               // In case of plugin error, mark it as loading failed.\r
+                               if ( typeof dialogDefinition != 'function' )\r
+                                       CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed';\r
+\r
+                               me.openDialog( dialogName, callback );\r
+                               body.setStyle( 'cursor', cursor );\r
+                       }\r
+\r
+                       if ( typeof dialogDefinitions == 'string' )\r
+                       {\r
+                               var loadDefinition = 1;\r
+                               CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded );\r
+                       }\r
+\r
+                       CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded );\r
 \r
                        return null;\r
                }\r
@@ -2869,6 +3016,34 @@ CKEDITOR.plugins.add( 'dialog',
  */\r
 \r
 /**\r
+ * The guildeline to follow when generating the dialog buttons. There are 3 possible options:\r
+ * <ul>\r
+ *     <li>'OS' - the buttons will be displayed in the default order of the user's OS;</li>\r
+ *     <li>'ltr' - for Left-To-Right order;</li>\r
+ *     <li>'rtl' - for Right-To-Left order.</li>\r
+ * </ul>\r
+ * @name CKEDITOR.config.dialog_buttonsOrder\r
+ * @type String\r
+ * @default 'OS'\r
+ * @since 3.5\r
+ * @example\r
+ * config.dialog_buttonsOrder = 'rtl';\r
+ */\r
+\r
+/**\r
+ * The dialog contents to removed. It's a string composed by dialog name and tab name with a colon between them.\r
+ * Separate each pair with semicolon (see example).\r
+ * <b>Note: All names are case-sensitive.</b>\r
+ * <b>Note: Be cautious when specifying dialog tabs that are mandatory, like "info", dialog functionality might be broken because of this!<b>\r
+ * @name CKEDITOR.config.removeDialogTabs\r
+ * @type String\r
+ * @since 3.5\r
+ * @default ''\r
+ * @example\r
+ * config.removeDialogTabs = 'flash:advanced;image:Link';\r
+ */\r
+\r
+/**\r
  * Fired when a dialog definition is about to be used to create a dialog into\r
  * an editor instance. This event makes it possible to customize the definition\r
  * before creating it.\r
@@ -2882,3 +3057,11 @@ CKEDITOR.plugins.add( 'dialog',
  * @param {CKEDITOR.editor} editor The editor instance that will use the\r
  *             dialog.\r
  */\r
+\r
+/**\r
+ * Fired when a tab is going to be selected in a dialog\r
+ * @name dialog#selectPage\r
+ * @event\r
+ * @param String page The id of the page that it's gonna be selected.\r
+ * @param String currentPage The id of the current page.\r
+ */\r