/*\r
-Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
\r
// Creates the appropriate node evaluator for the dom walker used inside\r
// check(Start|End)OfBlock.\r
- function getCheckStartEndBlockEvalFunction( isStart )\r
+ function getCheckStartEndBlockEvalFunction()\r
{\r
var skipBogus = false,\r
+ whitespaces = CKEDITOR.dom.walker.whitespaces(),\r
bookmarkEvaluator = CKEDITOR.dom.walker.bookmark( true ),\r
- nbspRegExp = /^[\t\r\n ]*(?: |\xa0)$/;\r
+ isBogus = CKEDITOR.dom.walker.bogus();\r
\r
return function( node )\r
{\r
- // First ignore bookmark nodes.\r
- if ( bookmarkEvaluator( node ) )\r
+ // First skip empty nodes.\r
+ if ( bookmarkEvaluator( node ) || whitespaces( node ) )\r
return true;\r
\r
- if ( node.type == CKEDITOR.NODE_TEXT )\r
+ // Skip the bogus node at the end of block.\r
+ if ( isBogus( node ) &&\r
+ !skipBogus )\r
{\r
- // Skip the block filler NBSP.\r
- if ( CKEDITOR.env.ie &&\r
- nbspRegExp.test( node.getText() ) &&\r
- !skipBogus &&\r
- !( isStart && node.getNext() ) )\r
- {\r
- skipBogus = true;\r
- }\r
- // If there's any visible text, then we're not at the start.\r
- else if ( node.hasAscendant( 'pre' ) || CKEDITOR.tools.trim( node.getText() ).length )\r
- return false;\r
- }\r
- else if ( node.type == CKEDITOR.NODE_ELEMENT )\r
- {\r
- // If there are non-empty inline elements (e.g. <img />), then we're not\r
- // at the start.\r
- if ( !inlineChildReqElements[ node.getName() ] )\r
- {\r
- // Skip the padding block br.\r
- if ( !CKEDITOR.env.ie &&\r
- node.is( 'br' ) &&\r
- !skipBogus &&\r
- !( isStart && node.getNext() ) )\r
- {\r
- skipBogus = true;\r
- }\r
- else\r
- return false;\r
- }\r
+ skipBogus = true;\r
+ return true;\r
}\r
+\r
+ // If there's any visible text, then we're not at the start.\r
+ if ( node.type == CKEDITOR.NODE_TEXT &&\r
+ ( node.hasAscendant( 'pre' ) ||\r
+ CKEDITOR.tools.trim( node.getText() ).length ) )\r
+ return false;\r
+\r
+ // If there are non-empty inline elements (e.g. <img />), then we're not\r
+ // at the start.\r
+ if ( node.type == CKEDITOR.NODE_ELEMENT && !inlineChildReqElements[ node.getName() ] )\r
+ return false;\r
+\r
return true;\r
};\r
}\r
// text node and non-empty elements unless it's being bookmark text.\r
function elementBoundaryEval( checkStart )\r
{\r
+ var whitespaces = CKEDITOR.dom.walker.whitespaces(),\r
+ bookmark = CKEDITOR.dom.walker.bookmark( 1 );\r
+\r
return function( node )\r
{\r
+ // First skip empty nodes.\r
+ if ( bookmark( node ) || whitespaces( node ) )\r
+ return true;\r
+\r
// Tolerant bogus br when checking at the end of block.\r
// Reject any text node unless it's being bookmark\r
// OR it's spaces.\r
// Reject any element unless it's being invisible empty. (#3883)\r
return !checkStart && isBogus( node ) ||\r
- ( node.type == CKEDITOR.NODE_TEXT ?\r
- !CKEDITOR.tools.trim( node.getText() ) || !!node.getParent().data( 'cke-bookmark' )\r
- : node.getName() in CKEDITOR.dtd.$removeEmpty );\r
+ node.type == CKEDITOR.NODE_ELEMENT &&\r
+ node.getName() in CKEDITOR.dtd.$removeEmpty;\r
};\r
}\r
\r
var whitespaceEval = new CKEDITOR.dom.walker.whitespaces(),\r
- bookmarkEval = new CKEDITOR.dom.walker.bookmark();\r
+ bookmarkEval = new CKEDITOR.dom.walker.bookmark(),\r
+ nbspRegExp = /^[\t\r\n ]*(?: |\xa0)$/;\r
\r
function nonWhitespaceOrBookmarkEval( node )\r
{\r
var startContainer = this.startContainer,\r
startOffset = this.startOffset;\r
\r
- // If the starting node is a text node, and non-empty before the offset,\r
- // then we're surely not at the start of block.\r
- if ( startOffset && startContainer.type == CKEDITOR.NODE_TEXT )\r
+ // [IE] Special handling for range start in text with a leading NBSP,\r
+ // we it to be isolated, for bogus check.\r
+ if ( CKEDITOR.env.ie && startOffset && startContainer.type == CKEDITOR.NODE_TEXT )\r
{\r
var textBefore = CKEDITOR.tools.ltrim( startContainer.substring( 0, startOffset ) );\r
- if ( textBefore.length )\r
- return false;\r
+ if ( nbspRegExp.test( textBefore ) )\r
+ this.trim( 0, 1 );\r
}\r
\r
// We need to grab the block element holding the start boundary, so\r
walkerRange.setStartAt( path.block || path.blockLimit, CKEDITOR.POSITION_AFTER_START );\r
\r
var walker = new CKEDITOR.dom.walker( walkerRange );\r
- walker.evaluator = getCheckStartEndBlockEvalFunction( true );\r
+ walker.evaluator = getCheckStartEndBlockEvalFunction();\r
\r
return walker.checkBackward();\r
},\r
var endContainer = this.endContainer,\r
endOffset = this.endOffset;\r
\r
- // If the ending node is a text node, and non-empty after the offset,\r
- // then we're surely not at the end of block.\r
- if ( endContainer.type == CKEDITOR.NODE_TEXT )\r
+ // [IE] Special handling for range end in text with a following NBSP,\r
+ // we it to be isolated, for bogus check.\r
+ if ( CKEDITOR.env.ie && endContainer.type == CKEDITOR.NODE_TEXT )\r
{\r
var textAfter = CKEDITOR.tools.rtrim( endContainer.substring( endOffset ) );\r
- if ( textAfter.length )\r
- return false;\r
+ if ( nbspRegExp.test( textAfter ) )\r
+ this.trim( 1, 0 );\r
}\r
\r
// We need to grab the block element holding the start boundary, so\r
walkerRange.setEndAt( path.block || path.blockLimit, CKEDITOR.POSITION_BEFORE_END );\r
\r
var walker = new CKEDITOR.dom.walker( walkerRange );\r
- walker.evaluator = getCheckStartEndBlockEvalFunction( false );\r
+ walker.evaluator = getCheckStartEndBlockEvalFunction();\r
\r
return walker.checkForward();\r
},\r
\r
/**\r
- * Check if elements at which the range boundaries anchor are read-only,\r
- * with respect to "contenteditable" attribute.\r
+ * Traverse with {@link CKEDITOR.dom.walker} to retrieve the previous element before the range start.\r
+ * @param {Function} evaluator Function used as the walker's evaluator.\r
+ * @param {Function} [guard] Function used as the walker's guard.\r
+ * @param {CKEDITOR.dom.element} [boundary] A range ancestor element in which the traversal is limited,\r
+ * default to the root editable if not defined.\r
+ *\r
+ * @return {CKEDITOR.dom.element|null} The returned node from the traversal.\r
+ */\r
+ getPreviousNode : function( evaluator, guard, boundary ) {\r
+\r
+ var walkerRange = this.clone();\r
+ walkerRange.collapse( 1 );\r
+ walkerRange.setStartAt( boundary || this.document.getBody(), CKEDITOR.POSITION_AFTER_START );\r
+\r
+ var walker = new CKEDITOR.dom.walker( walkerRange );\r
+ walker.evaluator = evaluator;\r
+ walker.guard = guard;\r
+ return walker.previous();\r
+ },\r
+\r
+ /**\r
+ * Traverse with {@link CKEDITOR.dom.walker} to retrieve the next element before the range start.\r
+ * @param {Function} evaluator Function used as the walker's evaluator.\r
+ * @param {Function} [guard] Function used as the walker's guard.\r
+ * @param {CKEDITOR.dom.element} [boundary] A range ancestor element in which the traversal is limited,\r
+ * default to the root editable if not defined.\r
+ *\r
+ * @return {CKEDITOR.dom.element|null} The returned node from the traversal.\r
*/\r
+ getNextNode: function( evaluator, guard, boundary )\r
+ {\r
+ var walkerRange = this.clone();\r
+ walkerRange.collapse();\r
+ walkerRange.setEndAt( boundary || this.document.getBody(), CKEDITOR.POSITION_BEFORE_END );\r
+\r
+ var walker = new CKEDITOR.dom.walker( walkerRange );\r
+ walker.evaluator = evaluator;\r
+ walker.guard = guard;\r
+ return walker.next();\r
+ },\r
+\r
checkReadOnly : ( function()\r
{\r
function checkNodesEditable( node, anotherEnd )\r
*/\r
moveToElementEditablePosition : function( el, isMoveToEnd )\r
{\r
- var nbspRegExp = /^[\t\r\n ]*(?: |\xa0)$/;\r
-\r
function nextDFS( node, childOnly )\r
{\r
var next;\r