JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
9c12e48a24f76518c4db842d1c94f7aefa42956f
[ckeditor.git] / _source / core / dom / rangelist.js
1 /*\r
2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 (function()\r
7 {\r
8         /**\r
9          * Represents a list os CKEDITOR.dom.range objects, which can be easily\r
10          * iterated sequentially.\r
11          * @constructor\r
12          * @param {CKEDITOR.dom.range|Array} [ranges] The ranges contained on this list.\r
13          *              Note that, if an array of ranges is specified, the range sequence\r
14          *              should match its DOM order. This class will not help to sort them.\r
15          */\r
16         CKEDITOR.dom.rangeList = function( ranges )\r
17         {\r
18                 if ( ranges instanceof CKEDITOR.dom.rangeList )\r
19                         return ranges;\r
20 \r
21                 if ( !ranges )\r
22                         ranges = [];\r
23                 else if ( ranges instanceof CKEDITOR.dom.range )\r
24                         ranges = [ ranges ];\r
25 \r
26                 return CKEDITOR.tools.extend( ranges, mixins );\r
27         };\r
28 \r
29         var mixins =\r
30         /** @lends CKEDITOR.dom.rangeList.prototype */\r
31         {\r
32                         /**\r
33                          * Creates an instance of the rangeList iterator, it should be used\r
34                          * only when the ranges processing could be DOM intrusive, which\r
35                          * means it may pollute and break other ranges in this list.\r
36                          * Otherwise, it's enough to just iterate over this array in a for loop.\r
37                          * @returns {CKEDITOR.dom.rangeListIterator}\r
38                          */\r
39                         createIterator : function()\r
40                         {\r
41                                 var rangeList = this,\r
42                                         bookmarks = [],\r
43                                         current;\r
44 \r
45                                 /**\r
46                                  * @lends CKEDITOR.dom.rangeListIterator.prototype\r
47                                  */\r
48                                 return {\r
49 \r
50                                         /**\r
51                                          * Retrieves the next range in the list.\r
52                                          */\r
53                                         getNextRange : function()\r
54                                         {\r
55                                                 current = current == undefined ? 0 : current + 1;\r
56 \r
57                                                 var range = rangeList[ current ];\r
58 \r
59                                                 // Multiple ranges might be mangled by each other.\r
60                                                 if ( range && rangeList.length > 1 )\r
61                                                 {\r
62                                                         // Bookmarking all other ranges on the first iteration,\r
63                                                         // the range correctness after it doesn't matter since we'll\r
64                                                         // restore them before the next iteration.\r
65                                                         if ( !current )\r
66                                                         {\r
67                                                                 // Make sure bookmark correctness by reverse processing.\r
68                                                                 for ( var i = rangeList.length - 1; i > 0; i-- )\r
69                                                                         bookmarks.unshift( rangeList[ i ].createBookmark( true ) );\r
70                                                         }\r
71                                                         else\r
72                                                                 range.moveToBookmark( bookmarks.shift() );\r
73                                                 }\r
74 \r
75                                                 return range;\r
76                                         }\r
77                                 };\r
78                         },\r
79 \r
80                         createBookmarks : function( serializable )\r
81                         {\r
82                                 var retval = [], bookmark;\r
83                                 for ( var i = 0; i < this.length ; i++ )\r
84                                 {\r
85                                         retval.push( bookmark = this[ i ].createBookmark( serializable, true) );\r
86 \r
87                                         // Updating the container & offset values for ranges\r
88                                         // that have been touched.\r
89                                         for ( var j = i + 1; j < this.length; j++ )\r
90                                         {\r
91                                                 this[ j ] = updateDirtyRange( bookmark, this[ j ] );\r
92                                                 this[ j ] = updateDirtyRange( bookmark, this[ j ], true );\r
93                                         }\r
94                                 }\r
95                                 return retval;\r
96                         },\r
97 \r
98                         createBookmarks2 : function( normalized )\r
99                         {\r
100                                 var bookmarks = [];\r
101 \r
102                                 for ( var i = 0 ; i < this.length ; i++ )\r
103                                         bookmarks.push( this[ i ].createBookmark2( normalized ) );\r
104 \r
105                                 return bookmarks;\r
106                         },\r
107 \r
108                         /**\r
109                          * Move each range in the list to the position specified by a list of bookmarks.\r
110                          * @param {Array} bookmarks The list of bookmarks, each one matching a range in the list.\r
111                          */\r
112                         moveToBookmarks :  function( bookmarks )\r
113                         {\r
114                                 for ( var i = 0 ; i < this.length ; i++ )\r
115                                         this[ i ].moveToBookmark( bookmarks[ i ] );\r
116                         }\r
117         };\r
118 \r
119         // Update the specified range which has been mangled by previous insertion of\r
120         // range bookmark nodes.(#3256)\r
121         function updateDirtyRange( bookmark, dirtyRange, checkEnd )\r
122         {\r
123                 var serializable = bookmark.serializable,\r
124                         container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ],\r
125                         offset = checkEnd ? 'endOffset' : 'startOffset';\r
126 \r
127                 var bookmarkStart = serializable ?\r
128                                 dirtyRange.document.getById( bookmark.startNode )\r
129                                 : bookmark.startNode;\r
130 \r
131                 var bookmarkEnd = serializable ?\r
132                                 dirtyRange.document.getById( bookmark.endNode )\r
133                                 : bookmark.endNode;\r
134 \r
135                 if ( container.equals( bookmarkStart.getPrevious() ) )\r
136                 {\r
137                         dirtyRange.startOffset = dirtyRange.startOffset\r
138                                         - container.getLength()\r
139                                         - bookmarkEnd.getPrevious().getLength();\r
140                         container = bookmarkEnd.getNext();\r
141                 }\r
142                 else if ( container.equals( bookmarkEnd.getPrevious() ) )\r
143                 {\r
144                         dirtyRange.startOffset = dirtyRange.startOffset - container.getLength();\r
145                         container = bookmarkEnd.getNext();\r
146                 }\r
147 \r
148                 container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++;\r
149                 container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++;\r
150 \r
151                 // Update and return this range.\r
152                 dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container;\r
153                 return dirtyRange;\r
154         }\r
155 })();\r
156 \r
157 /**\r
158  * (Virtual Class) Do not call this constructor. This class is not really part\r
159  *      of the API. It just describes the return type of {@link CKEDITOR.dom.rangeList#createIterator}.\r
160  * @name CKEDITOR.dom.rangeListIterator\r
161  * @constructor\r
162  * @example\r
163  */\r