/*\r
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
// This function is to be called under a "walker" instance scope.\r
function iterate( rtl, breakOnFalse )\r
{\r
+ var range = this.range;\r
+\r
// Return null if we have reached the end.\r
if ( this._.end )\r
return null;\r
\r
- var node,\r
- range = this.range,\r
- guard,\r
- userGuard = this.guard,\r
- type = this.type,\r
- getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' );\r
-\r
// This is the first call. Initialize it.\r
if ( !this._.start )\r
{\r
this._.start = 1;\r
\r
- // Trim text nodes and optmize the range boundaries. DOM changes\r
- // may happen at this point.\r
- range.trim();\r
-\r
// A collapsed range must return null at first call.\r
if ( range.collapsed )\r
{\r
this.end();\r
return null;\r
}\r
+\r
+ // Move outside of text node edges.\r
+ range.optimize();\r
}\r
\r
+ var node,\r
+ startCt = range.startContainer,\r
+ endCt = range.endContainer,\r
+ startOffset = range.startOffset,\r
+ endOffset = range.endOffset,\r
+ guard,\r
+ userGuard = this.guard,\r
+ type = this.type,\r
+ getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' );\r
+\r
// Create the LTR guard function, if necessary.\r
if ( !rtl && !this._.guardLTR )\r
{\r
- // Gets the node that stops the walker when going LTR.\r
- var limitLTR = range.endContainer,\r
- blockerLTR = limitLTR.getChild( range.endOffset );\r
+ // The node that stops walker from moving up.\r
+ var limitLTR = endCt.type == CKEDITOR.NODE_ELEMENT ?\r
+ endCt :\r
+ endCt.getParent();\r
+\r
+ // The node that stops the walker from going to next.\r
+ var blockerLTR = endCt.type == CKEDITOR.NODE_ELEMENT ?\r
+ endCt.getChild( endOffset ) :\r
+ endCt.getNext();\r
\r
this._.guardLTR = function( node, movingOut )\r
{\r
// Create the RTL guard function, if necessary.\r
if ( rtl && !this._.guardRTL )\r
{\r
- // Gets the node that stops the walker when going LTR.\r
- var limitRTL = range.startContainer,\r
- blockerRTL = ( range.startOffset > 0 ) && limitRTL.getChild( range.startOffset - 1 );\r
+ // The node that stops walker from moving up.\r
+ var limitRTL = startCt.type == CKEDITOR.NODE_ELEMENT ?\r
+ startCt :\r
+ startCt.getParent();\r
+\r
+ // The node that stops the walker from going to next.\r
+ var blockerRTL = startCt.type == CKEDITOR.NODE_ELEMENT ?\r
+ startOffset ?\r
+ startCt.getChild( startOffset - 1 ) : null :\r
+ startCt.getPrevious();\r
\r
this._.guardRTL = function( node, movingOut )\r
{\r
else\r
{\r
// Get the first node to be returned.\r
-\r
if ( rtl )\r
{\r
- node = range.endContainer;\r
+ node = endCt;\r
\r
- if ( range.endOffset > 0 )\r
+ if ( node.type == CKEDITOR.NODE_ELEMENT )\r
{\r
- node = node.getChild( range.endOffset - 1 );\r
- if ( guard( node ) === false )\r
- node = null;\r
+ if ( endOffset > 0 )\r
+ node = node.getChild( endOffset - 1 );\r
+ else\r
+ node = ( guard ( node, true ) === false ) ?\r
+ null : node.getPreviousSourceNode( true, type, guard );\r
}\r
- else\r
- node = ( guard ( node, true ) === false ) ?\r
- null : node.getPreviousSourceNode( true, type, guard );\r
}\r
else\r
{\r
- node = range.startContainer;\r
- node = node.getChild( range.startOffset );\r
+ node = startCt;\r
\r
- if ( node )\r
+ if ( node.type == CKEDITOR.NODE_ELEMENT )\r
{\r
- if ( guard( node ) === false )\r
- node = null;\r
+ if ( ! ( node = node.getChild( startOffset ) ) )\r
+ node = ( guard ( startCt, true ) === false ) ?\r
+ null : startCt.getNextSourceNode( true, type, guard ) ;\r
}\r
- else\r
- node = ( guard ( range.startContainer, true ) === false ) ?\r
- null : range.startContainer.getNextSourceNode( true, type, guard ) ;\r
}\r
+\r
+ if ( node && guard( node ) === false )\r
+ node = null;\r
}\r
\r
while ( node && !this._.end )\r
'table-column' : 1,\r
'table-cell' : 1,\r
'table-caption' : 1\r
- },\r
- blockBoundaryNodeNameMatch = { hr : 1 };\r
+ };\r
\r
CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames )\r
{\r
- var nodeNameMatches = CKEDITOR.tools.extend( {},\r
- blockBoundaryNodeNameMatch, customNodeNames || {} );\r
+ var nodeNameMatches = customNodeNames ?\r
+ CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$block, customNodeNames || {} ) :\r
+ CKEDITOR.dtd.$block;\r
\r
- return blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] ||\r
- nodeNameMatches[ this.getName() ];\r
+ // Don't consider floated formatting as block boundary, fall back to dtd check in that case. (#6297)\r
+ return this.getComputedStyle( 'float' ) == 'none' && blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ]\r
+ || nodeNameMatches[ this.getName() ];\r
};\r
\r
CKEDITOR.dom.walker.blockBoundary = function( customNodeNames )\r
{\r
return ( node && node.getName\r
&& node.getName() == 'span'\r
- && node.hasAttribute( '_cke_bookmark' ) );\r
+ && node.data( 'cke-bookmark' ) );\r
}\r
\r
return function( node )\r
{\r
return function( node )\r
{\r
- var isWhitespace = node && ( node.type == CKEDITOR.NODE_TEXT )\r
- && !CKEDITOR.tools.trim( node.getText() );\r
+ var isWhitespace;\r
+ if ( node && node.type == CKEDITOR.NODE_TEXT )\r
+ {\r
+ // whitespace, as well as the text cursor filler node we used in Webkit. (#9384)\r
+ isWhitespace = !CKEDITOR.tools.trim( node.getText() ) ||\r
+ CKEDITOR.env.webkit && node.getText() == '\u200b';\r
+ }\r
+\r
return !! ( isReject ^ isWhitespace );\r
};\r
};\r
var whitespace = CKEDITOR.dom.walker.whitespaces();\r
return function( node )\r
{\r
- // Nodes that take no spaces in wysiwyg:\r
- // 1. White-spaces but not including NBSP;\r
- // 2. Empty inline elements, e.g. <b></b> we're checking here\r
- // 'offsetHeight' instead of 'offsetWidth' for properly excluding\r
- // all sorts of empty paragraph, e.g. <br />.\r
- var isInvisible = whitespace( node ) || node.is && !node.$.offsetHeight;\r
- return !! ( isReject ^ isInvisible );\r
+ var invisible;\r
+\r
+ if ( whitespace( node ) )\r
+ invisible = 1;\r
+ else\r
+ {\r
+ // Visibility should be checked on element.\r
+ if ( node.type == CKEDITOR.NODE_TEXT )\r
+ node = node.getParent();\r
+\r
+ // Nodes that take no spaces in wysiwyg:\r
+ // 1. White-spaces but not including NBSP;\r
+ // 2. Empty inline elements, e.g. <b></b> we're checking here\r
+ // 'offsetHeight' instead of 'offsetWidth' for properly excluding\r
+ // all sorts of empty paragraph, e.g. <br />.\r
+ invisible = !node.$.offsetHeight;\r
+ }\r
+\r
+ return !! ( isReject ^ invisible );\r
+ };\r
+ };\r
+\r
+ CKEDITOR.dom.walker.nodeType = function( type, isReject )\r
+ {\r
+ return function( node )\r
+ {\r
+ return !! ( isReject ^ ( node.type == type ) );\r
+ };\r
+ };\r
+\r
+ CKEDITOR.dom.walker.bogus = function( isReject )\r
+ {\r
+ function nonEmpty( node )\r
+ {\r
+ return !isWhitespaces( node ) && !isBookmark( node );\r
+ }\r
+\r
+ return function( node )\r
+ {\r
+ var isBogus = !CKEDITOR.env.ie ? node.is && node.is( 'br' ) :\r
+ node.getText && tailNbspRegex.test( node.getText() );\r
+\r
+ if ( isBogus )\r
+ {\r
+ var parent = node.getParent(), next = node.getNext( nonEmpty );\r
+ isBogus = parent.isBlockBoundary() &&\r
+ ( !next ||\r
+ next.type == CKEDITOR.NODE_ELEMENT &&\r
+ next.isBlockBoundary() );\r
+ }\r
+\r
+ return !! ( isReject ^ isBogus );\r
};\r
};\r
\r
var tailNbspRegex = /^[\t\r\n ]*(?: |\xa0)$/,\r
- isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( 1 ),\r
- isNotBookmark = CKEDITOR.dom.walker.bookmark( 0, 1 ),\r
- fillerEvaluator = function( element )\r
+ isWhitespaces = CKEDITOR.dom.walker.whitespaces(),\r
+ isBookmark = CKEDITOR.dom.walker.bookmark(),\r
+ toSkip = function( node )\r
{\r
- return isNotBookmark( element ) && isNotWhitespaces( element );\r
+ return isBookmark( node )\r
+ || isWhitespaces( node )\r
+ || node.type == CKEDITOR.NODE_ELEMENT\r
+ && node.getName() in CKEDITOR.dtd.$inline\r
+ && !( node.getName() in CKEDITOR.dtd.$empty );\r
};\r
\r
// Check if there's a filler node at the end of an element, and return it.\r
CKEDITOR.dom.element.prototype.getBogus = function()\r
{\r
- var tail = this.getLast( fillerEvaluator );\r
+ // Bogus are not always at the end, e.g. <p><a>text<br /></a></p> (#7070).\r
+ var tail = this;\r
+ do { tail = tail.getPreviousSourceNode(); }\r
+ while ( toSkip( tail ) )\r
+\r
if ( tail && ( !CKEDITOR.env.ie ? tail.is && tail.is( 'br' )\r
: tail.getText && tailNbspRegex.test( tail.getText() ) ) )\r
{\r