X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fcore%2Fdom%2Frange.js;h=67759c80ad1e1ea2232f2d632d4edcdeb8a3ef8c;hb=refs%2Ftags%2Fv3.6.2;hp=fedbfb97e45684a2612f0d548a5921c3e1fe1fe6;hpb=48b1db88210b4160dce439c6e3e32e14af8c106b;p=ckeditor.git diff --git a/_source/core/dom/range.js b/_source/core/dom/range.js index fedbfb9..67759c8 100644 --- a/_source/core/dom/range.js +++ b/_source/core/dom/range.js @@ -4,16 +4,87 @@ For licensing, see LICENSE.html or http://ckeditor.com/license */ /** - * @class + * Creates a CKEDITOR.dom.range instance that can be used inside a specific + * DOM Document. + * @class Represents a delimited piece of content in a DOM Document. + * It is contiguous in the sense that it can be characterized as selecting all + * of the content between a pair of boundary-points.
+ *
+ * This class shares much of the W3C + * Document Object Model Range + * ideas and features, adding several range manipulation tools to it, but it's + * not intended to be compatible with it. + * @param {CKEDITOR.dom.document} document The document into which the range + * features will be available. + * @example + * // Create a range for the entire contents of the editor document body. + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * // Delete the contents. + * range.deleteContents(); */ CKEDITOR.dom.range = function( document ) { + /** + * Node within which the range begins. + * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT} + * @example + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * alert( range.startContainer.getName() ); // "body" + */ this.startContainer = null; + + /** + * Offset within the starting node of the range. + * @type {Number} + * @example + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * alert( range.startOffset ); // "0" + */ this.startOffset = null; + + /** + * Node within which the range ends. + * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT} + * @example + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * alert( range.endContainer.getName() ); // "body" + */ this.endContainer = null; + + /** + * Offset within the ending node of the range. + * @type {Number} + * @example + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * alert( range.endOffset ); // == editor.document.getBody().getChildCount() + */ this.endOffset = null; + + /** + * Indicates that this is a collapsed range. A collapsed range has it's + * start and end boudaries at the very same point so nothing is contained + * in it. + * @example + * var range = new CKEDITOR.dom.range( editor.document ); + * range.selectNodeContents( editor.document.getBody() ); + * alert( range.collapsed ); // "false" + * range.collapse(); + * alert( range.collapsed ); // "true" + */ this.collapsed = true; + /** + * The document within which the range can be used. + * @type {CKEDITOR.dom.document} + * @example + * // Selects the body contents of the range document. + * range.selectNodeContents( range.document.getBody() ); + */ this.document = document; }; @@ -288,7 +359,7 @@ CKEDITOR.dom.range = function( document ) if ( node.type == CKEDITOR.NODE_TEXT ) { // If there's any visible text, then we're not at the start. - if ( CKEDITOR.tools.trim( node.getText() ).length ) + if ( node.hasAscendant( 'pre' ) || CKEDITOR.tools.trim( node.getText() ).length ) return false; } else if ( node.type == CKEDITOR.NODE_ELEMENT ) @@ -518,6 +589,10 @@ CKEDITOR.dom.range = function( document ) startContainer = child; startOffset = 0; } + + // Get the normalized offset. + if ( child && child.type == CKEDITOR.NODE_ELEMENT ) + startOffset = child.getIndex( 1 ); } // Normalize the start. @@ -546,6 +621,10 @@ CKEDITOR.dom.range = function( document ) endContainer = child; endOffset = 0; } + + // Get the normalized offset. + if ( child && child.type == CKEDITOR.NODE_ELEMENT ) + endOffset = child.getIndex( 1 ); } // Normalize the end. @@ -1241,6 +1320,22 @@ CKEDITOR.dom.range = function( document ) CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END ); + // Avoid enlarging the range further when end boundary spans right after the BR. (#7490) + if ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) + { + var theRange = this.clone(); + walker = new CKEDITOR.dom.walker( theRange ); + + var whitespaces = CKEDITOR.dom.walker.whitespaces(), + bookmark = CKEDITOR.dom.walker.bookmark(); + + walker.evaluator = function( node ) { return !whitespaces( node ) && !bookmark( node ); }; + var previous = walker.previous(); + if ( previous && previous.type == CKEDITOR.NODE_ELEMENT && previous.is( 'br' ) ) + return; + } + + // Enlarging the end boundary. walkerRange = this.clone(); walkerRange.collapse(); @@ -1791,7 +1886,7 @@ CKEDITOR.dom.range = function( document ) return 0; } // Range enclosed entirely in an editable element. - else if ( node.is( 'body' ) + else if ( node.is( 'html' ) || node.getAttribute( 'contentEditable' ) == 'true' && ( node.contains( anotherEnd ) || node.equals( anotherEnd ) ) ) { @@ -1825,47 +1920,53 @@ CKEDITOR.dom.range = function( document ) */ moveToElementEditablePosition : function( el, isMoveToEnd ) { - var isEditable; - - // Empty elements are rejected. - if ( CKEDITOR.dtd.$empty[ el.getName() ] ) - return false; - - while ( el && el.type == CKEDITOR.NODE_ELEMENT ) + function nextDFS( node, childOnly ) { - isEditable = el.isEditable(); + var next; - // If an editable element is found, move inside it. - if ( isEditable ) - this.moveToPosition( el, isMoveToEnd ? - CKEDITOR.POSITION_BEFORE_END : - CKEDITOR.POSITION_AFTER_START ); - // Stop immediately if we've found a non editable inline element (e.g ). - else if ( CKEDITOR.dtd.$inline[ el.getName() ] ) + if ( node.type == CKEDITOR.NODE_ELEMENT + && node.isEditable( false ) + && !CKEDITOR.dtd.$nonEditable[ node.getName() ] ) { - this.moveToPosition( el, isMoveToEnd ? - CKEDITOR.POSITION_AFTER_END : - CKEDITOR.POSITION_BEFORE_START ); - return true; + next = node[ isMoveToEnd ? 'getLast' : 'getFirst' ]( nonWhitespaceOrBookmarkEval ); } - // Non-editable non-inline elements are to be bypassed, getting the next one. - if ( CKEDITOR.dtd.$empty[ el.getName() ] ) - el = el[ isMoveToEnd ? 'getPrevious' : 'getNext' ]( nonWhitespaceOrBookmarkEval ); - else - el = el[ isMoveToEnd ? 'getLast' : 'getFirst' ]( nonWhitespaceOrBookmarkEval ); + if ( !childOnly && !next ) + next = node[ isMoveToEnd ? 'getPrevious' : 'getNext' ]( nonWhitespaceOrBookmarkEval ); + return next; + } + + var found = 0; + + while ( el ) + { // Stop immediately if we've found a text node. - if ( el && el.type == CKEDITOR.NODE_TEXT ) + if ( el.type == CKEDITOR.NODE_TEXT ) { this.moveToPosition( el, isMoveToEnd ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ); - return true; + found = 1; + break; + } + + // If an editable element is found, move inside it, but not stop the searching. + if ( el.type == CKEDITOR.NODE_ELEMENT ) + { + if ( el.isEditable() ) + { + this.moveToPosition( el, isMoveToEnd ? + CKEDITOR.POSITION_BEFORE_END : + CKEDITOR.POSITION_AFTER_START ); + found = 1; + } } + + el = nextDFS( el, found ); } - return isEditable; + return !!found; }, /**