Index: _source/core/dom/rangelist.js =================================================================== --- _source/core/dom/rangelist.js Sun Jun 28 23:38:26 CST 2009 +++ _source/core/dom/rangelist.js Sun Jun 28 23:38:26 CST 2009 @@ -0,0 +1,198 @@ +/* +Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +( function() +{ + /** + * Representation of multiple ranges within a selection. + * @param {CKEDITOR.dom.range | Array<{CKEDITOR.dom.range> | undefined } ranges + * The ranges consist of this list, note that if an array of ranges is specified, + * the range sequence should compliant with the selection order, this class is + * will not help to sort them. + */ + CKEDITOR.dom.rangeList = function( ranges ) + { + if ( typeof ranges == 'undefined' ) + ranges = []; + if( CKEDITOR.tools.isArray( ranges ) ) + ranges = ranges; + else if ( ranges instanceof CKEDITOR.dom.range ) + ranges = [ ranges ]; + else + throw 'Unknown ranges type.'; + + this._ = + { + ranges : ranges, + count : ranges.length + } + + }; + + // Update the specified range which has been mangled by previous insertion of + // range bookmark nodes.(#3256) + function updateDirtyRange( bookmark, dirtyRange , checkEnd ) + { + var serializable = bookmark.serializable, + container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ], + offset = checkEnd ? 'endOffset' : 'startOffset', + bookmarkStart = serializable ? + dirtyRange.document.getById( bookmark.startNode ) + : bookmark.startNode, + bookmarkEnd = serializable ? + dirtyRange.document.getById( bookmark.endNode ) + : bookmark.endNode; + + if ( container.equals( bookmarkStart.getPrevious() ) ) + { + dirtyRange.startOffset = dirtyRange.startOffset + - container.getLength() - bookmarkEnd.getPrevious().getLength(); + container = bookmarkEnd.getNext(); + } + else if ( container.equals( bookmarkEnd.getPrevious() ) ) + { + dirtyRange.startOffset = dirtyRange.startOffset - container.getLength(); + container = bookmarkEnd.getNext(); + } + + container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++; + container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++; + + // Update and return this range. + dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container; + return dirtyRange; + } + + CKEDITOR.dom.rangeList.prototype = { + /** + * Appending the specified range object to end of current list. + * @param { CKEDITOR.dom.range} range + * @example + * var rangeList = new CKEDITOR.dom.rangeList(); + * var range = new CKEDITOR.dom.range(); + * ... + * rangeList.add( range ); + * alert( rangeList.getItem( 0 ) ); + */ + add : function( range ) + { + this._.ranges.push( range ); + this._.count++; + }, + + getItem : function( index ) + { + return this._.ranges[ index ] || null; + }, + + count : function() + { + return this._.count; + }, + + /** + * Create an instance of rangeList iterator, it should be only used while + * the processing of each range is possibly polluting other this._.ranges, e.g. destroy + * the following range's start container OR change others' offset value; + * Otherwise, simply iterating with {@link CKEDITOR.dom.rangeList::getItem} + * in a for loop. + */ + createIterator : function() + { + var ranges = this._.ranges; + + function iterator() + { + this._ = { +// // The current range under iteration. +// current : undefined, +// // The created bookmarks for the dirty this._.ranges. +// bookmarks : [] + }; + } + + iterator.prototype = { + + getNextRange : function() + { + var current = this._.current; + if ( !this._.count || current === this._.count - 1 ) + return null; + else + current = typeof current == 'undefined' ? 0 : current + 1; + + var range = this._.ranges [ current ]; + + // Multiple this._.ranges might be mangled by previous. + if ( this._.count > 1 ) + { + if ( !this._.bookmarks ) + this._.bookmarks = []; + + // Bookmarking all other this._.ranges on the first iteration. + if ( current == 0 ) + { + for ( var i = 1; i < this._.count; i++ ) + { + this._.bookmarks.push( this._.ranges[ i ].createBookmark() ); + } + } + // Update the _.current returning range. + else + { + range.moveToBookmark( this._.bookmarks.shift() ); + } + + } + this._.current = current; + return range; + } + }; + + return new iterator(); + }, + + + createBookmarks : function( serializable ) + { + var retval = [], bookmark; + for ( var i = 0; i < this._.count ; i++ ) + { + retval.push( bookmark = this._.ranges[ i ]. + createBookmark( serializable, true) ); + // Updating the container & offset values for rest of this._.ranges + // which have been mangled. + for ( var j = i + 1; j < this._.count; j++ ) + { + this._.ranges[ j ] = updateDirtyRange( bookmark, this._.ranges[ j ] ); + this._.ranges[ j ] = updateDirtyRange( bookmark, this._.ranges[ j ], true ); + } + } + return retval; + }, + + /** + * Apply each of the supplied bookmarks to the corresponding range at the index. + * @param bookmarks + */ + moveToBookmarks : function( bookmarks ) + { + for ( var i = 0 ; i < this._.ranges.length ; i++ ) + this._.ranges[ i ].moveToBookmark( bookmarks[ i ] ); + }, + + createBookmarks2 : function( normalized ) + { + var bookmarks = []; + + for ( var i = 0 ; i < this._.ranges.length ; i++ ) + bookmarks.push( this._.ranges[ i ].createBookmark2( normalized ) ); + + return bookmarks; + } + + }; + + +} )(); Index: _source/tests/core/dom/rangelist.html =================================================================== --- _source/tests/core/dom/rangelist.html Thu Jun 18 15:36:26 CST 2009 +++ _source/tests/core/dom/rangelist.html Thu Jun 18 15:36:26 CST 2009 @@ -0,0 +1,148 @@ + + +
+s. } - range.moveToBookmark( bookmark ); } // Removes a style from an element itself, don't care about its subtree. @@ -1000,17 +990,17 @@ function applyStyle( document, remove ) { - // Get all ranges from the selection. - var selection = document.getSelection(); - var ranges = selection.getRanges(); - var func = remove ? this.removeFromRange : this.applyToRange; + var selection = document.getSelection(), + // Bookmark the range so we can re-select it after processing. + bookmarks = selection.createBookmarks(), + func = remove ? this.removeFromRange : this.applyToRange, + range; - // Apply the style to the ranges. - for ( var i = 0 ; i < ranges.length ; i++ ) - func.call( this, ranges[ i ] ); + var iterator = selection.getRanges().createIterator(); + while ( range = iterator.getNextRange() ) + func.call( this, range ); - // Select the ranges again. - selection.selectRanges( ranges ); + selection.selectBookmarks( bookmarks ); } })(); Index: _source/plugins/enterkey/plugin.js =================================================================== --- _source/plugins/enterkey/plugin.js (revision 3630) +++ _source/plugins/enterkey/plugin.js Thu Jun 18 01:28:48 CST 2009 @@ -312,12 +312,12 @@ var ranges = editor.getSelection().getRanges(); // Delete the contents of all ranges except the first one. - for ( var i = ranges.length - 1 ; i > 0 ; i-- ) + for ( var i = ranges.count() - 1 ; i > 0 ; i-- ) { - ranges[ i ].deleteContents(); + ranges.getItem( i ).deleteContents(); } // Return the first range. - return ranges[ 0 ]; + return ranges.getItem( 0 ); } })(); Index: _source/plugins/justify/plugin.js =================================================================== --- _source/plugins/justify/plugin.js (revision 3662) +++ _source/plugins/justify/plugin.js Thu Jun 18 01:43:26 CST 2009 @@ -77,9 +77,9 @@ var cssClassName = this.cssClassName, iterator, block; - for ( var i = ranges.length - 1 ; i >= 0 ; i-- ) + for ( var i = ranges.count() - 1 ; i >= 0 ; i-- ) { - iterator = ranges[ i ].createIterator(); + iterator = ranges.getItem( i ).createIterator(); while ( ( block = iterator.getNextParagraph() ) ) { block.removeAttribute( 'align' ); Index: _source/plugins/tabletools/plugin.js =================================================================== --- _source/plugins/tabletools/plugin.js (revision 3623) +++ _source/plugins/tabletools/plugin.js Thu Jun 18 01:26:12 CST 2009 @@ -41,9 +41,9 @@ } } - for ( var i = 0 ; i < ranges.length ; i++ ) + for ( var i = 0 ; i < ranges.count() ; i++ ) { - var range = ranges[ i ]; + var range = ranges.getItem( i ); if ( range.collapsed ) { @@ -438,9 +438,9 @@ // Maintain the selection point at where the table was deleted. selection.selectElement( table ); - var range = selection.getRanges()[0]; - range.collapse(); - selection.selectRanges( [ range ] ); + var ranges = selection.getRanges(); + ranges.getItem( 0 ).collapse(); + selection.selectRanges( ranges ); // If the table's parent has only one child, remove it as well. if ( table.getParent().getChildCount() == 1 ) Index: _source/plugins/selection/plugin.js =================================================================== --- _source/plugins/selection/plugin.js (revision 3662) +++ _source/plugins/selection/plugin.js Thu Jun 18 15:17:20 CST 2009 @@ -537,11 +537,11 @@ boundaryInfo = getBoundaryInformation( nativeRange ); range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); - return ( cache.ranges = [ range ] ); + return ( cache.ranges = new CKEDITOR.dom.rangeList( range ) ); } else if ( type == CKEDITOR.SELECTION_ELEMENT ) { - var retval = this._.cache.ranges = []; + var retval = this._.cache.ranges = new CKEDITOR.dom.rangeList(); for ( var i = 0 ; i < nativeRange.length ; i++ ) { @@ -556,13 +556,13 @@ range.setStart( new CKEDITOR.dom.node( parentElement ), j ); range.setEnd( new CKEDITOR.dom.node( parentElement ), j + 1 ); - retval.push( range ); + retval.add( range ); } return retval; } - return ( cache.ranges = [] ); + return ( cache.ranges = new CKEDITOR.dom.rangeList() ); }; })() : @@ -576,21 +576,19 @@ // tranform the native ranges in CKEDITOR.dom.range // instances. - var ranges = []; + var ranges = new CKEDITOR.dom.rangeList(); var sel = this.getNative(); - if ( !sel ) - return []; - + if( sel ) - for ( var i = 0 ; i < sel.rangeCount ; i++ ) - { - var nativeRange = sel.getRangeAt( i ); - var range = new CKEDITOR.dom.range( this.document ); + for ( var i = 0 ; i < sel.rangeCount ; i++ ) + { + var nativeRange = sel.getRangeAt( i ); + var range = new CKEDITOR.dom.range( this.document ); - range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset ); - range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset ); + range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset ); + range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset ); - ranges.push( range ); + ranges.add( range ); - } + } return ( cache.ranges = ranges ); }, @@ -619,7 +617,7 @@ case CKEDITOR.SELECTION_TEXT : - var range = this.getRanges()[0]; + var range = this.getRanges().getItem( 0 ); if ( range ) { @@ -783,7 +781,7 @@ this._.cache.selectedElement = element; this._.cache.startElement = element; - this._.cache.ranges = [ range ]; + this._.cache.ranges = new CKEDITOR.dom.rangeList( range ); this._.cache.type = CKEDITOR.SELECTION_ELEMENT; return; @@ -825,13 +823,13 @@ } }, - selectRanges : function( ranges ) + selectRanges : function( rangeList ) { if ( this.isLocked ) { this._.cache.selectedElement = null; - this._.cache.startElement = ranges[ 0 ].getTouchedStartNode(); - this._.cache.ranges = ranges; + this._.cache.startElement = rangeList.getItem( 0 ).getTouchedStartNode(); + this._.cache.ranges = rangeList; this._.cache.type = CKEDITOR.SELECTION_TEXT; return; @@ -841,8 +839,8 @@ { // IE doesn't accept multiple ranges selection, so we just // select the first one. - if ( ranges[ 0 ] ) - ranges[ 0 ].select(); + if ( rangeList.count() ) + rangeList.getItem( 0 ).select(); this.reset(); } @@ -851,9 +849,9 @@ var sel = this.getNative(); sel.removeAllRanges(); - for ( var i = 0 ; i < ranges.length ; i++ ) + for ( var i = 0 ; i < rangeList.count() ; i++ ) { - var range = ranges[ i ]; + var range = rangeList.getItem( i ); var nativeRange = this.document.$.createRange(); nativeRange.setStart( range.startContainer.$, range.startOffset ); nativeRange.setEnd( range.endContainer.$, range.endOffset ); @@ -868,63 +866,29 @@ createBookmarks : function( serializable ) { - var retval = [], - ranges = this.getRanges(), - length = ranges.length, - bookmark; - for ( var i = 0; i < length ; i++ ) - { - retval.push( bookmark = ranges[ i ].createBookmark( serializable, true ) ); - - var serializable = bookmark.serializable, - bookmarkStart = serializable ? - this.document.getById( bookmark.startNode ) - : bookmark.startNode, - bookmarkEnd = serializable ? - this.document.getById( bookmark.endNode ) - : bookmark.endNode; - - // Updating the offset values for rest of ranges which have been mangled(#3256). - for ( var j = i + 1 ; j < length ; j++ ) - { - var dirtyRange = ranges[ j ], - rangeStart = dirtyRange.startContainer, - rangeEnd = dirtyRange.endContainer; - - rangeStart.equals( bookmarkStart.getParent() ) && dirtyRange.startOffset++; - rangeStart.equals( bookmarkEnd.getParent() ) && dirtyRange.startOffset++; - rangeEnd.equals( bookmarkStart.getParent() ) && dirtyRange.endOffset++; - rangeEnd.equals( bookmarkEnd.getParent() ) && dirtyRange.endOffset++; - } - } - - return retval; + return this.getRanges().createBookmarks( serializable ); }, createBookmarks2 : function( normalized ) { - var bookmarks = [], - ranges = this.getRanges(); - - for ( var i = 0 ; i < ranges.length ; i++ ) - bookmarks.push( ranges[i].createBookmark2( normalized ) ); - - return bookmarks; + return this.getRanges().createBookmarks2( normalized ); }, selectBookmarks : function( bookmarks ) { - var ranges = []; + var ranges = new CKEDITOR.dom.rangeList(); for ( var i = 0 ; i < bookmarks.length ; i++ ) { var range = new CKEDITOR.dom.range( this.document ); range.moveToBookmark( bookmarks[i] ); - ranges.push( range ); + ranges.add( range ); } this.selectRanges( ranges ); return this; } + }; + })(); CKEDITOR.dom.range.prototype.select = Index: _source/plugins/blockquote/plugin.js =================================================================== --- _source/plugins/blockquote/plugin.js (revision 3309) +++ _source/plugins/blockquote/plugin.js Thu Jun 18 01:27:30 CST 2009 @@ -47,7 +47,8 @@ { var state = editor.getCommand( 'blockquote' ).state, selection = editor.getSelection(), - range = selection && selection.getRanges()[0]; + ranges = selection && selection.getRanges(), + range = ranges.count() && ranges.getItem( 0 ); if ( !range ) return; Index: _source/plugins/indent/plugin.js =================================================================== --- _source/plugins/indent/plugin.js (revision 3480) +++ _source/plugins/indent/plugin.js Thu Jun 18 01:36:26 CST 2009 @@ -219,7 +219,7 @@ exec : function( editor ) { var selection = editor.getSelection(), - range = selection && selection.getRanges()[0]; + range = selection && selection.getRanges().getItem( 0 ); if ( !selection || !range ) return; Index: _source/core/loader.js =================================================================== --- _source/core/loader.js (revision 3496) +++ _source/core/loader.js Thu Jun 18 02:03:10 CST 2009 @@ -23,7 +24,7 @@ // Table of script names and their dependencies. var scripts = { - 'core/_bootstrap' : [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/elementpath', 'core/dom/text', 'core/dom/range' ], + 'core/_bootstrap' : [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/elementpath', 'core/dom/text', 'core/dom/range', 'core/dom/rangelist' ], 'core/ajax' : [ 'core/xml' ], 'core/ckeditor' : [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/htmlparser/filter', 'core/htmlparser/basicwriter', 'core/tools' ], 'core/ckeditor_base' : [], @@ -41,6 +42,7 @@ 'core/dom/domobject' : [ 'core/dom/event' ], 'core/dom/domwalker' : [ 'core/dom/node', 'core/dom/element', 'core/dom/document' ], 'core/dom/range' : [ 'core/dom/document', 'core/dom/documentfragment', 'core/dom/element', 'core/dom/domwalker', 'core/dom/walker' ], + 'core/dom/rangelist' : [ 'core/dom/range' ], 'core/dom/text' : [ 'core/dom/node', 'core/dom/domobject' ], 'core/dom/walker' : [ 'core/dom/node' ], 'core/dom/window' : [ 'core/dom/domobject' ],