JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.3.2
[ckeditor.git] / _source / plugins / selection / plugin.js
index e7c24b7..3d707fc 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
@@ -17,7 +17,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        // In IE, the "selectionchange" event may still get thrown when\r
                        // releasing the WYSIWYG mode, so we need to check it first.\r
                        var sel = this.getSelection();\r
-                       if ( !sel )\r
+                       if ( !sel || !sel.document.getWindow().$ )\r
                                return;\r
 \r
                        var firstElement = sel.getStartElement();\r
@@ -72,6 +72,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
        var selectAllCmd =\r
        {\r
+               modes : { wysiwyg : 1, source : 1 },\r
                exec : function( editor )\r
                {\r
                        switch ( editor.mode )\r
@@ -80,7 +81,18 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        editor.document.$.execCommand( 'SelectAll', false, null );\r
                                        break;\r
                                case 'source' :\r
-                                       // TODO\r
+                                       // Select the contents of the textarea\r
+                                       var textarea = editor.textarea.$ ;\r
+                                       if ( CKEDITOR.env.ie )\r
+                                       {\r
+                                               textarea.createTextRange().execCommand( 'SelectAll' ) ;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               textarea.selectionStart = 0 ;\r
+                                               textarea.selectionEnd = textarea.value.length ;\r
+                                       }\r
+                                       textarea.focus() ;\r
                        }\r
                },\r
                canUndo : false\r
