X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fcore%2Fdom%2Frange.js;h=584648496c9f964244d1804ba29a5656b727e834;hb=c9fdde67e6384bd5a66adc2b3bba5c4ce9db56c7;hp=5b1681f850c41a5118ac98bb844b7e584a1c97fb;hpb=ea7e3453c7b0f023b050aca6d9f83ab372860d91;p=ckeditor.git diff --git a/_source/core/dom/range.js b/_source/core/dom/range.js index 5b1681f..5846484 100644 --- a/_source/core/dom/range.js +++ b/_source/core/dom/range.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -133,7 +133,7 @@ CKEDITOR.dom.range = function( document ) currentNode = levelStartNode.getNext(); - while( currentNode ) + while ( currentNode ) { // Stop processing when the current node matches a node in the // endParents tree or if it is the endNode. @@ -180,7 +180,7 @@ CKEDITOR.dom.range = function( document ) { currentNode = levelStartNode.getPrevious(); - while( currentNode ) + while ( currentNode ) { // Stop processing when the current node matches a node in the // startParents tree or if it is the startNode. @@ -252,10 +252,10 @@ CKEDITOR.dom.range = function( document ) } // Cleanup any marked node. - if( removeStartNode ) + if ( removeStartNode ) startNode.remove(); - if( removeEndNode && endNode.$.parentNode ) + if ( removeEndNode && endNode.$.parentNode ) endNode.remove(); }; @@ -278,7 +278,7 @@ CKEDITOR.dom.range = function( document ) if ( CKEDITOR.tools.trim( node.getText() ).length ) return false; } - else + else if ( node.type == CKEDITOR.NODE_ELEMENT ) { // If there are non-empty inline elements (e.g. ), then we're not // at the start. @@ -305,7 +305,16 @@ CKEDITOR.dom.range = function( document ) return node.type != CKEDITOR.NODE_TEXT && node.getName() in CKEDITOR.dtd.$removeEmpty || !CKEDITOR.tools.trim( node.getText() ) - || node.getParent().hasAttribute( '_fck_bookmark' ); + || node.getParent().hasAttribute( '_cke_bookmark' ); + } + + var whitespaceEval = new CKEDITOR.dom.walker.whitespaces(), + bookmarkEval = new CKEDITOR.dom.walker.bookmark(); + + function nonWhitespaceOrBookmarkEval( node ) + { + // Whitespaces and bookmark nodes are to be ignored. + return !whitespaceEval( node ) && !bookmarkEval( node ); } CKEDITOR.dom.range.prototype = @@ -388,9 +397,10 @@ CKEDITOR.dom.range = function( document ) var startNode, endNode; var baseId; var clone; + var collapsed = this.collapsed; startNode = this.document.createElement( 'span' ); - startNode.setAttribute( '_fck_bookmark', 1 ); + startNode.setAttribute( '_cke_bookmark', 1 ); startNode.setStyle( 'display', 'none' ); // For IE, it must have something inside, otherwise it may be @@ -404,7 +414,7 @@ CKEDITOR.dom.range = function( document ) } // If collapsed, the endNode will not be created. - if ( !this.collapsed ) + if ( !collapsed ) { endNode = startNode.clone(); endNode.setHtml( ' ' ); @@ -433,7 +443,8 @@ CKEDITOR.dom.range = function( document ) return { startNode : serializable ? baseId + 'S' : startNode, endNode : serializable ? baseId + 'E' : endNode, - serializable : serializable + serializable : serializable, + collapsed : collapsed }; }, @@ -456,6 +467,8 @@ CKEDITOR.dom.range = function( document ) var startOffset = this.startOffset, endOffset = this.endOffset; + var collapsed = this.collapsed; + var child, previous; // If there is no range then get out of here. @@ -492,7 +505,7 @@ CKEDITOR.dom.range = function( document ) } // Process the end only if not normalized. - if ( !this.isCollapsed ) + if ( !collapsed ) { // Find out if the start is pointing to a text node that // will be normalized. @@ -523,10 +536,11 @@ CKEDITOR.dom.range = function( document ) return { start : startContainer.getAddress( normalized ), - end : this.isCollapsed ? null : endContainer.getAddress( normalized ), + end : collapsed ? null : endContainer.getAddress( normalized ), startOffset : startOffset, endOffset : endOffset, normalized : normalized, + collapsed : collapsed, is2 : true // It's a createBookmark2 bookmark. }; }, @@ -688,7 +702,7 @@ CKEDITOR.dom.range = function( document ) }, /** - * Move the range out of bookmark nodes if they're been the container. + * Move the range out of bookmark nodes if they'd been the container. */ optimizeBookmark: function() { @@ -696,10 +710,10 @@ CKEDITOR.dom.range = function( document ) endNode = this.endContainer; if ( startNode.is && startNode.is( 'span' ) - && startNode.hasAttribute( '_fck_bookmark' ) ) + && startNode.hasAttribute( '_cke_bookmark' ) ) this.setStartAt( startNode, CKEDITOR.POSITION_BEFORE_START ); if ( endNode && endNode.is && endNode.is( 'span' ) - && endNode.hasAttribute( '_fck_bookmark' ) ) + && endNode.hasAttribute( '_cke_bookmark' ) ) this.setEndAt( endNode, CKEDITOR.POSITION_AFTER_END ); }, @@ -733,15 +747,21 @@ CKEDITOR.dom.range = function( document ) startOffset = startContainer.getIndex() + 1; startContainer = startContainer.getParent(); - // Check if it is necessary to update the end boundary. - if ( !collapsed && this.startContainer.equals( this.endContainer ) ) + + // Check all necessity of updating the end boundary. + if ( this.startContainer.equals( this.endContainer ) ) this.setEnd( nextText, this.endOffset - this.startOffset ); + else if ( startContainer.equals( this.endContainer ) ) + this.endOffset += 1; } this.setStart( startContainer, startOffset ); if ( collapsed ) + { this.collapse( true ); + return; + } } var endContainer = this.endContainer; @@ -899,7 +919,7 @@ CKEDITOR.dom.range = function( document ) // If this is a visible element. // We need to check for the bookmark attribute because IE insists on // rendering the display:none nodes we use for bookmarks. (#3363) - if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_fck_bookmark' ) ) + if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_cke_bookmark' ) ) { // We'll accept it only if we need // whitespace, and this is an inline @@ -910,7 +930,7 @@ CKEDITOR.dom.range = function( document ) siblingText = sibling.getText(); - if ( !(/[^\s\ufeff]/).test( siblingText ) ) // Spaces + Zero Width No-Break Space (U+FEFF) + if ( (/[^\s\ufeff]/).test( siblingText ) ) // Spaces + Zero Width No-Break Space (U+FEFF) sibling = null; else { @@ -1058,7 +1078,7 @@ CKEDITOR.dom.range = function( document ) // If this is a visible element. // We need to check for the bookmark attribute because IE insists on // rendering the display:none nodes we use for bookmarks. (#3363) - if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_fck_bookmark' ) ) + if ( sibling.$.offsetWidth > 0 && !sibling.getAttribute( '_cke_bookmark' ) ) { // We'll accept it only if we need // whitespace, and this is an inline @@ -1069,7 +1089,7 @@ CKEDITOR.dom.range = function( document ) siblingText = sibling.getText(); - if ( !(/[^\s\ufeff]/).test( siblingText ) ) + if ( (/[^\s\ufeff]/).test( siblingText ) ) sibling = null; else { @@ -1151,13 +1171,13 @@ CKEDITOR.dom.range = function( document ) var walker = new CKEDITOR.dom.walker( walkerRange ), blockBoundary, // The node on which the enlarging should stop. - tailBr, // - defaultGuard = CKEDITOR.dom.walker.blockBoundary( + tailBr, // In case BR as block boundary. + notBlockBoundary = CKEDITOR.dom.walker.blockBoundary( ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br : 1 } : null ), // Record the encountered 'blockBoundary' for later use. boundaryGuard = function( node ) { - var retval = defaultGuard( node ); + var retval = notBlockBoundary( node ); if ( !retval ) blockBoundary = node; return retval; @@ -1178,12 +1198,14 @@ CKEDITOR.dom.range = function( document ) // It's the body which stop the enlarging if no block boundary found. blockBoundary = blockBoundary || body; - // Start the range at different position by comparing - // the document position of it with 'enlargeable' node. + // Start the range either after the end of found block (

...

[text) + // or at the start of block (

[text...), by comparing the document position + // with 'enlargeable' node. this.setStartAt( blockBoundary, !blockBoundary.is( 'br' ) && - ( !enlargeable || blockBoundary.contains( enlargeable ) ) ? + ( !enlargeable && this.checkStartOfBlock() + || enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END ); @@ -1204,12 +1226,12 @@ CKEDITOR.dom.range = function( document ) // It's the body which stop the enlarging if no block boundary found. blockBoundary = blockBoundary || body; - // Start the range at different position by comparing - // the document position of it with 'enlargeable' node. + // Close the range either before the found block start (text]

...

) or at the block end (...text]

) + // by comparing the document position with 'enlargeable' node. this.setEndAt( blockBoundary, - !blockBoundary.is( 'br' ) && - ( !enlargeable || blockBoundary.contains( enlargeable ) ) ? + ( !enlargeable && this.checkEndOfBlock() + || enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_BEFORE_END : CKEDITOR.POSITION_BEFORE_START ); // We must include the
at the end of range if there's @@ -1220,6 +1242,107 @@ CKEDITOR.dom.range = function( document ) }, /** + * Descrease the range to make sure that boundaries + * always anchor beside text nodes or innermost element. + * @param {Number} mode ( CKEDITOR.SHRINK_ELEMENT | CKEDITOR.SHRINK_TEXT ) The shrinking mode. + *
+ *
CKEDITOR.SHRINK_ELEMENT
+ *
Shrink the range boundaries to the edge of the innermost element.
+ *
CKEDITOR.SHRINK_TEXT
+ *
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.
+ *
+ * @param {Boolean} selectContents Whether result range anchors at the inner OR outer boundary of the node. + */ + shrink : function( mode, selectContents ) + { + // Unable to shrink a collapsed range. + if ( !this.collapsed ) + { + mode = mode || CKEDITOR.SHRINK_TEXT; + + var walkerRange = this.clone(); + + var startContainer = this.startContainer, + endContainer = this.endContainer, + startOffset = this.startOffset, + endOffset = this.endOffset, + collapsed = this.collapsed; + + // Whether the start/end boundary is moveable. + var moveStart = 1, + moveEnd = 1; + + if ( startContainer && startContainer.type == CKEDITOR.NODE_TEXT ) + { + if ( !startOffset ) + walkerRange.setStartBefore( startContainer ); + else if ( startOffset >= startContainer.getLength( ) ) + walkerRange.setStartAfter( startContainer ); + else + { + // Enlarge the range properly to avoid walker making + // DOM changes caused by triming the text nodes later. + walkerRange.setStartBefore( startContainer ); + moveStart = 0; + } + } + + if ( endContainer && endContainer.type == CKEDITOR.NODE_TEXT ) + { + if ( !endOffset ) + walkerRange.setEndBefore( endContainer ); + else if ( endOffset >= endContainer.getLength( ) ) + walkerRange.setEndAfter( endContainer ); + else + { + walkerRange.setEndAfter( endContainer ); + moveEnd = 0; + } + } + + var walker = new CKEDITOR.dom.walker( walkerRange ); + + walker.evaluator = function( node ) + { + return node.type == ( mode == CKEDITOR.SHRINK_ELEMENT ? + CKEDITOR.NODE_ELEMENT : CKEDITOR.NODE_TEXT ); + }; + + var currentElement; + walker.guard = function( node, movingOut ) + { + // Stop when we're shrink in element mode while encountering a text node. + if ( mode == CKEDITOR.SHRINK_ELEMENT && node.type == CKEDITOR.NODE_TEXT ) + return false; + + // Stop when we've already walked "through" an element. + if ( movingOut && node.equals( currentElement ) ) + return false; + + if ( !movingOut && node.type == CKEDITOR.NODE_ELEMENT ) + currentElement = node; + + return true; + }; + + if ( moveStart ) + { + var textStart = walker[ mode == CKEDITOR.SHRINK_ELEMENT ? 'lastForward' : 'next'](); + textStart && this.setStartAt( textStart, selectContents ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_START ); + } + + if ( moveEnd ) + { + walker.reset(); + var textEnd = walker[ mode == CKEDITOR.SHRINK_ELEMENT ? 'lastBackward' : 'previous'](); + textEnd && this.setEndAt( textEnd, selectContents ? CKEDITOR.POSITION_BEFORE_END : CKEDITOR.POSITION_AFTER_END ); + } + + return !!( moveStart || moveEnd ); + } + }, + + /** * Inserts a node at the start of the range. The range will be expanded * the contain the node. */ @@ -1272,6 +1395,11 @@ CKEDITOR.dom.range = function( document ) // we will not need this check for our use of this class so we can // ignore it for now. + // Fixing invalid range start inside dtd empty elements. + if( startNode.type == CKEDITOR.NODE_ELEMENT + && CKEDITOR.dtd.$empty[ startNode.getName() ] ) + startNode = startNode.getParent(), startOffset = startNode.getIndex(); + this.startContainer = startNode; this.startOffset = startOffset; @@ -1298,6 +1426,11 @@ CKEDITOR.dom.range = function( document ) // will not need this check for our use of this class so we can ignore // it for now. + // Fixing invalid range end inside dtd empty elements. + if( endNode.type == CKEDITOR.NODE_ELEMENT + && CKEDITOR.dtd.$empty[ endNode.getName() ] ) + endNode = endNode.getParent(), endOffset = endNode.getIndex() + 1; + this.endContainer = endNode; this.endOffset = endOffset; @@ -1457,18 +1590,7 @@ CKEDITOR.dom.range = function( document ) } else { - // Extract the contents of the block from the selection point to the end - // of its contents. - this.setEndAt( startBlock, CKEDITOR.POSITION_BEFORE_END ); - var documentFragment = this.extractContents(); - - // Duplicate the block element after it. - endBlock = startBlock.clone( false ); - - // Place the extracted contents into the duplicated block. - documentFragment.appendTo( endBlock ); - endBlock.insertAfter( startBlock ); - this.moveToPosition( startBlock, CKEDITOR.POSITION_AFTER_END ); + endBlock = this.splitElement( startBlock ); // In Gecko, the last child node must be a bogus
. // Note: bogus
added under