X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=_source%2Fcore%2Fdom%2Frange.js;h=fedbfb97e45684a2612f0d548a5921c3e1fe1fe6;hb=48b1db88210b4160dce439c6e3e32e14af8c106b;hp=3d8b10f0f31a50b2ff1c9af69466f228d25358aa;hpb=055b6b0792ce7dc53d47af606b367c04b927c2ab;p=ckeditor.git
diff --git a/_source/core/dom/range.js b/_source/core/dom/range.js
index 3d8b10f..fedbfb9 100644
--- a/_source/core/dom/range.js
+++ b/_source/core/dom/range.js
@@ -1,8 +1,11 @@
/*
-Copyright (c) 2003-2010, 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
*/
+/**
+ * @class
+ */
CKEDITOR.dom.range = function( document )
{
this.startContainer = null;
@@ -29,7 +32,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();
@@ -244,7 +247,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.
@@ -277,7 +290,7 @@ CKEDITOR.dom.range = function( document )
// If there's any visible text, then we're not at the start.
if ( CKEDITOR.tools.trim( node.getText() ).length )
return false;
- }
+ }
else if ( node.type == CKEDITOR.NODE_ELEMENT )
{
// If there are non-empty inline elements (e.g. ), then we're not
@@ -305,7 +318,7 @@ 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(),
@@ -348,7 +361,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 );
@@ -359,20 +375,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;
},
@@ -397,9 +422,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
@@ -413,7 +439,7 @@ CKEDITOR.dom.range = function( document )
}
// If collapsed, the endNode will not be created.
- if ( !this.collapsed )
+ if ( !collapsed )
{
endNode = startNode.clone();
endNode.setHtml( ' ' );
@@ -442,7 +468,8 @@ CKEDITOR.dom.range = function( document )
return {
startNode : serializable ? baseId + 'S' : startNode,
endNode : serializable ? baseId + 'E' : endNode,
- serializable : serializable
+ serializable : serializable,
+ collapsed : collapsed
};
},
@@ -465,6 +492,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.
@@ -501,7 +530,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.
@@ -532,10 +561,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.
};
},
@@ -705,10 +735,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 );
},
@@ -793,7 +823,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 )
{
@@ -914,7 +949,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
@@ -1073,7 +1109,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
@@ -1295,7 +1332,8 @@ CKEDITOR.dom.range = function( document )
}
}
- var walker = new CKEDITOR.dom.walker( walkerRange );
+ var walker = new CKEDITOR.dom.walker( walkerRange ),
+ isBookmark = CKEDITOR.dom.walker.bookmark();
walker.evaluator = function( node )
{
@@ -1306,6 +1344,9 @@ CKEDITOR.dom.range = function( document )
var currentElement;
walker.guard = function( node, movingOut )
{
+ if ( isBookmark( node ) )
+ return true;
+
// 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;
@@ -1393,7 +1434,7 @@ CKEDITOR.dom.range = function( document )
// 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();
+ startOffset = startNode.getIndex(), startNode = startNode.getParent();
this.startContainer = startNode;
this.startOffset = startOffset;
@@ -1424,7 +1465,7 @@ CKEDITOR.dom.range = function( document )
// 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;
+ endOffset = endNode.getIndex() + 1, endNode = endNode.getParent();
this.endContainer = endNode;
this.endOffset = endOffset;
@@ -1632,24 +1673,34 @@ CKEDITOR.dom.range = function( document )
},
/**
- * Check whether current range is on the inner edge of the specified element.
- * @param {Number} checkType ( CKEDITOR.START | CKEDITOR.END ) The checking side.
+ * Check whether a range boundary is at the inner boundary of a given
+ * element.
* @param {CKEDITOR.dom.element} element The target element to check.
+ * @param {Number} checkType The boundary to check for both the range
+ * and the element. It can be CKEDITOR.START or CKEDITOR.END.
+ * @returns {Boolean} "true" if the range boundary is at the inner
+ * boundary of the element.
*/
checkBoundaryOfElement : function( element, checkType )
{
+ var checkStart = ( checkType == CKEDITOR.START );
+
+ // Create a copy of this range, so we can manipulate it for our checks.
var walkerRange = this.clone();
+
+ // Collapse the range at the proper size.
+ walkerRange.collapse( checkStart );
+
// Expand the range to element boundary.
- walkerRange[ checkType == CKEDITOR.START ?
- 'setStartAt' : 'setEndAt' ]
- ( element, checkType == CKEDITOR.START ?
- CKEDITOR.POSITION_AFTER_START
- : CKEDITOR.POSITION_BEFORE_END );
+ walkerRange[ checkStart ? 'setStartAt' : 'setEndAt' ]
+ ( element, checkStart ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_END );
+ // Create the walker, which will check if we have anything useful
+ // in the range.
var walker = new CKEDITOR.dom.walker( walkerRange );
walker.evaluator = elementBoundaryEval;
- return walker[ checkType == CKEDITOR.START ?
- 'checkBackward' : 'checkForward' ]();
+
+ return walker[ checkStart ? 'checkBackward' : 'checkForward' ]();
},
// Calls to this function may produce changes to the DOM. The range may
@@ -1723,6 +1774,47 @@ CKEDITOR.dom.range = function( document )
},
/**
+ * Check if elements at which the range boundaries anchor are read-only,
+ * with respect to "contenteditable" attribute.
+ */
+ checkReadOnly : ( function()
+ {
+ function checkNodesEditable( node, anotherEnd )
+ {
+ while( node )
+ {
+ if ( node.type == CKEDITOR.NODE_ELEMENT )
+ {
+ if ( node.getAttribute( 'contentEditable' ) == 'false'
+ && !node.data( 'cke-editable' ) )
+ {
+ return 0;
+ }
+ // Range enclosed entirely in an editable element.
+ else if ( node.is( 'body' )
+ || node.getAttribute( 'contentEditable' ) == 'true'
+ && ( node.contains( anotherEnd ) || node.equals( anotherEnd ) ) )
+ {
+ break;
+ }
+ }
+ node = node.getParent();
+ }
+
+ return 1;
+ }
+
+ return function()
+ {
+ var startNode = this.startContainer,
+ endNode = this.endContainer;
+
+ // Check if elements path at both boundaries are editable.
+ return !( checkNodesEditable( startNode, endNode ) && checkNodesEditable( endNode, startNode ) );
+ };
+ })(),
+
+ /**
* Moves the range boundaries to the first/end editing point inside an
* element. For example, in an element tree like
* "<p><b><i></i></b> Text</p>", the start editing point is
@@ -1849,17 +1941,13 @@ CKEDITOR.ENLARGE_ELEMENT = 1;
CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2;
CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3;
-/**
- * Check boundary types.
- * @see CKEDITOR.dom.range.prototype.checkBoundaryOfElement
- */
+// Check boundary types.
+// @see CKEDITOR.dom.range.prototype.checkBoundaryOfElement
CKEDITOR.START = 1;
CKEDITOR.END = 2;
CKEDITOR.STARTEND = 3;
-/**
- * Shrink range types.
- * @see CKEDITOR.dom.range.prototype.shrink
- */
+// Shrink range types.
+// @see CKEDITOR.dom.range.prototype.shrink
CKEDITOR.SHRINK_ELEMENT = 1;
CKEDITOR.SHRINK_TEXT = 2;