@@ -92,7 +104,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                {\r
                        editor.on( 'contentDom', function()\r
                                {\r
-                                       var doc = editor.document;\r
+                                       var doc = editor.document,\r
+                                               body = doc.getBody(),\r
+                                               html = doc.getDocumentElement();\r
 \r
                                        if ( CKEDITOR.env.ie )\r
                                        {\r
@@ -102,30 +116,39 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                // than firing the selection change event.\r
 \r
                                                var savedRange,\r
-                                                       saveEnabled;\r
+                                                       saveEnabled,\r
+                                                       restoreEnabled = 1;\r
 \r
                                                // "onfocusin" is fired before "onfocus". It makes it\r
                                                // possible to restore the selection before click\r
                                                // events get executed.\r
-                                               doc.on( 'focusin', function()\r
+                                               body.on( 'focusin', function( evt )\r
                                                        {\r
+                                                               // If there are elements with layout they fire this event but\r
+                                                               // it must be ignored to allow edit its contents #4682\r
+                                                               if ( evt.data.$.srcElement.nodeName != 'BODY' )\r
+                                                                       return;\r
+\r
                                                                // If we have saved a range, restore it at this\r
                                                                // point.\r
                                                                if ( savedRange )\r
                                                                {\r
-                                                                       // Well not break because of this.\r
-                                                                       try\r
+                                                                       if ( restoreEnabled )\r
                                                                        {\r
-                                                                               savedRange.select();\r
+                                                                               // Well not break because of this.\r
+                                                                               try\r
+                                                                               {\r
+                                                                                       savedRange.select();\r
+                                                                               }\r
+                                                                               catch (e)\r
+                                                                               {}\r
                                                                        }\r
-                                                                       catch (e)\r
-                                                                       {}\r
 \r
                                                                        savedRange = null;\r
                                                                }\r
                                                        });\r
 \r
-                                               editor.window.on( 'focus', function()\r
+                                               body.on( 'focus', function()\r
                                                        {\r
                                                                // Enable selections to be saved.\r
                                                                saveEnabled = true;\r
@@ -133,25 +156,65 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                saveSelection();\r
                                                        });\r
 \r
-                                               // Check document selection before 'blur' fired, this\r
-                                               // will prevent us from breaking text selection somewhere\r
-                                               // else on the host page.(#3909)\r
-                                               editor.document.on( 'beforedeactivate', function()\r
+                                               body.on( 'beforedeactivate', function( evt )\r
                                                        {\r
+                                                               // Ignore this event if it's caused by focus switch between\r
+                                                               // internal editable control type elements, e.g. layouted paragraph. (#4682)\r
+                                                               if ( evt.data.$.toElement )\r
+                                                                       return;\r
+\r
                                                                // Disable selections from being saved.\r
                                                                saveEnabled = false;\r
+                                                               restoreEnabled = 1;\r
+                                                       });\r
 \r
-                                                               // IE may leave the selection still inside the\r
-                                                               // document. Let's force it to be removed.\r
-                                                               // TODO: The following has effect for\r
-                                                               // collapsed selections.\r
-                                                               editor.document.$.execCommand( 'Unselect' );\r
+                                               // IE before version 8 will leave cursor blinking inside the document after\r
+                                               // editor blurred unless we clean up the selection. (#4716)\r
+                                               if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )\r
+                                               {\r
+                                                       editor.on( 'blur', function( evt )\r
+                                                       {\r
+                                                               editor.document && editor.document.$.selection.empty();\r
                                                        });\r
+                                               }\r
+\r
+                                               // Listening on document element ensures that\r
+                                               // scrollbar is included. (#5280)\r
+                                               html.on( 'mousedown', function ()\r
+                                               {\r
+                                                       // Lock restore selection now, as we have\r
+                                                       // a followed 'click' event which introduce\r
+                                                       // new selection. (#5735)\r
+                                                       restoreEnabled = 0;\r
+                                               });\r
+\r
+                                               html.on( 'mouseup', function ()\r
+                                               {\r
+                                                       restoreEnabled = 1;\r
+                                               });\r
+\r
+                                               // In IE6/7 the blinking cursor appears, but contents are\r
+                                               // not editable. (#5634)\r
+                                               if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.version < 8 || CKEDITOR.env.quirks ) )\r
+                                               {\r
+                                                       // The 'click' event is not fired when clicking the\r
+                                                       // scrollbars, so we can use it to check whether\r
+                                                       // the empty space following <body> has been clicked.\r
+                                                       html.on( 'click', function( evt )\r
+                                                       {\r
+                                                               if ( evt.data.getTarget().getName() == 'html' )\r
+                                                                       editor.getSelection().getRanges()[ 0 ].select();\r
+                                                       });\r
+                                               }\r
 \r
                                                // IE fires the "selectionchange" event when clicking\r
                                                // inside a selection. We don't want to capture that.\r
-                                               doc.on( 'mousedown', disableSave );\r
-                                               doc.on( 'mouseup',\r
+                                               body.on( 'mousedown', function ()\r
+                                               {\r
+                                                       disableSave();\r
+                                               });\r
+\r
+                                               body.on( 'mouseup',\r
                                                        function()\r
                                                        {\r
                                                                saveEnabled = true;\r
@@ -162,8 +225,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                        0 );\r
                                                        });\r
 \r
-                                               doc.on( 'keydown', disableSave );\r
-                                               doc.on( 'keyup',\r
+                                               body.on( 'keydown', disableSave );\r
+                                               body.on( 'keyup',\r
                                                        function()\r
                                                        {\r
                                                                saveEnabled = true;\r
@@ -185,7 +248,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        if ( saveEnabled )\r
                                                        {\r
                                                                var doc = editor.document,\r
-                                                                       sel = doc && doc.$.selection;\r
+                                                                       sel = editor.getSelection(),\r
+                                                                       nativeSel = sel && sel.getNative();\r
 \r
                                                                // There is a very specific case, when clicking\r
                                                                // inside a text selection. In that case, the\r
@@ -195,7 +259,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                // range at the very start of the document. In\r
                                                                // such situation we have to test the range, to\r
                                                                // be sure it's valid.\r
-                                                               if ( testIt && sel && sel.type == 'None' )\r
+                                                               if ( testIt && nativeSel && nativeSel.type == 'None' )\r
                                                                {\r
                                                                        // The "InsertImage" command can be used to\r
                                                                        // test whether the selection is good or not.\r
@@ -208,7 +272,18 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                        }\r
                                                                }\r
 \r
-                                                               savedRange = sel && sel.createRange();\r
+                                                               // Avoid saving selection from within text input. (#5747)\r
+                                                               var parentTag;\r
+                                                               if ( nativeSel && nativeSel.type && nativeSel.type != 'Control'\r
+                                                                       && ( parentTag = nativeSel.createRange() )\r
+                                                                       && ( parentTag = parentTag.parentElement() )\r
+                                                                       && ( parentTag = parentTag.nodeName )\r
+                                                                       && parentTag.toLowerCase() in { input: 1, textarea : 1 } )\r
+                                                               {\r
+                                                                       return;\r
+                                                               }\r
+\r
+                                                               savedRange = nativeSel && sel.getRanges()[ 0 ];\r
 \r
                                                                checkSelectionChangeTimeout.call( editor );\r
                                                        }\r
@@ -333,7 +408,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
        var styleObjectElements =\r
        {\r
-               img:1,hr:1,li:1,table:1,tr:1,td:1,embed:1,object:1,ol:1,ul:1,\r
+               img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,\r
                a:1, input:1, form:1, select:1, textarea:1, button:1, fieldset:1, th:1, thead:1, tfoot:1\r
        };\r
 \r
@@ -441,6 +516,15 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        return ( cache.type = type );\r
                                },\r
 \r
+               /**\r
+                * Retrieve the {@link CKEDITOR.dom.range} instances that represent the current selection.\r
+                * Note: Some browsers returns multiple ranges even on a sequent selection, e.g. Firefox returns\r
+                * one range for each table cell when one or more table row is selected.\r
+                * @return {Array}\r
+                * @example\r
+                * var ranges = selection.getRanges();\r
+                * alert(ranges.length);\r
+                */\r
                getRanges :\r
                        CKEDITOR.env.ie ?\r
                                ( function()\r
@@ -467,17 +551,21 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                testRange = range.duplicate();\r
 \r
                                                                testRange.moveToElementText( child );\r
-                                                               testRange.collapse();\r
 \r
-                                                               var comparison = testRange.compareEndPoints( 'StartToStart', range );\r
+                                                               var comparisonStart = testRange.compareEndPoints( 'StartToStart', range ),\r
+                                                                       comparisonEnd = testRange.compareEndPoints( 'EndToStart', range );\r
+\r
+                                                               testRange.collapse();\r
 \r
-                                                               if ( comparison > 0 )\r
+                                                               if ( comparisonStart > 0 )\r
                                                                        break;\r
-                                                               else if ( comparison === 0 )\r
-                                                                       return {\r
-                                                                               container : parent,\r
-                                                                               offset : i\r
-                                                                       };\r
+                                                               // When selection stay at the side of certain self-closing elements, e.g. BR,\r
+                                                               // our comparison will never shows an equality. (#4824)\r
+                                                               else if ( !comparisonStart\r
+                                                                       || comparisonEnd == 1 && comparisonStart == -1 )\r
+                                                                       return { container : parent, offset : i };\r
+                                                               else if ( !comparisonEnd )\r
+                                                                       return { container : parent, offset : i + 1 };\r
 \r
                                                                testRange = null;\r
                                                        }\r
@@ -496,8 +584,17 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                // breaking character counting logic below. (#3949)\r
                                                var distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;\r
 \r
-                                               while ( distance > 0 )\r
-                                                       distance -= siblings[ --i ].nodeValue.length;\r
+                                               try\r
+                                               {\r
+                                                       while ( distance > 0 )\r
+                                                               distance -= siblings[ --i ].nodeValue.length;\r
+                                               }\r
+                                               // Measurement in IE could be somtimes wrong because of <select> element. (#4611)\r
+                                               catch( e )\r
+                                               {\r
+                                                       distance = 0;\r
+                                               }\r
+\r
 \r
                                                if ( distance === 0 )\r
                                                {\r
@@ -636,12 +733,14 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        // Decrease the range content to exclude particial\r
                                                        // selected node on the start which doesn't have\r
                                                        // visual impact. ( #3231 )\r
-                                                       while( true )\r
+                                                       while ( true )\r
                                                        {\r
                                                                var startContainer = range.startContainer,\r
                                                                        startOffset = range.startOffset;\r
+                                                               // Limit the fix only to non-block elements.(#3950)\r
                                                                if ( startOffset == ( startContainer.getChildCount ?\r
-                                                                       startContainer.getChildCount() : startContainer.getLength() ) )\r
+                                                                        startContainer.getChildCount() : startContainer.getLength() )\r
+                                                                        && !startContainer.isBlockBoundary() )\r
                                                                        range.setStartAfter( startContainer );\r
                                                                else break;\r
                                                        }\r
@@ -678,7 +777,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        {\r
                                                node = sel.anchorNode;\r
 \r
-                                               if ( node.nodeType != 1 )\r
+                                               if ( node && node.nodeType != 1 )\r
                                                        node = node.parentNode;\r
                                        }\r
                        }\r
@@ -701,26 +800,34 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        if ( cache.selectedElement !== undefined )\r
                                return cache.selectedElement;\r
 \r
-                       var node;\r
-\r
-                       if ( this.getType() == CKEDITOR.SELECTION_ELEMENT )\r
-                       {\r
-                               var sel = this.getNative();\r
+                       var self = this;\r
 \r
-                               if ( CKEDITOR.env.ie )\r
+                       var node = CKEDITOR.tools.tryThese(\r
+                               // Is it native IE control type selection?\r
+                               function()\r
                                {\r
-                                       try\r
+                                       return self.getNative().createRange().item( 0 );\r
+                               },\r
+                               // Figure it out by checking if there's a single enclosed\r
+                               // node of the range.\r
+                               function()\r
+                               {\r
+                                       var range  = self.getRanges()[ 0 ],\r
+                                               enclosed,\r
+                                               selected;\r
+\r
+                                       // Check first any enclosed element, e.g. <ul>[<li><a href="#">item</a></li>]</ul>\r
+                                       for ( var i = 2; i && !( ( enclosed = range.getEnclosedNode() )\r
+                                               && ( enclosed.type == CKEDITOR.NODE_ELEMENT )\r
+                                               && styleObjectElements[ enclosed.getName() ]\r
+                                               && ( selected = enclosed ) ); i-- )\r
                                        {\r
-                                               node = sel.createRange().item(0);\r
+                                               // Then check any deep wrapped element, e.g. [<b><i><img /></i></b>]\r
+                                               range.shrink( CKEDITOR.SHRINK_ELEMENT );\r
                                        }\r
-                                       catch(e) {}\r
-                               }\r
-                               else\r
-                               {\r
-                                       var range = sel.getRangeAt( 0 );\r
-                                       node = range.startContainer.childNodes[ range.startOffset ];\r
-                               }\r
-                       }\r
+\r
+                                       return  selected.$;\r
+                               });\r
 \r
                        return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null );\r
                },\r
