1 /* 2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 /** 9 * Represents a list os CKEDITOR.dom.range objects, which can be easily 10 * iterated sequentially. 11 * @constructor 12 * @param {CKEDITOR.dom.range|Array} [ranges] The ranges contained on this list. 13 * Note that, if an array of ranges is specified, the range sequence 14 * should match its DOM order. This class will not help to sort them. 15 */ 16 CKEDITOR.dom.rangeList = function( ranges ) 17 { 18 if ( ranges instanceof CKEDITOR.dom.rangeList ) 19 return ranges; 20 21 if ( !ranges ) 22 ranges = []; 23 else if ( ranges instanceof CKEDITOR.dom.range ) 24 ranges = [ ranges ]; 25 26 return CKEDITOR.tools.extend( ranges, mixins ); 27 }; 28 29 var mixins = 30 /** @lends CKEDITOR.dom.rangeList.prototype */ 31 { 32 /** 33 * Creates an instance of the rangeList iterator, it should be used 34 * only when the ranges processing could be DOM intrusive, which 35 * means it may pollute and break other ranges in this list. 36 * Otherwise, it's enough to just iterate over this array in a for loop. 37 * @returns {CKEDITOR.dom.rangeListIterator} 38 */ 39 createIterator : function() 40 { 41 var rangeList = this, 42 bookmark = CKEDITOR.dom.walker.bookmark(), 43 guard = function( node ) { return ! ( node.is && node.is( 'tr' ) ); }, 44 bookmarks = [], 45 current; 46 47 /** 48 * @lends CKEDITOR.dom.rangeListIterator.prototype 49 */ 50 return { 51 52 /** 53 * Retrieves the next range in the list. 54 * @param {Boolean} mergeConsequent Whether join two adjacent ranges into single, e.g. consequent table cells. 55 */ 56 getNextRange : function( mergeConsequent ) 57 { 58 current = current == undefined ? 0 : current + 1; 59 60 var range = rangeList[ current ]; 61 62 // Multiple ranges might be mangled by each other. 63 if ( range && rangeList.length > 1 ) 64 { 65 // Bookmarking all other ranges on the first iteration, 66 // the range correctness after it doesn't matter since we'll 67 // restore them before the next iteration. 68 if ( !current ) 69 { 70 // Make sure bookmark correctness by reverse processing. 71 for ( var i = rangeList.length - 1; i >= 0; i-- ) 72 bookmarks.unshift( rangeList[ i ].createBookmark( true ) ); 73 } 74 75 if ( mergeConsequent ) 76 { 77 // Figure out how many ranges should be merged. 78 var mergeCount = 0; 79 while ( rangeList[ current + mergeCount + 1 ] ) 80 { 81 var doc = range.document, 82 found = 0, 83 left = doc.getById( bookmarks[ mergeCount ].endNode ), 84 right = doc.getById( bookmarks[ mergeCount + 1 ].startNode ), 85 next; 86 87 // Check subsequent range. 88 while ( 1 ) 89 { 90 next = left.getNextSourceNode( false ); 91 if ( !right.equals( next ) ) 92 { 93 // This could be yet another bookmark or 94 // walking across block boundaries. 95 if ( bookmark( next ) || ( next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() ) ) 96 { 97 left = next; 98 continue; 99 } 100 } 101 else 102 found = 1; 103 104 break; 105 } 106 107 if ( !found ) 108 break; 109 110 mergeCount++; 111 } 112 } 113 114 range.moveToBookmark( bookmarks.shift() ); 115 116 // Merge ranges finally after moving to bookmarks. 117 while( mergeCount-- ) 118 { 119 next = rangeList[ ++current ]; 120 next.moveToBookmark( bookmarks.shift() ); 121 range.setEnd( next.endContainer, next.endOffset ); 122 } 123 } 124 125 return range; 126 } 127 }; 128 }, 129 130 createBookmarks : function( serializable ) 131 { 132 var retval = [], bookmark; 133 for ( var i = 0; i < this.length ; i++ ) 134 { 135 retval.push( bookmark = this[ i ].createBookmark( serializable, true) ); 136 137 // Updating the container & offset values for ranges 138 // that have been touched. 139 for ( var j = i + 1; j < this.length; j++ ) 140 { 141 this[ j ] = updateDirtyRange( bookmark, this[ j ] ); 142 this[ j ] = updateDirtyRange( bookmark, this[ j ], true ); 143 } 144 } 145 return retval; 146 }, 147 148 createBookmarks2 : function( normalized ) 149 { 150 var bookmarks = []; 151 152 for ( var i = 0 ; i < this.length ; i++ ) 153 bookmarks.push( this[ i ].createBookmark2( normalized ) ); 154 155 return bookmarks; 156 }, 157 158 /** 159 * Move each range in the list to the position specified by a list of bookmarks. 160 * @param {Array} bookmarks The list of bookmarks, each one matching a range in the list. 161 */ 162 moveToBookmarks : function( bookmarks ) 163 { 164 for ( var i = 0 ; i < this.length ; i++ ) 165 this[ i ].moveToBookmark( bookmarks[ i ] ); 166 } 167 }; 168 169 // Update the specified range which has been mangled by previous insertion of 170 // range bookmark nodes.(#3256) 171 function updateDirtyRange( bookmark, dirtyRange, checkEnd ) 172 { 173 var serializable = bookmark.serializable, 174 container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ], 175 offset = checkEnd ? 'endOffset' : 'startOffset'; 176 177 var bookmarkStart = serializable ? 178 dirtyRange.document.getById( bookmark.startNode ) 179 : bookmark.startNode; 180 181 var bookmarkEnd = serializable ? 182 dirtyRange.document.getById( bookmark.endNode ) 183 : bookmark.endNode; 184 185 if ( container.equals( bookmarkStart.getPrevious() ) ) 186 { 187 dirtyRange.startOffset = dirtyRange.startOffset 188 - container.getLength() 189 - bookmarkEnd.getPrevious().getLength(); 190 container = bookmarkEnd.getNext(); 191 } 192 else if ( container.equals( bookmarkEnd.getPrevious() ) ) 193 { 194 dirtyRange.startOffset = dirtyRange.startOffset - container.getLength(); 195 container = bookmarkEnd.getNext(); 196 } 197 198 container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++; 199 container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++; 200 201 // Update and return this range. 202 dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container; 203 return dirtyRange; 204 } 205 })(); 206 207 /** 208 * (Virtual Class) Do not call this constructor. This class is not really part 209 * of the API. It just describes the return type of {@link CKEDITOR.dom.rangeList#createIterator}. 210 * @name CKEDITOR.dom.rangeListIterator 211 * @constructor 212 * @example 213 */ 214