+++ /dev/null
-/*\r
-Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
-For licensing, see LICENSE.html or http://ckeditor.com/license\r
-*/\r
-\r
-(function()\r
-{\r
- // This function is to be called under a "walker" instance scope.\r
- function iterate( rtl, breakOnFalse )\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
-\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
-\r
- this._.guardLTR = function( node, movingOut )\r
- {\r
- return ( ( !movingOut || !limitLTR.equals( node ) )\r
- && ( !blockerLTR || !node.equals( blockerLTR ) )\r
- && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || node.getName() != 'body' ) );\r
- };\r
- }\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
-\r
- this._.guardRTL = function( node, movingOut )\r
- {\r
- return ( ( !movingOut || !limitRTL.equals( node ) )\r
- && ( !blockerRTL || !node.equals( blockerRTL ) )\r
- && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || node.getName() != 'body' ) );\r
- };\r
- }\r
-\r
- // Define which guard function to use.\r
- var stopGuard = rtl ? this._.guardRTL : this._.guardLTR;\r
-\r
- // Make the user defined guard function participate in the process,\r
- // otherwise simply use the boundary guard.\r
- if ( userGuard )\r
- {\r
- guard = function( node, movingOut )\r
- {\r
- if ( stopGuard( node, movingOut ) === false )\r
- return false;\r
-\r
- return userGuard( node, movingOut );\r
- };\r
- }\r
- else\r
- guard = stopGuard;\r
-\r
- if ( this.current )\r
- node = this.current[ getSourceNodeFn ]( false, type, guard );\r
- else\r
- {\r
- // Get the first node to be returned.\r
-\r
- if ( rtl )\r
- {\r
- node = range.endContainer;\r
-\r
- if ( range.endOffset > 0 )\r
- {\r
- node = node.getChild( range.endOffset - 1 );\r
- if ( guard( node ) === false )\r
- node = null;\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
-\r
- if ( node )\r
- {\r
- if ( guard( node ) === false )\r
- node = null;\r
- }\r
- else\r
- node = ( guard ( range.startContainer, true ) === false ) ?\r
- null : range.startContainer.getNextSourceNode( true, type, guard ) ;\r
- }\r
- }\r
-\r
- while ( node && !this._.end )\r
- {\r
- this.current = node;\r
-\r
- if ( !this.evaluator || this.evaluator( node ) !== false )\r
- {\r
- if ( !breakOnFalse )\r
- return node;\r
- }\r
- else if ( breakOnFalse && this.evaluator )\r
- return false;\r
-\r
- node = node[ getSourceNodeFn ]( false, type, guard );\r
- }\r
-\r
- this.end();\r
- return this.current = null;\r
- }\r
-\r
- function iterateToLast( rtl )\r
- {\r
- var node, last = null;\r
-\r
- while ( ( node = iterate.call( this, rtl ) ) )\r
- last = node;\r
-\r
- return last;\r
- }\r
-\r
- CKEDITOR.dom.walker = CKEDITOR.tools.createClass(\r
- {\r
- /**\r
- * Utility class to "walk" the DOM inside a range boundaries. If\r
- * necessary, partially included nodes (text nodes) are broken to\r
- * reflect the boundaries limits, so DOM and range changes may happen.\r
- * Outside changes to the range may break the walker.\r
- *\r
- * The walker may return nodes that are not totaly included into the\r
- * range boundaires. Let's take the following range representation,\r
- * where the square brackets indicate the boundaries:\r
- *\r
- * [<p>Some <b>sample] text</b>\r
- *\r
- * While walking forward into the above range, the following nodes are\r
- * returned: <p>, "Some ", <b> and "sample". Going\r
- * backwards instead we have: "sample" and "Some ". So note that the\r
- * walker always returns nodes when "entering" them, but not when\r
- * "leaving" them. The guard function is instead called both when\r
- * entering and leaving nodes.\r
- *\r
- * @constructor\r
- * @param {CKEDITOR.dom.range} range The range within which walk.\r
- */\r
- $ : function( range )\r
- {\r
- this.range = range;\r
-\r
- /**\r
- * A function executed for every matched node, to check whether\r
- * it's to be considered into the walk or not. If not provided, all\r
- * matched nodes are considered good.\r
- * If the function returns "false" the node is ignored.\r
- * @name CKEDITOR.dom.walker.prototype.evaluator\r
- * @property\r
- * @type Function\r
- */\r
- // this.evaluator = null;\r
-\r
- /**\r
- * A function executed for every node the walk pass by to check\r
- * whether the walk is to be finished. It's called when both\r
- * entering and exiting nodes, as well as for the matched nodes.\r
- * If this function returns "false", the walking ends and no more\r
- * nodes are evaluated.\r
- * @name CKEDITOR.dom.walker.prototype.guard\r
- * @property\r
- * @type Function\r
- */\r
- // this.guard = null;\r
-\r
- /** @private */\r
- this._ = {};\r
- },\r
-\r
-// statics :\r
-// {\r
-// /* Creates a CKEDITOR.dom.walker instance to walk inside DOM boundaries set by nodes.\r
-// * @param {CKEDITOR.dom.node} startNode The node from wich the walk\r
-// * will start.\r
-// * @param {CKEDITOR.dom.node} [endNode] The last node to be considered\r
-// * in the walk. No more nodes are retrieved after touching or\r
-// * passing it. If not provided, the walker stops at the\r
-// * <body> closing boundary.\r
-// * @returns {CKEDITOR.dom.walker} A DOM walker for the nodes between the\r
-// * provided nodes.\r
-// */\r
-// createOnNodes : function( startNode, endNode, startInclusive, endInclusive )\r
-// {\r
-// var range = new CKEDITOR.dom.range();\r
-// if ( startNode )\r
-// range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ;\r
-// else\r
-// range.setStartAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_AFTER_START ) ;\r
-//\r
-// if ( endNode )\r
-// range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ;\r
-// else\r
-// range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ;\r
-//\r
-// return new CKEDITOR.dom.walker( range );\r
-// }\r
-// },\r
-//\r
- proto :\r
- {\r
- /**\r
- * Stop walking. No more nodes are retrieved if this function gets\r
- * called.\r
- */\r
- end : function()\r
- {\r
- this._.end = 1;\r
- },\r
-\r
- /**\r
- * Retrieves the next node (at right).\r
- * @returns {CKEDITOR.dom.node} The next node or null if no more\r
- * nodes are available.\r
- */\r
- next : function()\r
- {\r
- return iterate.call( this );\r
- },\r
-\r
- /**\r
- * Retrieves the previous node (at left).\r
- * @returns {CKEDITOR.dom.node} The previous node or null if no more\r
- * nodes are available.\r
- */\r
- previous : function()\r
- {\r
- return iterate.call( this, 1 );\r
- },\r
-\r
- /**\r
- * Check all nodes at right, executing the evaluation fuction.\r
- * @returns {Boolean} "false" if the evaluator function returned\r
- * "false" for any of the matched nodes. Otherwise "true".\r
- */\r
- checkForward : function()\r
- {\r
- return iterate.call( this, 0, 1 ) !== false;\r
- },\r
-\r
- /**\r
- * Check all nodes at left, executing the evaluation fuction.\r
- * @returns {Boolean} "false" if the evaluator function returned\r
- * "false" for any of the matched nodes. Otherwise "true".\r
- */\r
- checkBackward : function()\r
- {\r
- return iterate.call( this, 1, 1 ) !== false;\r
- },\r
-\r
- /**\r
- * Executes a full walk forward (to the right), until no more nodes\r
- * are available, returning the last valid node.\r
- * @returns {CKEDITOR.dom.node} The last node at the right or null\r
- * if no valid nodes are available.\r
- */\r
- lastForward : function()\r
- {\r
- return iterateToLast.call( this );\r
- },\r
-\r
- /**\r
- * Executes a full walk backwards (to the left), until no more nodes\r
- * are available, returning the last valid node.\r
- * @returns {CKEDITOR.dom.node} The last node at the left or null\r
- * if no valid nodes are available.\r
- */\r
- lastBackward : function()\r
- {\r
- return iterateToLast.call( this, 1 );\r
- },\r
-\r
- reset : function()\r
- {\r
- delete this.current;\r
- this._ = {};\r
- }\r
-\r
- }\r
- });\r
-\r
- /*\r
- * Anything whose display computed style is block, list-item, table,\r
- * table-row-group, table-header-group, table-footer-group, table-row,\r
- * table-column-group, table-column, table-cell, table-caption, or whose node\r
- * name is hr, br (when enterMode is br only) is a block boundary.\r
- */\r
- var blockBoundaryDisplayMatch =\r
- {\r
- block : 1,\r
- 'list-item' : 1,\r
- table : 1,\r
- 'table-row-group' : 1,\r
- 'table-header-group' : 1,\r
- 'table-footer-group' : 1,\r
- 'table-row' : 1,\r
- 'table-column-group' : 1,\r
- 'table-column' : 1,\r
- 'table-cell' : 1,\r
- 'table-caption' : 1\r
- };\r
-\r
- CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames )\r
- {\r
- var nodeNameMatches = customNodeNames ?\r
- CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$block, customNodeNames || {} ) :\r
- CKEDITOR.dtd.$block;\r
-\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 function( node , type )\r
- {\r
- return ! ( node.type == CKEDITOR.NODE_ELEMENT\r
- && node.isBlockBoundary( customNodeNames ) );\r
- };\r
- };\r
-\r
- CKEDITOR.dom.walker.listItemBoundary = function()\r
- {\r
- return this.blockBoundary( { br : 1 } );\r
- };\r
-\r
- /**\r
- * Whether the to-be-evaluated node is a bookmark node OR bookmark node\r
- * inner contents.\r
- * @param {Boolean} contentOnly Whether only test againt the text content of\r
- * bookmark node instead of the element itself(default).\r
- * @param {Boolean} isReject Whether should return 'false' for the bookmark\r
- * node instead of 'true'(default).\r
- */\r
- CKEDITOR.dom.walker.bookmark = function( contentOnly, isReject )\r
- {\r
- function isBookmarkNode( node )\r
- {\r
- return ( node && node.getName\r
- && node.getName() == 'span'\r
- && node.data( 'cke-bookmark' ) );\r
- }\r
-\r
- return function( node )\r
- {\r
- var isBookmark, parent;\r
- // Is bookmark inner text node?\r
- isBookmark = ( node && !node.getName && ( parent = node.getParent() )\r
- && isBookmarkNode( parent ) );\r
- // Is bookmark node?\r
- isBookmark = contentOnly ? isBookmark : isBookmark || isBookmarkNode( node );\r
- return !! ( isReject ^ isBookmark );\r
- };\r
- };\r
-\r
- /**\r
- * Whether the node is a text node containing only whitespaces characters.\r
- * @param isReject\r
- */\r
- CKEDITOR.dom.walker.whitespaces = function( isReject )\r
- {\r
- return function( node )\r
- {\r
- var isWhitespace = node && ( node.type == CKEDITOR.NODE_TEXT )\r
- && !CKEDITOR.tools.trim( node.getText() );\r
- return !! ( isReject ^ isWhitespace );\r
- };\r
- };\r
-\r
- /**\r
- * Whether the node is invisible in wysiwyg mode.\r
- * @param isReject\r
- */\r
- CKEDITOR.dom.walker.invisible = function( isReject )\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
- };\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
- var tailNbspRegex = /^[\t\r\n ]*(?: |\xa0)$/,\r
- isWhitespaces = CKEDITOR.dom.walker.whitespaces(),\r
- isBookmark = CKEDITOR.dom.walker.bookmark(),\r
- toSkip = function( node )\r
- {\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
- // 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
- return tail;\r
- }\r
- return false;\r
- };\r
-\r
-})();\r