@@ -779,6 +886,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        this._.cache = {};\r
                },\r
 \r
+               /**\r
+                *  Make the current selection of type {@link CKEDITOR.SELECTION_ELEMENT} by enclosing the specified element.\r
+                * @param element\r
+                */\r
                selectElement : function( element )\r
                {\r
                        if ( this.isLocked )\r
@@ -813,6 +924,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        range.moveToElementText( element.$ );\r
                                        range.select();\r
                                }\r
+                               finally\r
+                               {\r
+                                       this.document.fire( 'selectionchange' );\r
+                               }\r
 \r
                                this.reset();\r
                        }\r
@@ -831,6 +946,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        }\r
                },\r
 \r
+               /**\r
+                *  Adding the specified ranges to document selection preceding\r
+                * by clearing up the original selection.\r
+                * @param {CKEDITOR.dom.range} ranges\r
+                */\r
                selectRanges : function( ranges )\r
                {\r
                        if ( this.isLocked )\r
@@ -885,6 +1005,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        }\r
                },\r
 \r
+               /**\r
+                *  Create bookmark for every single of this selection range (from #getRanges)\r
+                * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark} method,\r
+                * with extra cares to avoid interferon among those ranges. Same arguments are\r
+                * received as with the underlay range method.\r
+                */\r
                createBookmarks : function( serializable )\r
                {\r
                        var retval = [],\r
@@ -917,6 +1043,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        return retval;\r
                },\r
 \r
