JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.4.3
[ckeditor.git] / _source / core / dom / range.js
index c187400..8f10980 100644 (file)
@@ -3,6 +3,9 @@ Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
 For licensing, see LICENSE.html or http://ckeditor.com/license\r
 */\r
 \r
+/**\r
+ * @class\r
+ */\r
 CKEDITOR.dom.range = function( document )\r
 {\r
        this.startContainer     = null;\r
@@ -305,7 +308,7 @@ CKEDITOR.dom.range = function( document )
                return node.type != CKEDITOR.NODE_TEXT\r
                            && node.getName() in CKEDITOR.dtd.$removeEmpty\r
                            || !CKEDITOR.tools.trim( node.getText() )\r
-                           || node.getParent().hasAttribute( '_fck_bookmark' );\r
+                           || node.getParent().hasAttribute( '_cke_bookmark' );\r
        }\r
 \r
        var whitespaceEval = new CKEDITOR.dom.walker.whitespaces(),\r
@@ -348,7 +351,10 @@ CKEDITOR.dom.range = function( document )
                        this.collapsed = true;\r
                },\r
 \r
-               // The selection may be lost when cloning (due to the splitText() call).\r
+               /**\r
+                *  The content nodes of the range are cloned and added to a document fragment, which is returned.\r
+                *  <strong> Note: </strong> Text selection may lost after invoking this method. (caused by text node splitting).\r
+                */\r
                cloneContents : function()\r
                {\r
                        var docFrag = new CKEDITOR.dom.documentFragment( this.document );\r
@@ -359,6 +365,9 @@ CKEDITOR.dom.range = function( document )
                        return docFrag;\r
                },\r
 \r
+               /**\r
+                * Deletes the content nodes of the range permanently from the DOM tree.\r
+                */\r
                deleteContents : function()\r
                {\r
                        if ( this.collapsed )\r
@@ -367,6 +376,10 @@ CKEDITOR.dom.range = function( document )
                        execContentsAction( this, 0 );\r
                },\r
 \r
+               /**\r
+                *  The content nodes of the range are cloned and added to a document fragment,\r
+                * meanwhile they're removed permanently from the DOM tree.\r
+                */\r
                extractContents : function()\r
                {\r
                        var docFrag = new CKEDITOR.dom.documentFragment( this.document );\r
@@ -397,9 +410,10 @@ CKEDITOR.dom.range = function( document )
                        var startNode, endNode;\r
                        var baseId;\r
                        var clone;\r
+                       var collapsed = this.collapsed;\r
 \r
                        startNode = this.document.createElement( 'span' );\r
-                       startNode.setAttribute( '_fck_bookmark', 1 );\r
+                       startNode.setAttribute( '_cke_bookmark', 1 );\r
                        startNode.setStyle( 'display', 'none' );\r
 \r
                        // For IE, it must have something inside, otherwise it may be\r
@@ -413,7 +427,7 @@ CKEDITOR.dom.range = function( document )
                        }\r
 \r
                        // If collapsed, the endNode will not be created.\r
-                       if ( !this.collapsed )\r
+                       if ( !collapsed )\r
                        {\r
                                endNode = startNode.clone();\r
                                endNode.setHtml( '&nbsp;' );\r
@@ -442,7 +456,8 @@ CKEDITOR.dom.range = function( document )
                        return {\r
                                startNode : serializable ? baseId + 'S' : startNode,\r
                                endNode : serializable ? baseId + 'E' : endNode,\r
-                               serializable : serializable\r
+                               serializable : serializable,\r
+                               collapsed : collapsed\r
                        };\r
                },\r
 \r
@@ -465,6 +480,8 @@ CKEDITOR.dom.range = function( document )
                        var startOffset = this.startOffset,\r
                                endOffset       = this.endOffset;\r
 \r
+                       var collapsed = this.collapsed;\r
+\r
                        var child, previous;\r
 \r
                        // If there is no range then get out of here.\r
@@ -501,7 +518,7 @@ CKEDITOR.dom.range = function( document )
                                }\r
 \r
                                // Process the end only if not normalized.\r
