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 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>CKEDITOR.dom.rangelist</title>
+	<link rel="stylesheet" type="text/css" href="../../test.css" />
+	<script type="text/javascript" src="../../../../ckeditor_source.js"></script> <!-- %REMOVE_LINE%
+	<script type="text/javascript" src="../../../ckeditor.js"></script>
+	%REMOVE_LINE% -->
+	<script type="text/javascript" src="../../test.js"></script>
+	<script type="text/javascript">
+//<![CDATA[
+CKEDITOR.plugins.load( [ 'selection' ] );
+//]]>
+	</script>
+	<script type="text/javascript">
+//<![CDATA[
+var tc;
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local references.
+	var assert = CKEDITOR.test.assert;
+	var doc = new CKEDITOR.dom.document( document );
+
+	return tc = {
+
+		setUp : function()
+		{
+			doc.getById( 'playground').setHtml( '<p id="_P1">This is some <strong id="_B1">sample text</strong>. You are using</p>' );
+			var sel = doc.getSelection();
+			var range1 = new CKEDITOR.dom.range();
+			range1.setStart( doc.getById( '_P1' ).getFirst(), 8 );
+			range1.setEnd( doc.getById( '_B1' ).getFirst(), 2 );
+
+			var range2 = new CKEDITOR.dom.range();
+			range2.setStart( doc.getById( '_B1' ).getFirst(), 7 );
+			range2.setEnd( doc.getById( '_P1' ).getChildren().getItem( 2 ), 5 );
+
+			// <p id="_P1">This is [some <strong id="_B1">sa]mple [text</strong>. You] are using</p>
+			sel.selectRanges( new CKEDITOR.dom.rangeList( [ range1, range2 ] ) );
+		},
+		/////////////
+
+		test_count_getItem : function()
+		{
+			var rangeList = doc.getSelection().getRanges();
+			var count = rangeList.count();
+			assert.areSame( 2, count );
+
+			var range1 = rangeList.getItem( 0 );
+			var range2 = rangeList.getItem( 1 );
+
+			assert.areEqual( doc.getById( '_P1' ).getFirst().$, range1.startContainer.$ );
+			assert.areEqual( 8, range1.startOffset );
+			assert.areEqual( doc.getById( '_B1' ).getFirst().$, range1.endContainer.$ );
+			assert.areEqual( 2, range1.endOffset );
+
+			assert.areEqual( doc.getById( '_B1' ).getFirst().$, range2.startContainer.$ );
+			assert.areEqual( 7, range2.startOffset );
+			assert.areEqual( doc.getById( '_P1' ).getChildren().getItem( 2 ).$, range2.endContainer.$ );
+			assert.areEqual( 5, range2.endOffset );
+		},
+
+		test_add : function()
+		{
+			var rangeList = doc.getSelection().getRanges();
+
+			var range3 = new CKEDITOR.dom.range();
+			range3.setStart( doc.getById( '_P1' ).getChildren().getItem( 2 ), 8 );
+			range3.setEnd( doc.getById( '_P1' ).getChildren().getItem( 2 ), 14 );
+
+			rangeList.add( range3 );
+
+			doc.getSelection().selectRanges( rangeList );
+			var range3 = doc.getSelection().getRanges().getItem( 2 );
+			assert.areEqual( doc.getById( '_P1' ).getChildren().getItem( 2 ).$, range3.startContainer.$ );
+			assert.areEqual( 8, range3.startOffset );
+			assert.areEqual( doc.getById( '_P1' ).getChildren().getItem( 2 ).$, range3.endContainer.$ );
+			assert.areEqual( 14, range3.endOffset );
+		},
+
+		test_createBookmarks : function()
+		{
+
+			var bookmarks = doc.getSelection().getRanges().createBookmarks();
+			var range1 = new CKEDITOR.dom.range();
+			range1.moveToBookmark( bookmarks[ 0 ] );
+			var range2 = new CKEDITOR.dom.range();
+			range2.moveToBookmark( bookmarks[ 1 ] );
+
+			assert.areEqual( doc.getById( '_P1' ).$, range1.startContainer.$ );
+			assert.areEqual( 1, range1.startOffset );
+			assert.areEqual( doc.getById( '_B1' ).$, range1.endContainer.$ );
+			assert.areEqual( 1, range1.endOffset );
+
+			assert.areEqual( doc.getById( '_B1' ).$, range2.startContainer.$ );
+			assert.areEqual( 2, range2.startOffset );
+			assert.areEqual( doc.getById( '_P1' ).$, range2.endContainer.$ );
+			assert.areEqual( 4, range2.endOffset );
+		},
+
+		test_iterator : function()
+		{
+			var sel = doc.getSelection(),
+				rangeList = sel.getRanges(),
+				iterator = rangeList.createIterator(),
+				range;
+
+			// Create bookmarks before iteration for easy comparison.
+			rangeList.moveToBookmarks( rangeList.createBookmarks() );
+			var rangesBefore = [], rangesAfter = [];
+			for ( var i = 0 ; i < rangeList.count() ; i++ )
+			{
+				rangesBefore.push( rangeList.getItem( i ).clone() );
+			}
+
+			while( range = iterator.getNextRange() )
+			{
+				var nextRange = rangeList.getItem( iterator._.current + 1 );
+				// Destroy the next range.
+				nextRange && nextRange.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+				rangesAfter.push( range );
+			}
+
+			for( i = 0 ; i < rangesAfter.length ; i++  )
+			{
+				var rangeBefore = rangesBefore[ i ],
+					rangeAfter = rangesAfter[ i ];
+				assert.areSame( rangeBefore.startContainer.$, rangeAfter.startContainer.$ );
+				assert.areSame( rangeBefore.startOffset, rangeAfter.startOffset );
+				assert.areSame( rangeBefore.endContainer.$, rangeAfter.endContainer.$ );
+				assert.areSame( rangeBefore.endOffset, rangeAfter.endOffset );
+			}
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = tests.test_iterator;
+
+	//]]>
+	</script>
+</head>
+<body>
+<p id="playground"></p>
+</body>
+</html>
Index: _source/tests/testall.html
===================================================================
--- _source/tests/testall.html	(revision 3470)
+++ _source/tests/testall.html	Thu Jun 18 15:40:06 CST 2009
@@ -23,6 +23,7 @@
 	'core/dom/element',
 	'core/dom/node',
 	'core/dom/range',
+	'core/dom/rangelist',
 	'core/dom/text',
 	'core/dom/window',
 	'core/htmlparser/fragment',
Index: _source/plugins/pagebreak/plugin.js
===================================================================
--- _source/plugins/pagebreak/plugin.js	(revision 3308)
+++ _source/plugins/pagebreak/plugin.js	Thu Jun 18 01:29:30 CST 2009
@@ -82,9 +82,9 @@
 
 		var ranges = editor.getSelection().getRanges();
 
-		for ( var range, i = 0 ; i < ranges.length ; i++ )
+		for ( var range, i = 0 ; i < ranges.count() ; i++ )
 		{
-			range = ranges[ i ];
+			range = ranges.getItem( i );
 
 			if ( i > 0 )
 				breakObject = breakObject.clone( true );
Index: _source/plugins/link/dialogs/link.js
===================================================================
--- _source/plugins/link/dialogs/link.js	(revision 3440)
+++ _source/plugins/link/dialogs/link.js	Thu Jun 18 03:04:24 CST 2009
@@ -1001,10 +1001,10 @@
 				element = null,
 				me = this;
 			// Fill in all the relevant fields if there's already one link selected.
-			if ( ranges.length == 1 )
+			if ( ranges.count() )
 			{
 
-				var rangeRoot = ranges[0].getCommonAncestor( true );
+				var rangeRoot = ranges.getItem( 0 ).getCommonAncestor( true );
 				element = rangeRoot.getAscendant( 'a', true );
 				if ( element && element.getAttribute( 'href' ) )
 				{
@@ -1128,12 +1128,14 @@
 			{
 				// Create element if current selection is collapsed.
 				var selection = editor.getSelection(),
-					ranges = selection.getRanges();
-				if ( ranges.length == 1 && ranges[0].collapsed )
+					ranges = selection.getRanges(),
+					range;
+				if ( ranges.count() &&
+				    ( range = ranges.getItem( 0 ) ) && range.collapsed )
 				{
 					var text = new CKEDITOR.dom.text( attributes._cke_saved_href, editor.document );
-					ranges[0].insertNode( text );
-					ranges[0].selectNodeContents( text );
+					range.insertNode( text );
+					range.selectNodeContents( text );
 					selection.selectRanges( ranges );
 				}
 
Index: _source/plugins/link/dialogs/anchor.js
===================================================================
--- _source/plugins/link/dialogs/anchor.js	(revision 3477)
+++ _source/plugins/link/dialogs/anchor.js	Thu Jun 18 01:17:08 CST 2009
@@ -6,7 +6,7 @@
 CKEDITOR.dialog.add( 'anchor', function( editor )
 {
 	// Function called in onShow to load selected element.
-	var loadElements = function( editor, selection, ranges, element )
+	var loadElements = function( element )
 	{
 		this.editMode = true;
 		this.editObj = element;
@@ -63,18 +63,19 @@
 
 			var editor = this.getParentEditor(),
 				selection = editor.getSelection(),
-				ranges = selection.getRanges();
+				ranges = selection.getRanges(),
+				range;
 
-			if ( ranges.length == 1 )
+			if ( ranges.count() )
 			{
-				ranges[0].enlarge( CKEDITOR.ENLARGE_ELEMENT );
-				var rangeRoot = ranges[0].getCommonAncestor( true );
+				( range = ranges.getItem( 0 ) ).enlarge( CKEDITOR.ENLARGE_ELEMENT );
+				var rangeRoot = range.getCommonAncestor( true );
 				var element = rangeRoot.getAscendant( 'img', true );
 				if ( element && element.getAttribute( '_cke_real_element_type' ) && element.getAttribute( '_cke_real_element_type' ) == 'anchor' )
 				{
 					this.fakeObj = element;
 					element = editor.restoreRealElement( this.fakeObj );
-					loadElements.apply( this, [ editor, selection, ranges, element ] );
+					loadElements.call( this, element );
 					selection.selectElement( this.fakeObj );
 				}
 			}
Index: _source/tests/plugins/list/list.html
===================================================================
--- _source/tests/plugins/list/list.html	(revision 3477)
+++ _source/tests/plugins/list/list.html	Thu Jun 18 03:06:14 CST 2009
@@ -113,7 +113,7 @@
 
 							setRange( range, [ 1, 0, 0, 0, 0 ], true );
 							var sel = editor.getSelection();
-							sel.selectRanges( [ range ] );
+							sel.selectRanges( new CKEDITOR.dom.rangeList( range ) );
 
 							// Waiting for 'comand state' effected.
 							this.wait( function(){
@@ -255,7 +255,7 @@
 
 							setRange( range, [ 1, 0, 0, 0, 0 ], true );
 							var sel = editor.getSelection();
-							sel.selectRanges( [ range ] );
+							sel.selectRanges( new CKEDITOR.dom.rangeList( range ) );
 
 							// Waiting for 'comand state' effected.
 							this.wait( function(){
Index: _source/plugins/wysiwygarea/plugin.js
===================================================================
--- _source/plugins/wysiwygarea/plugin.js	(revision 3675)
+++ _source/plugins/wysiwygarea/plugin.js	Thu Jun 18 03:02:38 CST 2009
@@ -70,10 +70,9 @@
 
 			var range, clone, lastElement, bookmark;
 
-			for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
+			for ( var i = ranges.count() - 1 ; i >= 0 ; i-- )
 			{
-				range = ranges[ i ];
-
+				range = ranges.getItem( i );
 				// Remove the original contents.
 				range.deleteContents();
 
@@ -103,7 +102,7 @@
 			if ( next && next.type == CKEDITOR.NODE_ELEMENT )
 				range.moveToElementEditStart( next );
 
-			selection.selectRanges( [ range ] );
+			selection.selectRanges( new CKEDITOR.dom.rangeList( range ) );
 
 			if ( selIsLocked )
 				this.getSelection().lock();
@@ -128,7 +127,7 @@
 			path = evt.data.path,
 			blockLimit = path.blockLimit,
 			selection = evt.data.selection,
-			range = selection.getRanges()[0],
+			range = selection.getRanges().getItem( 0 ),
 			body = editor.document.getBody(),
 			enterMode = editor.config.enterMode;
 
Index: _source/plugins/removeformat/plugin.js
===================================================================
--- _source/plugins/removeformat/plugin.js	(revision 3174)
+++ _source/plugins/removeformat/plugin.js	Thu Jun 18 01:36:02 CST 2009
@@ -34,7 +34,7 @@
 
 				var ranges = editor.getSelection().getRanges();
 
-				for ( var i = 0, range ; range = ranges[ i ] ; i++ )
+				for ( var range, i = 0 ; i < ranges.count() ; i++ )
 				{
 					if ( range.collapsed )
 						continue;
Index: _source/plugins/find/dialogs/find.js
===================================================================
--- _source/plugins/find/dialogs/find.js	(revision 3678)
+++ _source/plugins/find/dialogs/find.js	Thu Jun 18 01:20:42 CST 2009
@@ -242,8 +242,10 @@
 					this.removeHighlight();
 
 				// Apply the highlight.
-				var range = this.toDomRange();
+				var range = this.toDomRange(),
+					bookmark = range.createBookmark();
 				highlightStyle.applyToRange( range );
+				range.moveToBookmark( bookmark );
 				this._.highlightRange = range;
 
 				// Scroll the editor to the highlighted area.
@@ -264,7 +266,9 @@
 				if ( !this._.highlightRange )
 					return;
 
+				var bookmark = this._.highlightRange.createBookmark();
 				highlightStyle.removeFromRange( this._.highlightRange );
+				this._.highlightRange.moveToBookmark( bookmark );
 				this.updateFromDomRange( this._.highlightRange );
 				this._.highlightRange = null;
 			},
@@ -547,10 +551,12 @@
 		{
 			var searchRange,
 				sel = editor.getSelection(),
+				ranges,
 				body = editor.document.getBody();
-			if ( sel && !isDefault )
+			if ( !isDefault && sel
+				 && ( ranges = sel.getRanges() ) && ranges.count() )
 			{
-				searchRange = sel.getRanges()[ 0 ].clone();
+				searchRange = ranges.getItem( 0 ).clone();
 				searchRange.collapse( true );
 			}
 			else
Index: _source/plugins/table/dialogs/table.js
===================================================================
--- _source/plugins/table/dialogs/table.js	(revision 3649)
+++ _source/plugins/table/dialogs/table.js	Thu Jun 18 01:44:24 CST 2009
@@ -41,9 +41,9 @@
 						if ( selectedTable.getName() != 'table' )
 							selectedTable = null;
 					}
-					else if ( ranges.length > 0 )
+					else if ( ranges.count() )
 					{
-						var rangeRoot = ranges[0].getCommonAncestor( true );
+						var rangeRoot = ranges.getItem( 0 ).getCommonAncestor( true );
 						selectedTable = rangeRoot.getAscendant( 'table', true );
 					}
 
Index: ckeditor.pack
===================================================================
--- ckeditor.pack	(revision 3649)
+++ ckeditor.pack	Thu Jun 18 02:03:36 CST 2009
@@ -123,6 +123,7 @@
 					'_source/core/dom/domwalker.js',
 					'_source/core/dom/walker.js',
 					'_source/core/dom/range.js',
+					'_source/core/dom/rangelist.js',
 					'_source/core/_bootstrap.js',
 //					'_source/lang/en.js',
 					'_source/plugins/about/plugin.js',
Index: _source/plugins/list/plugin.js
===================================================================
--- _source/plugins/list/plugin.js	(revision 3607)
+++ _source/plugins/list/plugin.js	Thu Jun 18 01:34:30 CST 2009
@@ -361,7 +361,7 @@
 				ranges = selection && selection.getRanges();
 
 			// There should be at least one selected range.
-			if ( !ranges || ranges.length < 1 )
+			if ( !ranges.count() )
 				return;
 
 			// Midas lists rule #1 says we can create a list even in an empty document.
@@ -376,15 +376,15 @@
 					var paragraph = doc.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' :
 							( editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'br' ) );
 					paragraph.appendTo( body );
-					ranges = [ new CKEDITOR.dom.range( doc ) ];
+					ranges = new CKEDITOR.dom.rangeList( new CKEDITOR.dom.range( doc ) );
 					// IE exception on inserting anything when anchor inside <br>.
 					if ( paragraph.is( 'br' ) )
 					{
-						ranges[ 0 ].setStartBefore( paragraph );
-						ranges[ 0 ].setEndAfter( paragraph );
+						ranges.getItem( 0 ).setStartBefore( paragraph );
+						ranges.getItem( 0 ).setEndAfter( paragraph );
 					}
 					else
-						ranges[ 0 ].selectNodeContents( paragraph );
+						ranges.getItem( 0 ).selectNodeContents( paragraph );
 					selection.selectRanges( ranges );
 				}
 			}
@@ -396,10 +396,10 @@
 			var listGroups = [],
 				database = {};
 
-			while ( ranges.length > 0 )
+			for ( var range, i = 0 ; i < ranges.count() ; i++ )
 			{
-				var range = ranges.shift(),
-					boundaryNodes = range.getBoundaryNodes(),
+				range = ranges.getItem( i );
+				var	boundaryNodes = range.getBoundaryNodes(),
 					startNode = boundaryNodes.startNode,
 					endNode = boundaryNodes.endNode;
 				if ( startNode.type == CKEDITOR.NODE_ELEMENT && startNode.getName() == 'td' )
Index: _source/plugins/link/plugin.js
===================================================================
--- _source/plugins/link/plugin.js	(revision 3308)
+++ _source/plugins/link/plugin.js	Thu Jun 18 03:01:04 CST 2009
@@ -166,13 +166,14 @@
 			rangeRoot,
 			element;
 
-		for ( var i = 0 ; i < ranges.length ; i++ )
+		for ( var range, i = 0 ; i < ranges.count() ; i++ )
 		{
-			rangeRoot = ranges[i].getCommonAncestor( true );
+			range = ranges.getItem( i );
+			rangeRoot = range.getCommonAncestor( true );
 			element = rangeRoot.getAscendant( 'a', true );
 			if ( !element )
 				continue;
-			ranges[i].selectNodeContents( element );
+			range.selectNodeContents( element );
 		}
 
 		selection.selectRanges( ranges );
Index: _source/plugins/styles/plugin.js
===================================================================
--- _source/plugins/styles/plugin.js	(revision 3517)
+++ _source/plugins/styles/plugin.js	Thu Jun 18 02:49:10 CST 2009
@@ -299,9 +299,6 @@
 		// Get the DTD definition for the element. Defaults to "span".
 		var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span );
 
-		// Bookmark the range so we can re-select it after processing.
-		var bookmark = range.createBookmark();
-
 		// Expand the range.
 		range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
 		range.trim();
@@ -497,9 +494,6 @@
 			}
 		}
 
-//		this._FixBookmarkStart( startNode );
-
-		range.moveToBookmark( bookmark );
 	}
 
 	function removeInlineStyle( range )
@@ -623,9 +617,6 @@
 
 	function applyBlockStyle( range )
 	{
-		// Bookmark the range so we can re-select it after processing.
-		var bookmark = range.createBookmark();
-
 		var iterator = range.createIterator();
 		iterator.enforceRealBlocks = true;
 
@@ -668,7 +659,6 @@
 //				this._CheckAndSplitPre( newBlock ) ;	// Split <br><br> in successive <pre>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' ],
