JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.0.2
[ckeditor.git] / _source / plugins / wysiwygarea / plugin.js
index 225f2f3..d3b9e62 100644 (file)
@@ -15,7 +15,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
         */\r
        var nonExitableElementNames = { table:1,pre:1 };\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*$/gi;\r
 \r
        function onInsertHtml( evt )\r
        {\r
@@ -81,14 +81,27 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
                                // If we're inserting a block at dtd-violated position, split\r
                                // the parent blocks until we reach blockLimit.\r
-                               var parent, dtd;\r
-                               if ( this.config.enterMode != CKEDITOR.ENTER_BR && isBlock )\r
+                               var current, dtd;\r
+                               if ( isBlock )\r
                                {\r
-                                       while( ( parent = range.getCommonAncestor( false, true ) )\r
-                                                       && ( dtd = CKEDITOR.dtd[ parent.getName() ] )\r
+                                       while( ( current = range.getCommonAncestor( false, true ) )\r
+                                                       && ( dtd = CKEDITOR.dtd[ current.getName() ] )\r
                                                        && !( dtd && dtd [ elementName ] ) )\r
                                        {\r
-                                               range.splitBlock();\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
+                                               else if ( range.checkStartOfBlock()\r
+                                                        && range.checkEndOfBlock() )\r
+                                               {\r
+                                                       range.setStartBefore( current );\r
+                                                       range.collapse( true );\r
+                                                       current.remove();\r
+                                               }\r
+                                               else\r
+                                                       range.splitBlock();\r
                                        }\r
                                }\r
 \r
@@ -122,6 +135,27 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                }\r
        }\r
 \r
+       // DOM modification here should not bother dirty flag.(#4385)\r
+       function restoreDirty( editor )\r
+       {\r
+               if( !editor.checkDirty() )\r
+                       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
@@ -143,60 +177,53 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                         && blockLimit.getName() == 'body'\r
                         && !path.block )\r
                {\r
-                       var bms = selection.createBookmarks(),\r
-                               fixedBlock = range.fixBlock( true,\r
+                       restoreDirty( editor );\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
-                       var paddingBlock = editor.document.createElement(\r
-                                       ( CKEDITOR.env.ie && enterMode != CKEDITOR.ENTER_BR ) ?\r
-                                               '<br _cke_bogus="true" />' : 'br' );\r
-                       body.append( paddingBlock );\r
+                       restoreDirty( editor );\r
+                       if( !CKEDITOR.env.ie )\r
+                               body.appendBogus();\r
+                       else\r
+                               body.append( editor.document.createText( '\xa0' ) );\r
                }\r
        }\r
 \r
@@ -310,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
@@ -363,26 +385,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                domWindow       = editor.window         = new CKEDITOR.dom.window( domWindow );\r
                                                domDocument     = editor.document       = new CKEDITOR.dom.document( domDocument );\r
 \r
-                                               // Gecko need a key event to 'wake up' the editing\r
-                                               // ability when document is empty.(#3864)\r
-                                               var firstNode = domDocument.getBody().getFirst();\r
-                                               if ( CKEDITOR.env.gecko\r
-                                                       && firstNode && firstNode.is\r
-                                                       && firstNode.is( 'br' ) && firstNode.hasAttribute( '_moz_editor_bogus_node' ) )\r
-                                               {\r
-                                                       var keyEventSimulate = domDocument.$.createEvent( "KeyEvents" );\r
-                                                       keyEventSimulate.initKeyEvent( 'keypress', true, true, domWindow.$, false,\r
-                                                               false, false, false, 0, 32 );\r
-                                                       domDocument.$.dispatchEvent( keyEventSimulate );\r
-                                                       var bogusText = domDocument.getBody().getFirst() ;\r
-                                                       // Compensate the line maintaining <br> if enterMode is not block.\r
-                                                       if ( editor.config.enterMode == CKEDITOR.ENTER_BR )\r
-                                                               domDocument.createElement( 'br', { attributes: { '_moz_dirty' : "" } } )\r
-                                                                       .replace( bogusText );\r
-                                                       else\r
-                                                               bogusText.remove();\r
-                                               }\r
-\r
                                                // Gecko/Webkit need some help when selecting control type elements. (#3448)\r
                                                if ( !( CKEDITOR.env.ie || CKEDITOR.env.opera) )\r
                                                {\r
@@ -412,7 +414,23 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        } );\r
                                                }\r
 \r
