diff options
Diffstat (limited to 'sources/core/dom/rangelist.js')
-rw-r--r-- | sources/core/dom/rangelist.js | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/sources/core/dom/rangelist.js b/sources/core/dom/rangelist.js new file mode 100644 index 0000000..250dfd9 --- /dev/null +++ b/sources/core/dom/rangelist.js | |||
@@ -0,0 +1,199 @@ | |||
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 | */ | ||