+               /**\r
+                *  Create bookmark for every single of this selection range (from #getRanges)\r
+                * by calling the {@link CKEDITOR.dom.range.prototype.createBookmark2} method,\r
+                * with extra cares to avoid interferon among those ranges. Same arguments are\r
+                * received as with the underlay range method.\r
+                */\r
                createBookmarks2 : function( normalized )\r
                {\r
                        var bookmarks = [],\r
@@ -928,6 +1060,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        return bookmarks;\r
                },\r
 \r
+               /**\r
+                * Select the virtual ranges denote by the bookmarks by calling #selectRanges.\r
+                * @param bookmarks\r
+                */\r
                selectBookmarks : function( bookmarks )\r
                {\r
                        var ranges = [];\r
@@ -939,134 +1075,176 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        }\r
                        this.selectRanges( ranges );\r
                        return this;\r
+               },\r
+\r
+               /**\r
+                * Retrieve the common ancestor node of the first range and the last range.\r
+                */\r
+               getCommonAncestor : function()\r
+               {\r
+                       var ranges = this.getRanges(),\r
+                               startNode = ranges[ 0 ].startContainer,\r
+                               endNode = ranges[ ranges.length - 1 ].endContainer;\r
+                       return startNode.getCommonAncestor( endNode );\r
+               },\r
+\r
+               /**\r
+                * Moving scroll bar to the current selection's start position.\r
+                */\r
+               scrollIntoView : function()\r
+               {\r
+                       // If we have split the block, adds a temporary span at the\r
+                       // range position and scroll relatively to it.\r
+                       var start = this.getStartElement();\r
+                       start.scrollIntoView();\r
                }\r
        };\r
 })();\r
 \r
