X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=_source%2Fcore%2Fdom%2Frange.js;h=477197ef053ced2de6cf8cd9d9a66d0be11bf83e;hb=4e70ea24db840898be8cc21c950363a52a2a6aba;hp=5b1681f850c41a5118ac98bb844b7e584a1c97fb;hpb=ea7e3453c7b0f023b050aca6d9f83ab372860d91;p=ckeditor.git
diff --git a/_source/core/dom/range.js b/_source/core/dom/range.js
index 5b1681f..477197e 100644
--- a/_source/core/dom/range.js
+++ b/_source/core/dom/range.js
@@ -1,16 +1,90 @@
/*
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
+/**
+ * Creates a CKEDITOR.dom.range instance that can be used inside a specific
+ * DOM Document.
+ * @class Represents a delimited piece of content in a DOM Document.
+ * It is contiguous in the sense that it can be characterized as selecting all
+ * of the content between a pair of boundary-points.
+ *
+ * This class shares much of the W3C
+ * Document Object Model Range
+ * ideas and features, adding several range manipulation tools to it, but it's
+ * not intended to be compatible with it.
+ * @param {CKEDITOR.dom.document} document The document into which the range
+ * features will be available.
+ * @example
+ * // Create a range for the entire contents of the editor document body.
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * // Delete the contents.
+ * range.deleteContents();
+ */
CKEDITOR.dom.range = function( document )
{
+ /**
+ * Node within which the range begins.
+ * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.startContainer.getName() ); // "body"
+ */
this.startContainer = null;
+
+ /**
+ * Offset within the starting node of the range.
+ * @type {Number}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.startOffset ); // "0"
+ */
this.startOffset = null;
+
+ /**
+ * Node within which the range ends.
+ * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.endContainer.getName() ); // "body"
+ */
this.endContainer = null;
+
+ /**
+ * Offset within the ending node of the range.
+ * @type {Number}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.endOffset ); // == editor.document.getBody().getChildCount()
+ */
this.endOffset = null;
+
+ /**
+ * Indicates that this is a collapsed range. A collapsed range has it's
+ * start and end boudaries at the very same point so nothing is contained
+ * in it.
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.collapsed ); // "false"
+ * range.collapse();
+ * alert( range.collapsed ); // "true"
+ */
this.collapsed = true;
+ /**
+ * The document within which the range can be used.
+ * @type {CKEDITOR.dom.document}
+ * @example
+ * // Selects the body contents of the range document.
+ * range.selectNodeContents( range.document.getBody() );
+ */
this.document = document;
};
@@ -29,7 +103,7 @@ CKEDITOR.dom.range = function( document )
// This is a shared function used to delete, extract and clone the range
// contents.
// V2
- var execContentsAction = function( range, action, docFrag )
+ var execContentsAction = function( range, action, docFrag, mergeThen )
{
range.optimizeBookmark();
@@ -133,7 +207,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 +254,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.
@@ -244,7 +318,17 @@ CKEDITOR.dom.range = function( document )
if ( removeStartNode && topEnd.$.parentNode == startNode.$.parentNode )
endIndex--;
- range.setStart( topEnd.getParent(), endIndex );
+ // Merge splitted parents.
+ if ( mergeThen && topStart.type == CKEDITOR.NODE_ELEMENT )
+ {
+ var span = CKEDITOR.dom.element.createFromHtml( ' ', range.document );
+ span.insertAfter( topStart );
+ topStart.mergeSiblings( false );
+ range.moveToBookmark( { startNode : span } );
+ }
+ else
+ range.setStart( topEnd.getParent(), endIndex );
}
// Collapse it to the start.
@@ -252,10 +336,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();
};
@@ -275,10 +359,10 @@ CKEDITOR.dom.range = function( document )
if ( node.type == CKEDITOR.NODE_TEXT )
{
// If there's any visible text, then we're not at the start.
- if ( CKEDITOR.tools.trim( node.getText() ).length )
+ if ( node.hasAscendant( 'pre' ) || 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 +389,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().data( '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 =
@@ -339,7 +432,10 @@ CKEDITOR.dom.range = function( document )
this.collapsed = true;
},
- // The selection may be lost when cloning (due to the splitText() call).
+ /**
+ * The content nodes of the range are cloned and added to a document fragment, which is returned.
+ * Note: Text selection may lost after invoking this method. (caused by text node splitting).
+ */
cloneContents : function()
{
var docFrag = new CKEDITOR.dom.documentFragment( this.document );
@@ -350,20 +446,29 @@ CKEDITOR.dom.range = function( document )
return docFrag;
},
- deleteContents : function()
+ /**
+ * Deletes the content nodes of the range permanently from the DOM tree.
+ * @param {Boolean} [mergeThen] Merge any splitted elements result in DOM true due to partial selection.
+ */
+ deleteContents : function( mergeThen )
{
if ( this.collapsed )
return;
- execContentsAction( this, 0 );
+ execContentsAction( this, 0, null, mergeThen );
},
- extractContents : function()
+ /**
+ * The content nodes of the range are cloned and added to a document fragment,
+ * meanwhile they're removed permanently from the DOM tree.
+ * @param {Boolean} [mergeThen] Merge any splitted elements result in DOM true due to partial selection.
+ */
+ extractContents : function( mergeThen )
{
var docFrag = new CKEDITOR.dom.documentFragment( this.document );
if ( !this.collapsed )
- execContentsAction( this, 1, docFrag );
+ execContentsAction( this, 1, docFrag, mergeThen );
return docFrag;
},
@@ -388,9 +493,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.data( 'cke-bookmark', 1 );
startNode.setStyle( 'display', 'none' );
// For IE, it must have something inside, otherwise it may be
@@ -404,7 +510,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 +539,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 +563,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.
@@ -480,6 +589,10 @@ CKEDITOR.dom.range = function( document )
startContainer = child;
startOffset = 0;
}
+
+ // Get the normalized offset.
+ if ( child && child.type == CKEDITOR.NODE_ELEMENT )
+ startOffset = child.getIndex( 1 );
}
// Normalize the start.
@@ -492,7 +605,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.
@@ -508,6 +621,10 @@ CKEDITOR.dom.range = function( document )
endContainer = child;
endOffset = 0;
}
+
+ // Get the normalized offset.
+ if ( child && child.type == CKEDITOR.NODE_ELEMENT )
+ endOffset = child.getIndex( 1 );
}
// Normalize the end.
@@ -523,10 +640,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 +806,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 +814,10 @@ CKEDITOR.dom.range = function( document )
endNode = this.endContainer;
if ( startNode.is && startNode.is( 'span' )
- && startNode.hasAttribute( '_fck_bookmark' ) )
+ && startNode.data( 'cke-bookmark' ) )
this.setStartAt( startNode, CKEDITOR.POSITION_BEFORE_START );
if ( endNode && endNode.is && endNode.is( 'span' )
- && endNode.hasAttribute( '_fck_bookmark' ) )
+ && endNode.data( 'cke-bookmark' ) )
this.setEndAt( endNode, CKEDITOR.POSITION_AFTER_END );
},
@@ -733,15 +851,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;
@@ -778,7 +902,12 @@ CKEDITOR.dom.range = function( document )
}
},
- enlarge : function( unit )
+ /**
+ * Expands the range so that partial units are completely contained.
+ * @param unit {Number} The unit type to expand with.
+ * @param {Boolean} [excludeBrs=false] Whether include line-breaks when expanding.
+ */
+ enlarge : function( unit, excludeBrs )
{
switch ( unit )
{
@@ -899,7 +1028,8 @@ 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' ) )
+ // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+ if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) )
{
// We'll accept it only if we need
// whitespace, and this is an inline
@@ -910,7 +1040,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 +1188,8 @@ 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' ) )
+ // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+ if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) )
{
// We'll accept it only if we need
// whitespace, and this is an inline
@@ -1069,7 +1200,7 @@ CKEDITOR.dom.range = function( document )
siblingText = sibling.getText();
- if ( !(/[^\s\ufeff]/).test( siblingText ) )
+ if ( (/[^\s\ufeff]/).test( siblingText ) )
sibling = null;
else
{
@@ -1151,13 +1282,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 +1309,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 +1337,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