Skip to content

Commit c40b595

Browse files
author
garry.yao
committed
Fixing #3608: Introducing CKEDITOR.dom.walker.bookmark to skip bookmark nodes and optimize range::enlage to avoid particial element selection.
git-svn-id: https://svn.ckeditor.com/CKEditor/trunk@3607 da63caf2-3823-0410-a1e8-d69ccee00dfb
1 parent 2ce2cfc commit c40b595

File tree

4 files changed

+92
-50
lines changed

4 files changed

+92
-50
lines changed

_source/core/dom/range.js

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,13 @@ CKEDITOR.dom.range = function( document ) {
233233
// Creates the appropriate node evaluator for the dom walker used inside
234234
// check(Start|End)OfBlock.
235235
function getCheckStartEndBlockEvalFunction( isStart ) {
236-
var hadBr = false;
236+
var hadBr = false,
237+
bookmarkEvaluator = CKEDITOR.dom.walker.bookmark( true );
237238
return function( node ) {
239+
// First ignore bookmark nodes.
240+
if ( bookmarkEvaluator( node ) )
241+
return true;
242+
238243
if ( node.type == CKEDITOR.NODE_TEXT ) {
239244
// If there's any visible text, then we're not at the start.
240245
if ( CKEDITOR.tools.trim( node.getText() ).length )
@@ -975,34 +980,62 @@ CKEDITOR.dom.range = function( document ) {
975980
case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:
976981

977982
// Enlarging the start boundary.
978-
var walkerRange = new CKEDITOR.dom.range( this.document );
979-
walkerRange.setStartAt( this.document.getBody(), CKEDITOR.POSITION_AFTER_START );
983+
var walkerRange = new CKEDITOR.dom.range( this.document ),
984+
body = this.document.getBody();
985+
walkerRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
980986
walkerRange.setEnd( this.startContainer, this.startOffset );
981987

982988
var walker = new CKEDITOR.dom.walker( walkerRange ),
983-
984-
guard = CKEDITOR.dom.walker.blockBoundary(
989+
blockBoundary, // The node on which the enlarging should stop.
990+
tailBr, //
991+
defaultGuard = CKEDITOR.dom.walker.blockBoundary(
985992
( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br:1 } : null ),
986-
tailBr,
987-
listGuard = function( node ) {
988-
var result = guard( node );
989-
if ( !result && node.is && node.is( 'br' ) )
993+
// Record the encountered 'blockBoundary' for later use.
994+
boundaryGuard = function( node ) {
995+
var retval = defaultGuard( node );
996+
if ( !retval )
997+
blockBoundary = node;
998+
return retval;
999+
},
1000+
// Record the encounted 'tailBr' for later use.
1001+
tailBrGuard = function( node ) {
1002+
var retval = boundaryGuard( node );
1003+
if ( !retval && node.is && node.is( 'br' ) )
9901004
tailBr = node;
991-
return result;
1005+
return retval;
9921006
};
993-
walker.guard = guard;
9941007

995-
if ( ( enlargeable = walker.lastBackward() ) )
996-
this.setStartAt( enlargeable, CKEDITOR.POSITION_BEFORE_START );
1008+
walker.guard = boundaryGuard;
1009+
1010+
1011+
if ( ( enlargeable = walker.lastBackward() ) ) {
1012+
// It's the body which stop the enlaring if no block boundary found.
1013+
blockBoundary = blockBoundary || body;
1014+
1015+
// Start the range at different position by comparing
1016+
// the document position of it with 'enlargeable' node.
1017+
this.setStartAt( blockBoundary, blockBoundary.contains( enlargeable ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END );
1018+
}
9971019

9981020
// Enlarging the end boundary.
9991021
walkerRange = this.clone();
10001022
walkerRange.collapse();
1001-
walkerRange.setEndAt( this.document.getBody(), CKEDITOR.POSITION_BEFORE_END );
1023+
walkerRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
10021024
walker = new CKEDITOR.dom.walker( walkerRange );
1003-
walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? listGuard : guard;
1004-
if ( ( enlargeable = walker.lastForward() ) )
1005-
this.setEndAfter( enlargeable );
1025+
1026+
// tailBrGuard only used for on range end.
1027+
walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? tailBrGuard : boundaryGuard;
1028+
blockBoundary = null;
1029+
// End the range right before the block boundary node.
1030+
;
1031+
if ( ( enlargeable = walker.lastForward() ) ) {
1032+
// It's the body which stop the enlaring if no block boundary found.
1033+
blockBoundary = blockBoundary || body;
1034+
1035+
// Start the range at different position by comparing
1036+
// the document position of it with 'enlargeable' node.
1037+
this.setEndAt( blockBoundary, blockBoundary.contains( enlargeable ) ? CKEDITOR.POSITION_BEFORE_END : CKEDITOR.POSITION_BEFORE_START );
1038+
}
10061039
// We must include the <br> at the end of range if there's
10071040
// one and we're expanding list item contents
10081041
if ( tailBr )

_source/core/dom/walker.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,5 +293,32 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
293293
CKEDITOR.dom.walker.listItemBoundary = function() {
294294
return this.blockBoundary( { br:1 } );
295295
};
296+
/**
297+
* Whether the node is a bookmark node's inner text node.
298+
*/
299+
CKEDITOR.dom.walker.bookmarkContents = function( node ) {},
300+
301+
/**
302+
* Whether the to-be-evaluated node is a bookmark node OR bookmark node
303+
* inner contents.
304+
* @param {Boolean} contentOnly Whether only test againt the text content of
305+
* bookmark node instead of the element itself(default).
306+
* @param {Boolean} isReject Whether should return 'false' for the bookmark
307+
* node instead of 'true'(default).
308+
*/
309+
CKEDITOR.dom.walker.bookmark = function( contentOnly, isReject ) {
310+
function isBookmarkNode( node ) {
311+
return ( node && node.getName && node.getName() == 'span' && node.hasAttribute( '_fck_bookmark' ) );
312+
}
313+
314+
return function( node ) {
315+
var retval, parent;
316+
// Is bookmark inner text node?
317+
retval = ( node && !node.getName && ( parent = node.getParent() ) && isBookmarkNode( parent ) );
318+
// Is bookmark node?
319+
retval = contentOnly ? retval : retval || isBookmarkNode( node );
320+
return isReject ? !retval : !!retval;
321+
};
322+
};
296323

297324
})();

_source/plugins/domiterator/plugin.js

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,6 @@ CKEDITOR.plugins.add( 'domiterator' );
1111

1212
(function() {
1313

14-
function isBookmarkNode( node ) {
15-
return ( node && node.getName && node.getName() == 'span' && node.hasAttribute( '_fck_bookmark' ) ) || ( node && !node.getName && isBookmarkNode( node.getParent() ) );
16-
}
17-
18-
function ignoreBookmarkEvaluator( node ) {
19-
return !isBookmarkNode( node );
20-
}
21-
22-
23-
/**
24-
* Find next source order node, ignore bookmark nodes and stop at the specified end node.
25-
* @param {Object} currentNode
26-
* @param {Object} endNode
27-
*/
28-
function getNextSourceNode( currentNode, endNode, startFromSibling ) {
29-
var next = currentNode;
30-
do {
31-
next = next.getNextSourceNode( startFromSibling, null, endNode );
32-
}
33-
while ( isBookmarkNode( next ) )
34-
return next;
35-
}
36-
3714
var iterator = function( range ) {
3815
if ( arguments.length < 1 )
3916
return;
@@ -66,15 +43,16 @@ CKEDITOR.plugins.add( 'domiterator' );
6643
range = this.range.clone();
6744
range.enlarge( this.forceBrBreak ? CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
6845

69-
var walker = new CKEDITOR.dom.walker( range );
70-
walker.evaluator = ignoreBookmarkEvaluator;
46+
var walker = new CKEDITOR.dom.walker( range ),
47+
ignoreBookmarkTextEvaluator = CKEDITOR.dom.walker.bookmark( true, true );
48+
// Avoid anchor inside bookmark inner text.
49+
walker.evaluator = ignoreBookmarkTextEvaluator;
7150
this._.nextNode = walker.next();
72-
7351
// TODO: It's better to have walker.reset() used here.
7452
walker = new CKEDITOR.dom.walker( range );
75-
walker.evaluator = ignoreBookmarkEvaluator;
53+
walker.evaluator = ignoreBookmarkTextEvaluator;
7654
var lastNode = walker.previous();
77-
this._.lastNode = getNextSourceNode( lastNode, null, true );
55+
this._.lastNode = lastNode.getNextSourceNode( true );
7856
// Probably the document end is reached, we need a marker node.
7957
if ( !this._.lastNode ) {
8058
this._.lastNode = range.document.createText( '' );
@@ -88,7 +66,6 @@ CKEDITOR.plugins.add( 'domiterator' );
8866
lastNode = this._.lastNode;
8967

9068
this._.nextNode = null;
91-
9269
while ( currentNode ) {
9370
// closeRange indicates that a paragraph boundary has been found,
9471
// so the range can be closed.
@@ -198,7 +175,7 @@ CKEDITOR.plugins.add( 'domiterator' );
198175
if ( isLast )
199176
break;
200177

201-
currentNode = getNextSourceNode( currentNode, lastNode, continueFromSibling );
178+
currentNode = currentNode.getNextSourceNode( continueFromSibling, null, lastNode );
202179
}
203180

204181
// Now, based on the processed range, look for (or create) the block to be returned.
@@ -256,7 +233,7 @@ CKEDITOR.plugins.add( 'domiterator' );
256233
// the current range, which could be an <li> child (nested
257234
// lists) or the next sibling <li>.
258235

259-
this._.nextNode = ( block.equals( lastNode ) ? null : getNextSourceNode( range.getBoundaryNodes().endNode, lastNode, true ) );
236+
this._.nextNode = ( block.equals( lastNode ) ? null : range.getBoundaryNodes().endNode.getNextSourceNode( true, null, lastNode ) );
260237
}
261238
}
262239

@@ -283,7 +260,7 @@ CKEDITOR.plugins.add( 'domiterator' );
283260
// above block can be removed or changed, so we can rely on it for the
284261
// next interation.
285262
if ( !this._.nextNode ) {
286-
this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null : getNextSourceNode( block, lastNode, true );
263+
this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null : block.getNextSourceNode( true, null, lastNode );
287264
}
288265

289266
return block;

_source/plugins/list/plugin.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
326326
var paragraph = doc.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : ( editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'br' ) );
327327
paragraph.appendTo( body );
328328
ranges = [ new CKEDITOR.dom.range( doc ) ];
329-
ranges[ 0 ].selectNodeContents( paragraph );
329+
// IE exception on inserting anything when anchor inside <br>.
330+
if ( paragraph.is( 'br' ) ) {
331+
ranges[ 0 ].setStartBefore( paragraph );
332+
ranges[ 0 ].setEndAfter( paragraph );
333+
} else
334+
ranges[ 0 ].selectNodeContents( paragraph );
330335
selection.selectRanges( ranges );
331336
}
332337
}

0 commit comments

Comments
 (0)