-CKEDITOR.dom.range.prototype.select =\r
-       CKEDITOR.env.ie ?\r
-               // V2\r
-               function( forceExpand )\r
-               {\r
-                       var collapsed = this.collapsed;\r
-                       var isStartMarkerAlone;\r
-                       var dummySpan;\r
+( function()\r
+{\r
+       var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),\r
+                       fillerTextRegex = /\ufeff|\u00a0/,\r
+                       nonCells = { table:1,tbody:1,tr:1 };\r
+\r
+       CKEDITOR.dom.range.prototype.select =\r
+               CKEDITOR.env.ie ?\r
+                       // V2\r
+                       function( forceExpand )\r
+                       {\r
+                               var collapsed = this.collapsed;\r
+                               var isStartMarkerAlone;\r
+                               var dummySpan;\r
+\r
+                               // IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g.\r
+                               // <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>...\r
+                               if ( this.startContainer.type == CKEDITOR.NODE_ELEMENT && this.startContainer.getName() in nonCells\r
+                                       || this.endContainer.type == CKEDITOR.NODE_ELEMENT && this.endContainer.getName() in nonCells )\r
+                               {\r
+                                       this.shrink( CKEDITOR.NODE_ELEMENT, true );\r
+                               }\r
 \r
-                       var bookmark = this.createBookmark();\r
+                               var bookmark = this.createBookmark();\r
 \r
-                       // Create marker tags for the start and end boundaries.\r
-                       var startNode = bookmark.startNode;\r
+                               // Create marker tags for the start and end boundaries.\r
+                               var startNode = bookmark.startNode;\r
 \r
-                       var endNode;\r
-                       if ( !collapsed )\r
-                               endNode = bookmark.endNode;\r
+                               var endNode;\r
+                               if ( !collapsed )\r
+                                       endNode = bookmark.endNode;\r
 \r
-                       // Create the main range which will be used for the selection.\r
-                       var ieRange = this.document.$.body.createTextRange();\r
+                               // Create the main range which will be used for the selection.\r
+                               var ieRange = this.document.$.body.createTextRange();\r
 \r
-                       // Position the range at the start boundary.\r
-                       ieRange.moveToElementText( startNode.$ );\r
-                       ieRange.moveStart( 'character', 1 );\r
+                               // Position the range at the start boundary.\r
+                               ieRange.moveToElementText( startNode.$ );\r
+                               ieRange.moveStart( 'character', 1 );\r
 \r
-                       if ( endNode )\r
-                       {\r
-                               // Create a tool range for the end.\r
-                               var ieRangeEnd = this.document.$.body.createTextRange();\r
+                               if ( endNode )\r
+                               {\r
+                                       // Create a tool range for the end.\r
+                                       var ieRangeEnd = this.document.$.body.createTextRange();\r
 \r
-                               // Position the tool range at the end.\r
-                               ieRangeEnd.moveToElementText( endNode.$ );\r
+                                       // Position the tool range at the end.\r
+                                       ieRangeEnd.moveToElementText( endNode.$ );\r
 \r
-                               // Move the end boundary of the main range to match the tool range.\r
-                               ieRange.setEndPoint( 'EndToEnd', ieRangeEnd );\r
-                               ieRange.moveEnd( 'character', -1 );\r
-                       }\r
-                       else\r
-                       {\r
-                               // The isStartMarkerAlone logic comes from V2. It guarantees that the lines\r
-                               // will expand and that the cursor will be blinking on the right place.\r
-                               // Actually, we are using this flag just to avoid using this hack in all\r
-                               // situations, but just on those needed.\r
-                               isStartMarkerAlone = forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) );\r
-\r
-                               // Append a temporary <span>&#65279;</span> before the selection.\r
-                               // This is needed to avoid IE destroying selections inside empty\r
-                               // inline elements, like <b></b> (#253).\r
-                               // It is also needed when placing the selection right after an inline\r
-                               // element to avoid the selection moving inside of it.\r
-                               dummySpan = this.document.createElement( 'span' );\r
-                               dummySpan.setHtml( '&#65279;' );        // Zero Width No-Break Space (U+FEFF). See #1359.\r
-                               dummySpan.insertBefore( startNode );\r
-\r
-                               if ( isStartMarkerAlone )\r
+                                       // Move the end boundary of the main range to match the tool range.\r
+                                       ieRange.setEndPoint( 'EndToEnd', ieRangeEnd );\r
+                                       ieRange.moveEnd( 'character', -1 );\r
+                               }\r
+                               else\r
                                {\r
-                                       // To expand empty blocks or line spaces after <br>, we need\r
-                                       // instead to have any char, which will be later deleted using the\r
-                                       // selection.\r
-                                       // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359)\r
-                                       this.document.createText( '\ufeff' ).insertBefore( startNode );\r
+                                       // The isStartMarkerAlone logic comes from V2. It guarantees that the lines\r
+                                       // will expand and that the cursor will be blinking on the right place.\r
+                                       // Actually, we are using this flag just to avoid using this hack in all\r
+                                       // situations, but just on those needed.\r
+                                       var next = startNode.getNext( notWhitespaces );\r
+                                       isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) )     // already a filler there?\r
+                                                                                 && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) );\r
+\r
+                                       // Append a temporary <span>&#65279;</span> before the selection.\r
+                                       // This is needed to avoid IE destroying selections inside empty\r
+                                       // inline elements, like <b></b> (#253).\r
+                                       // It is also needed when placing the selection right after an inline\r
+                                       // element to avoid the selection moving inside of it.\r
+                                       dummySpan = this.document.createElement( 'span' );\r
+                                       dummySpan.setHtml( '&#65279;' );        // Zero Width No-Break Space (U+FEFF). See #1359.\r
+                                       dummySpan.insertBefore( startNode );\r
+\r
+                                       if ( isStartMarkerAlone )\r
+                                       {\r
+                                               // To expand empty blocks or line spaces after <br>, we need\r
+                                               // instead to have any char, which will be later deleted using the\r
+                                               // selection.\r
+                                               // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359)\r
+                                               this.document.createText( '\ufeff' ).insertBefore( startNode );\r
+                                       }\r
                                }\r
