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