-                                               var focusTarget = ( CKEDITOR.env.ie || CKEDITOR.env.safari ) ?\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
                                                focusTarget.on( 'blur', function()\r
@@ -422,6 +440,32 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
                                                focusTarget.on( 'focus', function()\r
                                                        {\r
+                                                               // Gecko need a key event to 'wake up' the editing\r
+                                                               // ability when document is empty.(#3864)\r
+                                                               if ( CKEDITOR.env.gecko )\r
+                                                               {\r
+                                                                       var first = body;\r
+                                                                       while( first.firstChild )\r
+                                                                               first = first.firstChild;\r
+\r
+                                                                       if( !first.nextSibling\r
+                                                                               && ( 'BR' == first.tagName )\r
+                                                                               && first.hasAttribute( '_moz_editor_bogus_node' ) )\r
+                                                                       {\r
+                                                                               var keyEventSimulate = domDocument.$.createEvent( "KeyEvents" );\r
+                                                                               keyEventSimulate.initKeyEvent( 'keypress', true, true, domWindow.$, false,\r
+                                                                                       false, false, false, 0, 32 );\r
+                                                                               domDocument.$.dispatchEvent( keyEventSimulate );\r
+                                                                               var bogusText = domDocument.getBody().getFirst() ;\r
+                                                                               // Compensate the line maintaining <br> if enterMode is not block.\r
+                                                                               if ( editor.config.enterMode == CKEDITOR.ENTER_BR )\r
+                                                                                       domDocument.createElement( 'br', { attributes: { '_moz_dirty' : "" } } )\r
+                                                                                               .replace( bogusText );\r
+                                                                               else\r
+                                                                                       bogusText.remove();\r
+                                                                       }\r
+                                                               }\r
+\r
                                                                editor.focusManager.focus();\r
                                                        });\r
 \r
@@ -429,9 +473,46 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                if ( keystrokeHandler )\r
                                                        keystrokeHandler.attach( domDocument );\r
 \r
+                                               if ( CKEDITOR.env.ie )\r
+                                               {\r
+                                                       // Cancel default action for backspace in IE on control types. (#4047)\r
+                                                       domDocument.on( 'keydown', function( evt )\r
+                                                       {\r
+                                                               // Backspace.\r
+                                                               var control = evt.data.getKeystroke() == 8\r
+                                                                                         && editor.getSelection().getSelectedElement();\r
+                                                               if ( control )\r
+                                                               {\r
+                                                                       // Make undo snapshot.\r
+                                                                       editor.fire( 'saveSnapshot' );\r
+                                                                       // Remove manually.\r
+                                                                       control.remove();\r
+                                                                       editor.fire( 'saveSnapshot' );\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
@@ -451,6 +532,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                        editor.focus();\r
                                                                        isPendingFocus = false;\r
                                                                }\r
+                                                               setTimeout( function()\r
+                                                               {\r
+                                                                       editor.fire( 'dataReady' );\r
+                                                               }, 0 );\r
 \r
                                                                /*\r
                                                                 * IE BUG: IE might have rendered the iframe with invisible contents.\r
@@ -511,7 +596,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                        editor.config.docType +\r
                                                                        '<html dir="' + editor.config.contentsLangDirection + '">' +\r
                                                                        '<head>' +\r
-                                                                               '<link href="' + editor.config.contentsCss + '" type="text/css" rel="stylesheet" _fcktemp="true"/>' +\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
@@ -574,6 +661,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
@@ -584,8 +690,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