-                       }\r
 \r
-                       // Remove the markers (reset the position, because of the changes in the DOM tree).\r
-                       this.setStartBefore( startNode );\r
-                       startNode.remove();\r
+                               // Remove the markers (reset the position, because of the changes in the DOM tree).\r
+                               this.setStartBefore( startNode );\r
+                               startNode.remove();\r
 \r
-                       if ( collapsed )\r
-                       {\r
-                               if ( isStartMarkerAlone )\r
+                               if ( collapsed )\r
                                {\r
-                                       // Move the selection start to include the temporary \ufeff.\r
-                                       ieRange.moveStart( 'character', -1 );\r
+                                       if ( isStartMarkerAlone )\r
+                                       {\r
+                                               // Move the selection start to include the temporary \ufeff.\r
+                                               ieRange.moveStart( 'character', -1 );\r
 \r
-                                       ieRange.select();\r
+                                               ieRange.select();\r
 \r
-                                       // Remove our temporary stuff.\r
-                                       this.document.$.selection.clear();\r
+                                               // Remove our temporary stuff.\r
+                                               this.document.$.selection.clear();\r
+                                       }\r
+                                       else\r
+                                               ieRange.select();\r
+\r
+                                       this.moveToPosition( dummySpan, CKEDITOR.POSITION_BEFORE_START );\r
+                                       dummySpan.remove();\r
                                }\r
                                else\r
+                               {\r
+                                       this.setEndBefore( endNode );\r
+                                       endNode.remove();\r
                                        ieRange.select();\r
+                               }\r
 \r
-                               dummySpan.remove();\r
+                               this.document.fire( 'selectionchange' );\r
                        }\r