-                               if ( !this.isCollapsed )\r
+                               if ( !collapsed )\r
                                {\r
                                        // Find out if the start is pointing to a text node that\r
                                        // will be normalized.\r
@@ -532,10 +549,11 @@ CKEDITOR.dom.range = function( document )
 \r
                        return {\r
                                start           : startContainer.getAddress( normalized ),\r
-                               end                     : this.isCollapsed ? null : endContainer.getAddress( normalized ),\r
+                               end                     : collapsed ? null : endContainer.getAddress( normalized ),\r
                                startOffset     : startOffset,\r
                                endOffset       : endOffset,\r
                                normalized      : normalized,\r
+                               collapsed       : collapsed,\r
                                is2                     : true          // It's a createBookmark2 bookmark.\r
                        };\r
                },\r
@@ -697,7 +715,7 @@ CKEDITOR.dom.range = function( document )
                },\r
 \r
                /**\r
-                * Move the range out of bookmark nodes if they're been the container.\r
+                * Move the range out of bookmark nodes if they'd been the container.\r
                 */\r
                optimizeBookmark: function()\r
                {\r
@@ -705,10 +723,10 @@ CKEDITOR.dom.range = function( document )
                                endNode = this.endContainer;\r
 \r
                        if ( startNode.is && startNode.is( 'span' )\r
-                               && startNode.hasAttribute( '_fck_bookmark' ) )\r
+                               && startNode.hasAttribute( '_cke_bookmark' ) )\r
                                this.setStartAt( startNode, CKEDITOR.POSITION_BEFORE_START );\r
                        if ( endNode && endNode.is && endNode.is( 'span' )\r
-                               && endNode.hasAttribute( '_fck_bookmark' ) )\r
+                               && endNode.hasAttribute( '_cke_bookmark' ) )\r
                                this.setEndAt( endNode,  CKEDITOR.POSITION_AFTER_END );\r
                },\r
 \r
@@ -914,7 +932,7 @@ CKEDITOR.dom.range = function( document )
                                                                // If this is a visible element.\r
                                                                // We need to check for the bookmark attribute because IE insists on\r
                                                                // rendering the display:none nodes we use for bookmarks. (#3363)\r
