JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.1
[ckeditor.git] / _source / plugins / wysiwygarea / plugin.js
index ab06245..c2a8b15 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
 For licensing, see LICENSE.html or http://ckeditor.com/license\r
 */\r
 \r
@@ -10,18 +10,18 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
 (function()\r
 {\r
-       /**\r
-        * List of elements in which has no way to move editing focus outside.\r
-        */\r
+       // List of elements in which has no way to move editing focus outside.\r
        var nonExitableElementNames = { table:1,pre:1 };\r
+\r
        // Matching an empty paragraph at the end of document.\r
-       var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>|&nbsp;|&#160;)\s*(:?<\/\1>)?\s*$/gi;\r
+       var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>|&nbsp;|\u00A0|&#160;)?\s*(:?<\/\1>)?\s*(?=$|<\/body>)/gi;\r
 \r
        function onInsertHtml( evt )\r
        {\r
                if ( this.mode == 'wysiwyg' )\r
                {\r
                        this.focus();\r
+                       this.fire( 'saveSnapshot' );\r
 \r
                        var selection = this.getSelection(),\r
                                data = evt.data;\r
@@ -46,6 +46,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        }\r
                        else\r
                                this.document.$.execCommand( 'inserthtml', false, data );\r
+\r
+                       CKEDITOR.tools.setTimeout( function()\r
+                               {\r
+                                       this.fire( 'saveSnapshot' );\r
+                               }, 0, this );\r
                }\r
        }\r
 \r
@@ -88,9 +93,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        && ( dtd = CKEDITOR.dtd[ current.getName() ] )\r
                                                        && !( dtd && dtd [ elementName ] ) )\r
                                        {\r
+                                               // Split up inline elements.\r
+                                               if ( current.getName() in CKEDITOR.dtd.span )\r
+                                                       range.splitElement( current );\r
                                                // If we're in an empty block which indicate a new paragraph,\r
                                                // simply replace it with the inserting block.(#3664)\r
-                                               if ( range.checkStartOfBlock()\r
+                                               else if ( range.checkStartOfBlock()\r
                                                         && range.checkEndOfBlock() )\r
                                                {\r
                                                        range.setStartBefore( current );\r
@@ -139,6 +147,20 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        setTimeout( function(){ editor.resetDirty(); } );\r
        }\r
 \r
+       var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ),\r
+               isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );\r
+\r
+       function isNotEmpty( node )\r
+       {\r
+               return isNotWhitespace( node ) && isNotBookmark( node );\r
+       }\r
+\r
+       function isNbsp( node )\r
+       {\r
+               return node.type == CKEDITOR.NODE_TEXT\r
+                          && CKEDITOR.tools.trim( node.getText() ).match( /^(?:&nbsp;|\xa0)$/ );\r
+       }\r
+\r
        /**\r
         *  Auto-fixing block-less content by wrapping paragraph (#3190), prevent\r
         *  non-exitable-block by padding extra br.(#3189)\r
@@ -161,61 +183,52 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                         && !path.block )\r
                {\r
                        restoreDirty( editor );\r
-                       var bms = selection.createBookmarks(),\r
-                               fixedBlock = range.fixBlock( true,\r
+                       var fixedBlock = range.fixBlock( true,\r
                                        editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p'  );\r
 \r
-                       // For IE, we'll be removing any bogus br ( introduce by fixing body )\r
-                       // right now to prevent it introducing visual line break.\r
+                       // For IE, we should remove any filler node which was introduced before.\r
                        if ( CKEDITOR.env.ie )\r
                        {\r
-                               var brNodeList = fixedBlock.getElementsByTag( 'br' ), brNode;\r
-                               for ( var i = 0 ; i < brNodeList.count() ; i++ )\r
-                               {\r
-                                       if( ( brNode = brNodeList.getItem( i ) ) && brNode.hasAttribute( '_cke_bogus' ) )\r
-                                               brNode.remove();\r
-                               }\r
+                               var first = fixedBlock.getFirst( isNotEmpty );\r
+                               first && isNbsp( first ) && first.remove();\r
                        }\r
 \r
-                       selection.selectBookmarks( bms );\r
-\r
-                       // If the fixed block is blank and is already followed by a exitable\r
-                       // block, we should drop it and move to the exist block(#3684).\r
-                       var children = fixedBlock.getChildren(),\r
-                               count = children.count(),\r
-                               firstChild,\r
-                               whitespaceGuard = CKEDITOR.dom.walker.whitespaces( true ),\r
-                               previousElement = fixedBlock.getPrevious( whitespaceGuard ),\r
-                               nextElement = fixedBlock.getNext( whitespaceGuard ),\r
-                               enterBlock;\r
-                       if ( previousElement && previousElement.getName\r
-                                && !( previousElement.getName() in nonExitableElementNames ) )\r
-                               enterBlock = previousElement;\r
-                       else if ( nextElement && nextElement.getName\r
-                                         && !( nextElement.getName() in nonExitableElementNames ) )\r
-                               enterBlock = nextElement;\r
-\r
-                       // Not all blocks are editable, e.g. <hr />, further checking it.(#3994)\r
-                       if( ( !count\r
-                                 || ( firstChild = children.getItem( 0 ) ) && firstChild.is && firstChild.is( 'br' ) )\r
-                               && enterBlock\r
-                               && range.moveToElementEditStart( enterBlock ) )\r
+                       // If the fixed block is blank and already followed by a exitable\r
+                       // block, we should revert the fix. (#3684)\r
+                       if( fixedBlock.getOuterHtml().match( emptyParagraphRegexp ) )\r
                        {\r
-                               fixedBlock.remove();\r
-                               range.select();\r
+                               var previousElement = fixedBlock.getPrevious( isNotWhitespace ),\r
+                                       nextElement = fixedBlock.getNext( isNotWhitespace );\r
+\r
+\r
+                               if ( previousElement && previousElement.getName\r
+                                        && !( previousElement.getName() in nonExitableElementNames )\r
+                                        && range.moveToElementEditStart( previousElement )\r
+                                        || nextElement && nextElement.getName\r
+                                          && !( nextElement.getName() in nonExitableElementNames )\r
+                                          && range.moveToElementEditStart( nextElement ) )\r
+                               {\r
+                                       fixedBlock.remove();\r
+                               }\r
                        }\r
+\r
+                       range.select();\r
+                       // Notify non-IE that selection has changed.\r
+                       if( !CKEDITOR.env.ie )\r
+                               editor.selectionChange();\r
                }\r
 \r
-               // Inserting the padding-br before body if it's preceded by an\r
-               // unexitable block.\r
+               // All browsers are incapable to moving cursor out of certain non-exitable\r
+               // blocks (e.g. table, list, pre) at the end of document, make this happen by\r
+               // place a bogus node there, which would be later removed by dataprocessor.\r
                var lastNode = body.getLast( CKEDITOR.dom.walker.whitespaces( true ) );\r
                if ( lastNode && lastNode.getName && ( lastNode.getName() in nonExitableElementNames ) )\r
                {\r
                        restoreDirty( editor );\r
-                       var paddingBlock = editor.document.createElement(\r
-                                       ( CKEDITOR.env.ie && enterMode != CKEDITOR.ENTER_BR ) ?\r
-                                               '<br _cke_bogus="true" />' : 'br' );\r
-                       body.append( paddingBlock );\r
+                       if( !CKEDITOR.env.ie )\r
+                               body.appendBogus();\r
+                       else\r
+                               body.append( editor.document.createText( '\xa0' ) );\r
                }\r
        }\r
 \r
@@ -242,7 +255,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        var isCustomDomain = CKEDITOR.env.isCustomDomain();\r
 \r
                                        // Creates the iframe that holds the editable document.\r
-                                       var createIFrame = function()\r
+                                       var createIFrame = function( data )\r
                                        {\r
                                                if ( iframe )\r
                                                        iframe.remove();\r
@@ -250,46 +263,41 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        fieldset.remove();\r
 \r
                                                frameLoaded = 0;\r
-                                               // The document domain must be set within the src\r
-                                               // attribute;\r
-                                               // Defer the script execution until iframe\r
-                                               // has been added to main window, this is needed for some\r
-                                               // browsers which will begin to load the frame content\r
-                                               // prior to it's presentation in DOM.(#3894)\r
-                                               var src = 'void( '\r
-                                                               + ( CKEDITOR.env.gecko ? 'setTimeout' : '' ) + '( function(){' +\r
-                                                               'document.open();' +\r
-                                                               ( CKEDITOR.env.ie && isCustomDomain ? 'document.domain="' + document.domain + '";' : '' ) +\r
-                                                               'document.write( window.parent[ "_cke_htmlToLoad_' + editor.name + '" ] );' +\r
-                                                               'document.close();' +\r
-                                                               'window.parent[ "_cke_htmlToLoad_' + editor.name + '" ] = null;' +\r
-                                                               '}'\r
-                                                               + ( CKEDITOR.env.gecko ? ', 0 )' : ')()' )\r
-                                                               + ' )';\r
-\r
-                                               // Loading via src attribute does not work in Opera.\r
-                                               if ( CKEDITOR.env.opera )\r
-                                                       src = 'void(0);';\r
 \r
                                                iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' +\r
-                                                               ' style="width:100%;height:100%"' +\r
-                                                               ' frameBorder="0"' +\r
-                                                               ' tabIndex="-1"' +\r
-                                                               ' allowTransparency="true"' +\r
-                                                               ' src="javascript:' + encodeURIComponent( src ) + '"' +\r
-                                                               '></iframe>' );\r
+                                                       ' style="width:100%;height:100%"' +\r
+                                                       ' frameBorder="0"' +\r
+                                                       // Support for custom document.domain in IE.\r
+                                                       ( isCustomDomain ?\r
+                                                               ' src="javascript:void((function(){' +\r
+                                                                       'document.open();' +\r
+                                                                       'document.domain=\'' + document.domain + '\';' +\r
+                                                                       'document.close();' +\r
+                                                               '})())"' : '' ) +\r
+                                                       ' tabIndex="-1"' +\r
+                                                       ' allowTransparency="true"' +\r
+                                                       '></iframe>' );\r
+\r
+                                               // Register onLoad event for iframe element, which\r
+                                               // will fill it with content and set custom domain.\r
+                                               iframe.on( 'load', function( e )\r
+                                               {\r
+                                                       e.removeListener();\r
+                                                       var doc = iframe.getFrameDocument().$;\r
+\r
+                                                       // Custom domain handling is needed after each document.open().\r
+                                                       doc.open();\r
+                                                       if ( isCustomDomain )\r
+                                                               doc.domain = document.domain;\r
+                                                       doc.write( data );\r
+                                                       doc.close();\r
+\r
+                                               } );\r
 \r
                                                var accTitle = editor.lang.editorTitle.replace( '%1', editor.name );\r
 \r
                                                if ( CKEDITOR.env.gecko )\r
                                                {\r
-                                                       // Double checking the iframe will be loaded properly(#4058).\r
-                                                       iframe.on( 'load', function( ev )\r
-                                                       {\r
-                                                               ev.removeListener();\r
-                                                               contentDomReady( iframe.$.contentWindow );\r
-                                                       } );\r
-\r
                                                        // Accessibility attributes for Firefox.\r
                                                        mainElement.setAttributes(\r
                                                                {\r
@@ -329,16 +337,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        mainElement.append( iframe );\r
                                        };\r
 \r
-                                       // The script that is appended to the data being loaded. It\r
-                                       // enables editing, and makes some\r
+                                       // The script that launches the bootstrap logic on 'domReady', so the document\r
+                                       // is fully editable even before the editing iframe is fully loaded (#4455).\r
                                        var activationScript =\r
                                                '<script id="cke_actscrpt" type="text/javascript">' +\r
-                                                       'window.onload = function()' +\r
-                                                       '{' +\r
-                                                               // Call the temporary function for the editing\r
-                                                               // boostrap.\r
-                                                               'window.parent.CKEDITOR._["contentDomReady' + editor.name + '"]( window );' +\r
-                                                       '}' +\r
+                                                       'window.parent.CKEDITOR._["contentDomReady' + editor.name + '"]( window );' +\r
                                                '</script>';\r
 \r
                                        // Editing area bootstrap code.\r
@@ -411,6 +414,22 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        } );\r
                                                }\r
 \r
+                                               // IE standard compliant in editing frame doesn't focus the editor when\r
+                                               // clicking outside actual content, manually apply the focus. (#1659)\r
+                                               if( CKEDITOR.env.ie\r
+                                                       && domDocument.$.compatMode == 'CSS1Compat' )\r
+                                               {\r
+                                                       var htmlElement = domDocument.getDocumentElement();\r
+                                                       htmlElement.on( 'mousedown', function( evt )\r
+                                                       {\r
+                                                               // Setting focus directly on editor doesn't work, we\r
+                                                               // have to use here a temporary element to 'redirect'\r
+                                                               // the focus.\r
+                                                               if ( evt.data.getTarget().equals( htmlElement ) )\r
+                                                                       ieFocusGrabber.focus();\r
+                                                       } );\r
+                                               }\r
+\r
                                                var focusTarget = ( CKEDITOR.env.ie || CKEDITOR.env.webkit ) ?\r
                                                                domWindow : domDocument;\r
 \r
@@ -454,13 +473,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                if ( keystrokeHandler )\r
                                                        keystrokeHandler.attach( domDocument );\r
 \r
-                                               // Cancel default action for backspace in IE on control types. (#4047)\r
                                                if ( CKEDITOR.env.ie )\r
                                                {\r
-                                                       editor.on( 'key', function( event )\r
+                                                       // Cancel default action for backspace in IE on control types. (#4047)\r
+                                                       domDocument.on( 'keydown', function( evt )\r
                                                        {\r
                                                                // Backspace.\r
-                                                               var control = event.data.keyCode == 8\r
+                                                               var control = evt.data.getKeystroke() == 8\r
                                                                                          && editor.getSelection().getSelectedElement();\r
                                                                if ( control )\r
                                                                {\r
@@ -469,14 +488,31 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                        // Remove manually.\r
                                                                        control.remove();\r
                                                                        editor.fire( 'saveSnapshot' );\r
-                                                                       event.cancel();\r
+                                                                       evt.cancel();\r
                                                                }\r
                                                        } );\r
+\r
+                                                       // PageUp/PageDown scrolling is broken in document\r
+                                                       // with standard doctype, manually fix it. (#4736)\r
+                                                       if( domDocument.$.compatMode == 'CSS1Compat' )\r
+                                                       {\r
+                                                               var pageUpDownKeys = { 33 : 1, 34 : 1 };\r
+                                                               domDocument.on( 'keydown', function( evt )\r
+                                                               {\r
+                                                                       if( evt.data.getKeystroke() in pageUpDownKeys )\r
+                                                                       {\r
+                                                                               setTimeout( function ()\r
+                                                                               {\r
+                                                                                       editor.getSelection().scrollIntoView();\r
+                                                                               }, 0 );\r
+                                                                       }\r
+                                                               } );\r
+                                                       }\r
                                                }\r
 \r
                                                // Adds the document body as a context menu target.\r
                                                if ( editor.contextMenu )\r
-                                                       editor.contextMenu.addTarget( domDocument );\r
+                                                       editor.contextMenu.addTarget( domDocument, editor.config.browserContextMenuOnCtrl !== false );\r
 \r
                                                setTimeout( function()\r
                                                        {\r
@@ -550,54 +586,107 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        {\r
                                                                isLoadingData = true;\r
 \r
-                                                               // Get the HTML version of the data.\r
-                                                               if ( editor.dataProcessor )\r
+                                                               var config = editor.config,\r
+                                                                       fullPage = config.fullPage,\r
+                                                                       docType = config.docType;\r
+\r
+                                                               // Build the additional stuff to be included into <head>.\r
+                                                               var headExtra =\r
+                                                                       '<style type="text/css" cke_temp="1">' +\r
+                                                                               editor._.styles.join( '\n' ) +\r
+                                                                       '</style>';\r
+\r
+                                                               !fullPage && ( headExtra =\r
+                                                                       CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ) +\r
+                                                                       headExtra );\r
+\r
+                                                               var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" cke_temp="1" />' : '';\r
+\r
+                                                               if ( fullPage )\r
                                                                {\r
-                                                                       data = editor.dataProcessor.toHtml( data, fixForBody );\r
+                                                                       // Search and sweep out the doctype declaration.\r
+                                                                       data = data.replace( /<!DOCTYPE[^>]*>/i, function( match )\r
+                                                                               {\r
+                                                                                       editor.docType = docType = match;\r
+                                                                                       return '';\r
+                                                                               });\r
                                                                }\r
 \r
-                                                               data =\r
-                                                                       editor.config.docType +\r
-                                                                       '<html dir="' + editor.config.contentsLangDirection + '">' +\r
-                                                                       '<head>' +\r
-                                                                               '<link type="text/css" rel="stylesheet" href="' +\r
-                                                                               [].concat( editor.config.contentsCss ).join( '"><link type="text/css" rel="stylesheet" href="' ) +\r
-                                                                               '">' +\r
-                                                                               '<style type="text/css" _fcktemp="true">' +\r
-                                                                                       editor._.styles.join( '\n' ) +\r
-                                                                               '</style>'+\r
-                                                                       '</head>' +\r
-                                                                       '<body>' +\r
-                                                                               data +\r
-                                                                       '</body>' +\r
-                                                                       '</html>' +\r
-                                                                       activationScript;\r
-\r
-                                                               window[ '_cke_htmlToLoad_' + editor.name ] = data;\r
-                                                               CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady;\r
-                                                               createIFrame();\r
+                                                               // Get the HTML version of the data.\r
+                                                               if ( editor.dataProcessor )\r
+                                                                       data = editor.dataProcessor.toHtml( data, fixForBody );\r
 \r
-                                                               // Opera must use the old method for loading contents.\r
-                                                               if ( CKEDITOR.env.opera )\r
+                                                               if ( fullPage )\r
+                                                               {\r
+                                                                       // Check if the <body> tag is available.\r
+                                                                       if ( !(/<body[\s|>]/).test( data ) )\r
+                                                                               data = '<body>' + data;\r
+\r
+                                                                       // Check if the <html> tag is available.\r
+                                                                       if ( !(/<html[\s|>]/).test( data ) )\r
+                                                                               data = '<html>' + data + '</html>';\r
+\r
+                                                                       // Check if the <head> tag is available.\r
+                                                                       if ( !(/<head[\s|>]/).test( data ) )\r
+                                                                               data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' ) ;\r
+\r
+                                                                       // The base must be the first tag in the HEAD, e.g. to get relative\r
+                                                                       // links on styles.\r
+                                                                       baseTag && ( data = data.replace( /<head>/, '$&' + baseTag ) );\r
+\r
+                                                                       // Inject the extra stuff into <head>.\r
+                                                                       // Attention: do not change it before testing it well. (V2)\r
+                                                                       // This is tricky... if the head ends with <meta ... content type>,\r
+                                                                       // Firefox will break. But, it works if we place our extra stuff as\r
+                                                                       // the last elements in the HEAD.\r
+                                                                       data = data.replace( /<\/head\s*>/, headExtra + '$&' );\r
+\r
+                                                                       // Add the DOCTYPE back to it.\r
+                                                                       data = docType + data;\r
+                                                               }\r
+                                                               else\r
                                                                {\r
-                                                                       var doc = iframe.$.contentWindow.document;\r
-                                                                       doc.open();\r
-                                                                       doc.write( data );\r
-                                                                       doc.close();\r
+                                                                       data =\r
+                                                                               config.docType +\r
+                                                                               '<html dir="' + config.contentsLangDirection + '">' +\r
+                                                                               '<head>' +\r
+                                                                                       baseTag +\r
+                                                                                       headExtra +\r
+                                                                               '</head>' +\r
+                                                                               '<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) +\r
+                                                                                                 ( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) +\r
+                                                                                                 '>' +\r
+                                                                                       data +\r
+                                                                               '</html>';\r
                                                                }\r
+\r
+                                                               data += activationScript;\r
+\r
+                                                               CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady;\r
+                                                               createIFrame( data );\r
                                                        },\r
 \r
                                                        getData : function()\r
                                                        {\r
-                                                               var data = iframe.getFrameDocument().getBody().getHtml();\r
+                                                               var config = editor.config,\r
+                                                                       fullPage = config.fullPage,\r
+                                                                       docType = fullPage && editor.docType,\r
+                                                                       doc = iframe.getFrameDocument();\r
+\r
+                                                               var data = fullPage\r
+                                                                       ? doc.getDocumentElement().getOuterHtml()\r
+                                                                       : doc.getBody().getHtml();\r
 \r
                                                                if ( editor.dataProcessor )\r
                                                                        data = editor.dataProcessor.toDataFormat( data, fixForBody );\r
 \r
                                                                // Strip the last blank paragraph within document.\r
-                                                               if ( editor.config.ignoreEmptyParagraph )\r
+                                                               if ( config.ignoreEmptyParagraph )\r
                                                                        data = data.replace( emptyParagraphRegexp, '' );\r
 \r
+                                                               if ( docType )\r
+                                                                       data = docType + '\n' + data;\r
+\r
                                                                return data;\r
                                                        },\r
 \r
@@ -625,6 +714,25 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                else if ( editor.window )\r
                                                                {\r
                                                                        editor.window.focus();\r
+\r
+                                                                       // Force the selection to happen, in this way\r
+                                                                       // we guarantee the focus will be there. (#4848)\r
+                                                                       if ( CKEDITOR.env.ie )\r
+                                                                       {\r
+                                                                               try\r
+                                                                               {\r
+                                                                                       var sel = editor.getSelection();\r
+                                                                                       sel = sel && sel.getNative();\r
+                                                                                       var range = sel && sel.type && sel.createRange();\r
+                                                                                       if ( range )\r
+                                                                                       {\r
+                                                                                                       sel.empty();\r
+                                                                                                       range.select();\r
+                                                                                       }\r
+                                                                               }\r
+                                                                               catch (e) {}\r
+                                                                       }\r
+\r
                                                                        editor.selectionChange();\r
                                                                }\r
                                                        }\r
@@ -635,8 +743,60 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189)\r
                                        editor.on( 'selectionChange', onSelectionChangeFixBody, null, null, 1 );\r
                                });\r
+\r
+                       // Create an invisible element to grab focus.\r
+                       if( CKEDITOR.env.ie )\r
+                       {\r
+                               var ieFocusGrabber;\r
+                               editor.on( 'uiReady', function()\r
+                               {\r
+                                       ieFocusGrabber = editor.container.append( CKEDITOR.dom.element.createFromHtml(\r
+                                       '<input tabindex="-1" style="position:absolute; left:-10000">' ) );\r
+\r
+                                       ieFocusGrabber.on( 'focus', function()\r
+                                               {\r
+                                                       editor.focus();\r
+                                               } );\r
+                               } );\r
+                       }\r
                }\r
        });\r
+\r
+       // Fixing Firefox 'Back-Forward Cache' break design mode. (#4514)\r
+       if( CKEDITOR.env.gecko )\r
+       {\r
+               var topWin = window.top;\r
+\r
+               ( function ()\r
+               {\r
+                       var topBody = topWin.document.body;\r
+\r
+                       if( !topBody )\r
+                               topWin.addEventListener( 'load', arguments.callee, false );\r
+                       else\r
+                       {\r
+                               topBody.setAttribute( 'onpageshow', topBody.getAttribute( 'onpageshow' )\r
+                                               + ';event.persisted && CKEDITOR.tools.callFunction(' +\r
+                                               CKEDITOR.tools.addFunction( function()\r
+                                               {\r
+                                                       var allInstances = CKEDITOR.instances,\r
+                                                               editor,\r
+                                                               doc;\r
+                                                       for( var i in allInstances )\r
+                                                       {\r
+                                                               editor = allInstances[ i ];\r
+                                                               doc = editor.document;\r
+                                                               if( doc )\r
+                                                               {\r
+                                                                       doc.$.designMode = 'off';\r
+                                                                       doc.$.designMode = 'on';\r
+                                                               }\r
+                                                       }\r
+                                               } ) + ')' );\r
+                       }\r
+               } )();\r
+\r
+       }\r
 })();\r
 \r
 /**\r