-                       else\r
+               :\r
+                       function()\r
                        {\r
-                               this.setEndBefore( endNode );\r
-                               endNode.remove();\r
-                               ieRange.select();\r
-                       }\r
-               }\r
-       :\r
-               function()\r
-               {\r
-                       var startContainer = this.startContainer;\r
+                               var startContainer = this.startContainer;\r
 \r
-                       // If we have a collapsed range, inside an empty element, we must add\r
-                       // something to it, otherwise the caret will not be visible.\r
-                       if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() )\r
-                               startContainer.append( new CKEDITOR.dom.text( '' ) );\r
+                               // If we have a collapsed range, inside an empty element, we must add\r
+                               // something to it, otherwise the caret will not be visible.\r
+                               if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() )\r
+                                       startContainer.append( new CKEDITOR.dom.text( '' ) );\r
 \r
-                       var nativeRange = this.document.$.createRange();\r
-                       nativeRange.setStart( startContainer.$, this.startOffset );\r
+                               var nativeRange = this.document.$.createRange();\r
+                               nativeRange.setStart( startContainer.$, this.startOffset );\r
 \r
-                       try\r
-                       {\r
-                               nativeRange.setEnd( this.endContainer.$, this.endOffset );\r
-                       }\r
-                       catch ( e )\r
-                       {\r
-                               // There is a bug in Firefox implementation (it would be too easy\r
-                               // otherwise). The new start can't be after the end (W3C says it can).\r
-                               // So, let's create a new range and collapse it to the desired point.\r
-                               if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 )\r
+                               try\r
                                {\r
-                                       this.collapse( true );\r
                                        nativeRange.setEnd( this.endContainer.$, this.endOffset );\r
                                }\r
-                               else\r
-                                       throw( e );\r
-                       }\r
+                               catch ( e )\r
+                               {\r
+                                       // There is a bug in Firefox implementation (it would be too easy\r
+                                       // otherwise). The new start can't be after the end (W3C says it can).\r
+                                       // So, let's create a new range and collapse it to the desired point.\r
+                                       if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 )\r
+                                       {\r
+                                               this.collapse( true );\r
+                                               nativeRange.setEnd( this.endContainer.$, this.endOffset );\r
+                                       }\r
+                                       else\r
+                                               throw( e );\r
+                               }\r
 \r
-                       var selection = this.document.getSelection().getNative();\r
-                       selection.removeAllRanges();\r
-                       selection.addRange( nativeRange );\r
-               };\r
+                               var selection = this.document.getSelection().getNative();\r
+                               selection.removeAllRanges();\r
+                               selection.addRange( nativeRange );\r
+                       };\r
+} )();\r