initial commit
[namibia] / public / scripts / ckeditor / _source / core / dom / rangelist.js
1 /*
2 Copyright (c) 2003-2011, 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  */