--- /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
+ /**\r
+ * Represents a list os CKEDITOR.dom.range objects, which can be easily\r
+ * iterated sequentially.\r
+ * @constructor\r
+ * @param {CKEDITOR.dom.range|Array} [ranges] The ranges contained on this list.\r
+ * Note that, if an array of ranges is specified, the range sequence\r
+ * should match its DOM order. This class will not help to sort them.\r
+ */\r
+ CKEDITOR.dom.rangeList = function( ranges )\r
+ {\r
+ if ( ranges instanceof CKEDITOR.dom.rangeList )\r
+ return ranges;\r
+\r
+ if ( !ranges )\r
+ ranges = [];\r
+ else if ( ranges instanceof CKEDITOR.dom.range )\r
+ ranges = [ ranges ];\r
+\r
+ return CKEDITOR.tools.extend( ranges, mixins );\r
+ };\r
+\r
+ var mixins =\r
+ /** @lends CKEDITOR.dom.rangeList.prototype */\r
+ {\r
+ /**\r
+ * Creates an instance of the rangeList iterator, it should be used\r
+ * only when the ranges processing could be DOM intrusive, which\r
+ * means it may pollute and break other ranges in this list.\r
+ * Otherwise, it's enough to just iterate over this array in a for loop.\r
+ * @returns {CKEDITOR.dom.rangeListIterator}\r
+ */\r
+ createIterator : function()\r
+ {\r
+ var rangeList = this,\r
+ bookmark = CKEDITOR.dom.walker.bookmark(),\r
+ guard = function( node ) { return ! ( node.is && node.is( 'tr' ) ); },\r
+ bookmarks = [],\r
+ current;\r
+\r
+ /**\r
+ * @lends CKEDITOR.dom.rangeListIterator.prototype\r
+ */\r
+ return {\r
+\r
+ /**\r
+ * Retrieves the next range in the list.\r
+ * @param {Boolean} mergeConsequent Whether join two adjacent ranges into single, e.g. consequent table cells.\r
+ */\r
+ getNextRange : function( mergeConsequent )\r
+ {\r
+ current = current == undefined ? 0 : current + 1;\r
+\r
+ var range = rangeList[ current ];\r
+\r
+ // Multiple ranges might be mangled by each other.\r
+ if ( range && rangeList.length > 1 )\r
+ {\r
+ // Bookmarking all other ranges on the first iteration,\r
+ // the range correctness after it doesn't matter since we'll\r
+ // restore them before the next iteration.\r
+ if ( !current )\r
+ {\r
+ // Make sure bookmark correctness by reverse processing.\r
+ for ( var i = rangeList.length - 1; i >= 0; i-- )\r
+ bookmarks.unshift( rangeList[ i ].createBookmark( true ) );\r
+ }\r
+\r
+ if ( mergeConsequent )\r
+ {\r
+ // Figure out how many ranges should be merged.\r
+ var mergeCount = 0;\r
+ while ( rangeList[ current + mergeCount + 1 ] )\r
+ {\r
+ var doc = range.document,\r
+ found = 0,\r
+ left = doc.getById( bookmarks[ mergeCount ].endNode ),\r
+ right = doc.getById( bookmarks[ mergeCount + 1 ].startNode ),\r
+ next;\r
+\r
+ // Check subsequent range.\r
+ while ( 1 )\r
+ {\r
+ next = left.getNextSourceNode( false );\r
+ if ( !right.equals( next ) )\r
+ {\r
+ // This could be yet another bookmark or\r
+ // walking across block boundaries.\r
+ if ( bookmark( next ) || ( next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() ) )\r
+ {\r
+ left = next;\r
+ continue;\r
+ }\r
+ }\r
+ else\r
+ found = 1;\r
+\r
+ break;\r
+ }\r
+\r
+ if ( !found )\r
+ break;\r
+\r
+ mergeCount++;\r
+ }\r
+ }\r
+\r
+ range.moveToBookmark( bookmarks.shift() );\r
+\r
+ // Merge ranges finally after moving to bookmarks.\r
+ while( mergeCount-- )\r
+ {\r
+ next = rangeList[ ++current ];\r
+ next.moveToBookmark( bookmarks.shift() );\r
+ range.setEnd( next.endContainer, next.endOffset );\r
+ }\r
+ }\r
+\r
+ return range;\r
+ }\r
+ };\r
+ },\r
+\r
+ createBookmarks : function( serializable )\r
+ {\r
+ var retval = [], bookmark;\r
+ for ( var i = 0; i < this.length ; i++ )\r
+ {\r
+ retval.push( bookmark = this[ i ].createBookmark( serializable, true) );\r
+\r
+ // Updating the container & offset values for ranges\r
+ // that have been touched.\r
+ for ( var j = i + 1; j < this.length; j++ )\r
+ {\r
+ this[ j ] = updateDirtyRange( bookmark, this[ j ] );\r
+ this[ j ] = updateDirtyRange( bookmark, this[ j ], true );\r
+ }\r
+ }\r
+ return retval;\r
+ },\r
+\r
+ createBookmarks2 : function( normalized )\r
+ {\r
+ var bookmarks = [];\r
+\r
+ for ( var i = 0 ; i < this.length ; i++ )\r
+ bookmarks.push( this[ i ].createBookmark2( normalized ) );\r
+\r
+ return bookmarks;\r
+ },\r
+\r
+ /**\r
+ * Move each range in the list to the position specified by a list of bookmarks.\r
+ * @param {Array} bookmarks The list of bookmarks, each one matching a range in the list.\r
+ */\r
+ moveToBookmarks : function( bookmarks )\r
+ {\r
+ for ( var i = 0 ; i < this.length ; i++ )\r
+ this[ i ].moveToBookmark( bookmarks[ i ] );\r
+ }\r
+ };\r
+\r
+ // Update the specified range which has been mangled by previous insertion of\r
+ // range bookmark nodes.(#3256)\r
+ function updateDirtyRange( bookmark, dirtyRange, checkEnd )\r
+ {\r
+ var serializable = bookmark.serializable,\r
+ container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ],\r
+ offset = checkEnd ? 'endOffset' : 'startOffset';\r
+\r
+ var bookmarkStart = serializable ?\r
+ dirtyRange.document.getById( bookmark.startNode )\r
+ : bookmark.startNode;\r
+\r
+ var bookmarkEnd = serializable ?\r
+ dirtyRange.document.getById( bookmark.endNode )\r
+ : bookmark.endNode;\r
+\r
+ if ( container.equals( bookmarkStart.getPrevious() ) )\r
+ {\r
+ dirtyRange.startOffset = dirtyRange.startOffset\r
+ - container.getLength()\r
+ - bookmarkEnd.getPrevious().getLength();\r
+ container = bookmarkEnd.getNext();\r
+ }\r
+ else if ( container.equals( bookmarkEnd.getPrevious() ) )\r
+ {\r
+ dirtyRange.startOffset = dirtyRange.startOffset - container.getLength();\r
+ container = bookmarkEnd.getNext();\r
+ }\r
+\r
+ container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++;\r
+ container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++;\r
+\r
+ // Update and return this range.\r
+ dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container;\r
+ return dirtyRange;\r
+ }\r
+})();\r
+\r
+/**\r
+ * (Virtual Class) Do not call this constructor. This class is not really part\r
+ * of the API. It just describes the return type of {@link CKEDITOR.dom.rangeList#createIterator}.\r
+ * @name CKEDITOR.dom.rangeListIterator\r
+ * @constructor\r
+ * @example\r
+ */\r