-                                                               if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_fck_bookmark' ) )\r
+                                                               if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_cke_bookmark' ) )\r
                                                                {\r
                                                                        // We'll accept it only if we need\r
                                                                        // whitespace, and this is an inline\r
@@ -1073,7 +1091,7 @@ CKEDITOR.dom.range = function( document )
                                                                // If this is a visible element.\r
                                                                // We need to check for the bookmark attribute because IE insists on\r
                                                                // rendering the display:none nodes we use for bookmarks. (#3363)\r
-                                                               if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_fck_bookmark' ) )\r
+                                                               if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_cke_bookmark' ) )\r
                                                                {\r
                                                                        // We'll accept it only if we need\r
                                                                        // whitespace, and this is an inline\r
@@ -1166,13 +1184,13 @@ CKEDITOR.dom.range = function( document )
 \r
                                        var walker = new CKEDITOR.dom.walker( walkerRange ),\r
                                            blockBoundary,  // The node on which the enlarging should stop.\r
-                                               tailBr, //\r
-                                           defaultGuard = CKEDITOR.dom.walker.blockBoundary(\r
+                                               tailBr, // In case BR as block boundary.\r
+                                           notBlockBoundary = CKEDITOR.dom.walker.blockBoundary(\r
                                                                ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br : 1 } : null ),\r
                                                // Record the encountered 'blockBoundary' for later use.\r
                                                boundaryGuard = function( node )\r
                                                {\r
-                                                       var retval = defaultGuard( node );\r
+                                                       var retval = notBlockBoundary( node );\r
                                                        if ( !retval )\r
                                                                blockBoundary = node;\r
                                                        return retval;\r
@@ -1193,8 +1211,9 @@ CKEDITOR.dom.range = function( document )
                                        // It's the body which stop the enlarging if no block boundary found.\r
                                        blockBoundary = blockBoundary || body;\r
 \r
-                                       // Start the range at different position by comparing\r
-                                       // the document position of it with 'enlargeable' node.\r
+                                       // Start the range either after the end of found block (<p>...</p>[text)\r
+                                       // or at the start of block (<p>[text...), by comparing the document position\r
+                                       // with 'enlargeable' node.\r
                                        this.setStartAt(\r
                                                        blockBoundary,\r
                                                        !blockBoundary.is( 'br' ) &&\r
@@ -1220,8 +1239,8 @@ CKEDITOR.dom.range = function( document )
                                        // It's the body which stop the enlarging if no block boundary found.\r
                                        blockBoundary = blockBoundary || body;\r
 \r
-                                       // Start the range at different position by comparing\r
-                                       // the document position of it with 'enlargeable' node.\r
+                                       // Close the range either before the found block start (text]<p>...</p>) or at the block end (...text]</p>)\r
+                                       // by comparing the document position with 'enlargeable' node.\r
                                        this.setEndAt(\r
                                                        blockBoundary,\r
                                                        ( !enlargeable && this.checkEndOfBlock()\r
@@ -1237,8 +1256,15 @@ CKEDITOR.dom.range = function( document )
 \r
                /**\r
                 *  Descrease the range to make sure that boundaries\r
-                *  always anchor beside text nodes or innermost element.\r
+               *  always anchor beside text nodes or innermost element.\r
                 * @param {Number} mode  ( CKEDITOR.SHRINK_ELEMENT | CKEDITOR.SHRINK_TEXT ) The shrinking mode.\r
+                * <dl>\r
+                *       <dt>CKEDITOR.SHRINK_ELEMENT</dt>\r
+                *       <dd>Shrink the range boundaries to the edge of the innermost element.</dd>\r
+                *       <dt>CKEDITOR.SHRINK_TEXT</dt>\r
+                *       <dd>Shrink the range boudaries to anchor by the side of enclosed text  node, range remains if there's no text nodes on boundaries at all.</dd>\r
+                 * </dl>\r
+                * @param {Boolean} selectContents Whether result range anchors at the inner OR outer boundary of the node.\r
                 */\r
                shrink : function( mode, selectContents )\r
                {\r
@@ -1287,7 +1313,8 @@ CKEDITOR.dom.range = function( document )
                                        }\r
                                }\r
 \r
-                               var walker = new CKEDITOR.dom.walker( walkerRange );\r
+                               var walker = new CKEDITOR.dom.walker( walkerRange ),\r
+                                       isBookmark = CKEDITOR.dom.walker.bookmark();\r
 \r
                                walker.evaluator = function( node )\r
                                {\r
@@ -1298,6 +1325,9 @@ CKEDITOR.dom.range = function( document )
                                var currentElement;\r
                                walker.guard = function( node, movingOut )\r
                                {\r
+                                       if ( isBookmark( node ) )\r
+                                               return true;\r
+\r
                                        // Stop when we're shrink in element mode while encountering a text node.\r
                                        if ( mode == CKEDITOR.SHRINK_ELEMENT && node.type == CKEDITOR.NODE_TEXT )\r
                                                return false;\r
@@ -1385,7 +1415,7 @@ CKEDITOR.dom.range = function( document )
                        // Fixing invalid range start inside dtd empty elements.\r
                        if( startNode.type == CKEDITOR.NODE_ELEMENT\r
                                && CKEDITOR.dtd.$empty[ startNode.getName() ] )\r
-                               startNode = startNode.getParent(), startOffset = startNode.getIndex();\r
+                               startOffset = startNode.getIndex(), startNode = startNode.getParent();\r
 \r
                        this.startContainer     = startNode;\r
                        this.startOffset        = startOffset;\r
@@ -1416,7 +1446,7 @@ CKEDITOR.dom.range = function( document )
                        // Fixing invalid range end inside dtd empty elements.\r
                        if( endNode.type == CKEDITOR.NODE_ELEMENT\r
                                && CKEDITOR.dtd.$empty[ endNode.getName() ] )\r
-                               endNode = endNode.getParent(), endOffset = endNode.getIndex() + 1;\r
+                               endOffset = endNode.getIndex() + 1, endNode = endNode.getParent();\r
 \r
                        this.endContainer       = endNode;\r
                        this.endOffset          = endOffset;\r
@@ -1624,26 +1654,36 @@ CKEDITOR.dom.range = function( document )
                },\r
 \r
                /**\r
-                * Check whether current range is on the inner edge of the specified element.\r
-                * @param {Number} checkType ( CKEDITOR.START | CKEDITOR.END ) The checking side.\r
+                * Check whether a range boundary is at the inner boundary of a given\r
+                * element.\r
                 * @param {CKEDITOR.dom.element} element The target element to check.\r
+                * @param {Number} checkType The boundary to check for both the range\r
+                *              and the element. It can be CKEDITOR.START or CKEDITOR.END.\r
+                * @returns {Boolean} "true" if the range boundary is at the inner\r
+                *              boundary of the element.\r
                 */\r
                checkBoundaryOfElement : function( element, checkType )\r
                {\r
+                       var checkStart = ( checkType == CKEDITOR.START );\r
+\r
+                       // Create a copy of this range, so we can manipulate it for our checks.\r
                        var walkerRange = this.clone();\r
+\r
+                       // Collapse the range at the proper size.\r
+                       walkerRange.collapse( checkStart );\r
+\r
                        // Expand the range to element boundary.\r
-                       walkerRange[ checkType == CKEDITOR.START ?\r
-                        'setStartAt' : 'setEndAt' ]\r
-                        ( element, checkType == CKEDITOR.START ?\r
-                          CKEDITOR.POSITION_AFTER_START\r
-                          : CKEDITOR.POSITION_BEFORE_END );\r
+                       walkerRange[ checkStart ? 'setStartAt' : 'setEndAt' ]\r
+                        ( element, checkStart ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_END );\r
 \r
-                       var walker = new CKEDITOR.dom.walker( walkerRange ),\r
-                        retval = false;\r
+                       // Create the walker, which will check if we have anything useful\r
+                       // in the range.\r
+                       var walker = new CKEDITOR.dom.walker( walkerRange );\r
                        walker.evaluator = elementBoundaryEval;\r
-                       return walker[ checkType == CKEDITOR.START ?\r
-                               'checkBackward' : 'checkForward' ]();\r
+\r
+                       return walker[ checkStart ? 'checkBackward' : 'checkForward' ]();\r
                },\r
+\r
                // Calls to this function may produce changes to the DOM. The range may\r
                // be updated to reflect such changes.\r
                checkStartOfBlock : function()\r
@@ -1791,7 +1831,7 @@ CKEDITOR.dom.range = function( document )
                {\r
                        var walkerRange = this.clone();\r
 \r
-                       // Optimize and analyze the range to avoid DOM destructive nature of walker. (#\r
+                       // Optimize and analyze the range to avoid DOM destructive nature of walker. (#5780)\r
                        walkerRange.optimize();\r
                        if ( walkerRange.startContainer.type != CKEDITOR.NODE_ELEMENT\r
                                        || walkerRange.endContainer.type != CKEDITOR.NODE_ELEMENT )\r
@@ -1841,13 +1881,13 @@ CKEDITOR.ENLARGE_ELEMENT = 1;
 CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2;\r
 CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3;\r
 \r
-/**\r
- * Check boundary types.\r
- * @see CKEDITOR.dom.range::checkBoundaryOfElement\r
- */\r
+// Check boundary types.\r
+// @see CKEDITOR.dom.range.prototype.checkBoundaryOfElement\r
 CKEDITOR.START = 1;\r
 CKEDITOR.END = 2;\r
 CKEDITOR.STARTEND = 3;\r
 \r
+// Shrink range types.\r
+// @see CKEDITOR.dom.range.prototype.shrink\r
 CKEDITOR.SHRINK_ELEMENT = 1;\r
 CKEDITOR.SHRINK_TEXT = 2;\r