~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-dataset/src/main/webapp/dhis-web-maintenance-dataset/javascript/FCK/editor/_source/classes/fckw3crange.js

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 
3
 * Copyright (C) 2003-2007 Frederico Caldeira Knabben
 
4
 *
 
5
 * == BEGIN LICENSE ==
 
6
 *
 
7
 * Licensed under the terms of any of the following licenses at your
 
8
 * choice:
 
9
 *
 
10
 *  - GNU General Public License Version 2 or later (the "GPL")
 
11
 *    http://www.gnu.org/licenses/gpl.html
 
12
 *
 
13
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 
14
 *    http://www.gnu.org/licenses/lgpl.html
 
15
 *
 
16
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 
17
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 
18
 *
 
19
 * == END LICENSE ==
 
20
 *
 
21
 * This class partially implements the W3C DOM Range for browser that don't
 
22
 * support the standards (like IE):
 
23
 * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html
 
24
 */
 
25
 
 
26
var FCKW3CRange = function( parentDocument )
 
27
{
 
28
        this._Document = parentDocument ;
 
29
 
 
30
        this.startContainer     = null ;
 
31
        this.startOffset        = null ;
 
32
        this.endContainer       = null ;
 
33
        this.endOffset          = null ;
 
34
        this.collapsed          = true ;
 
35
}
 
36
 
 
37
FCKW3CRange.CreateRange = function( parentDocument )
 
38
{
 
39
        // We could opt to use the Range implentation of the browsers. The problem
 
40
        // is that every browser have different bugs on their implementations,
 
41
        // mostly related to different interpretations of the W3C specifications.
 
42
        // So, for now, let's use our implementation and pray for browsers fixings
 
43
        // soon. Otherwise will go crazy on trying to find out workarounds.
 
44
        /*
 
45
        // Get the browser implementation of the range, if available.
 
46
        if ( parentDocument.createRange )
 
47
        {
 
48
                var range = parentDocument.createRange() ;
 
49
                if ( typeof( range.startContainer ) != 'undefined' )
 
50
                        return range ;
 
51
        }
 
52
        */
 
53
        return new FCKW3CRange( parentDocument ) ;
 
54
}
 
55
 
 
56
FCKW3CRange.CreateFromRange = function( parentDocument, sourceRange )
 
57
{
 
58
        var range = FCKW3CRange.CreateRange( parentDocument ) ;
 
59
        range.setStart( sourceRange.startContainer, sourceRange.startOffset ) ;
 
60
        range.setEnd( sourceRange.endContainer, sourceRange.endOffset ) ;
 
61
        return range ;
 
62
}
 
63
 
 
64
FCKW3CRange.prototype =
 
65
{
 
66
 
 
67
        _UpdateCollapsed : function()
 
68
        {
 
69
      this.collapsed = ( this.startContainer == this.endContainer && this.startOffset == this.endOffset ) ;
 
70
        },
 
71
 
 
72
        // W3C requires a check for the new position. If it is after the end
 
73
        // boundary, the range should be collapsed to the new start. It seams we
 
74
        // will not need this check for our use of this class so we can ignore it for now.
 
75
        setStart : function( refNode, offset )
 
76
        {
 
77
                this.startContainer     = refNode ;
 
78
                this.startOffset        = offset ;
 
79
 
 
80
                if ( !this.endContainer )
 
81
                {
 
82
                        this.endContainer       = refNode ;
 
83
                        this.endOffset          = offset ;
 
84
                }
 
85
 
 
86
                this._UpdateCollapsed() ;
 
87
        },
 
88
 
 
89
        // W3C requires a check for the new position. If it is before the start
 
90
        // boundary, the range should be collapsed to the new end. It seams we
 
91
        // will not need this check for our use of this class so we can ignore it for now.
 
92
        setEnd : function( refNode, offset )
 
93
        {
 
94
                this.endContainer       = refNode ;
 
95
                this.endOffset          = offset ;
 
96
 
 
97
                if ( !this.startContainer )
 
98
                {
 
99
                        this.startContainer     = refNode ;
 
100
                        this.startOffset        = offset ;
 
101
                }
 
102
 
 
103
                this._UpdateCollapsed() ;
 
104
        },
 
105
 
 
106
        setStartAfter : function( refNode )
 
107
        {
 
108
                this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;
 
109
        },
 
110
 
 
111
        setStartBefore : function( refNode )
 
112
        {
 
113
                this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;
 
114
        },
 
115
 
 
116
        setEndAfter : function( refNode )
 
117
        {
 
118
                this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;
 
119
        },
 
120
 
 
121
        setEndBefore : function( refNode )
 
122
        {
 
123
                this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;
 
124
        },
 
125
 
 
126
        collapse : function( toStart )
 
127
        {
 
128
                if ( toStart )
 
129
                {
 
130
                        this.endContainer       = this.startContainer ;
 
131
                        this.endOffset          = this.startOffset ;
 
132
                }
 
133
                else
 
134
                {
 
135
                        this.startContainer     = this.endContainer ;
 
136
                        this.startOffset        = this.endOffset ;
 
137
                }
 
138
 
 
139
                this.collapsed = true ;
 
140
        },
 
141
 
 
142
        selectNodeContents : function( refNode )
 
143
        {
 
144
                this.setStart( refNode, 0 ) ;
 
145
                this.setEnd( refNode, refNode.nodeType == 3 ? refNode.data.length : refNode.childNodes.length ) ;
 
146
        },
 
147
 
 
148
        insertNode : function( newNode )
 
149
        {
 
150
                var startContainer = this.startContainer ;
 
151
                var startOffset = this.startOffset ;
 
152
 
 
153
                // If we are in a text node.
 
154
                if ( startContainer.nodeType == 3 )
 
155
                {
 
156
                        startContainer.splitText( startOffset ) ;
 
157
 
 
158
                        // Check if it is necessary to update the end boundary.
 
159
                        if ( startContainer == this.endContainer )
 
160
                                this.setEnd( startContainer.nextSibling, this.endOffset - this.startOffset ) ;
 
161
 
 
162
                        // Insert the new node it after the text node.
 
163
                        FCKDomTools.InsertAfterNode( startContainer, newNode ) ;
 
164
 
 
165
                        return ;
 
166
                }
 
167
                else
 
168
                {
 
169
                        // Simply insert the new node before the current start node.
 
170
                        startContainer.insertBefore( newNode, startContainer.childNodes[ startOffset ] || null ) ;
 
171
 
 
172
                        // Check if it is necessary to update the end boundary.
 
173
                        if ( startContainer == this.endContainer )
 
174
                        {
 
175
                                this.endOffset++ ;
 
176
                                this.collapsed = false ;
 
177
                        }
 
178
                }
 
179
        },
 
180
 
 
181
        deleteContents : function()
 
182
        {
 
183
                if ( this.collapsed )
 
184
                        return ;
 
185
 
 
186
                this._ExecContentsAction( 0 ) ;
 
187
        },
 
188
 
 
189
        extractContents : function()
 
190
        {
 
191
                var docFrag = new FCKDocumentFragment( this._Document ) ;
 
192
 
 
193
                if ( !this.collapsed )
 
194
                        this._ExecContentsAction( 1, docFrag ) ;
 
195
 
 
196
                return docFrag ;
 
197
        },
 
198
 
 
199
        // The selection may be lost when clonning (due to the splitText() call).
 
200
        cloneContents : function()
 
201
        {
 
202
                var docFrag = new FCKDocumentFragment( this._Document ) ;
 
203
 
 
204
                if ( !this.collapsed )
 
205
                        this._ExecContentsAction( 2, docFrag ) ;
 
206
 
 
207
                return docFrag ;
 
208
        },
 
209
 
 
210
        _ExecContentsAction : function( action, docFrag )
 
211
        {
 
212
                var startNode   = this.startContainer ;
 
213
                var endNode             = this.endContainer ;
 
214
 
 
215
                var startOffset = this.startOffset ;
 
216
                var endOffset   = this.endOffset ;
 
217
 
 
218
                var removeStartNode     = false ;
 
219
                var removeEndNode       = false ;
 
220
 
 
221
                // Check the start and end nodes and make the necessary removals or changes.
 
222
 
 
223
                // Start from the end, otherwise DOM mutations (splitText) made in the
 
224
                // start boundary may interfere on the results here.
 
225
 
 
226
                // For text containers, we must simply split the node and point to the
 
227
                // second part. The removal will be handled by the rest of the code .
 
228
                if ( endNode.nodeType == 3 )
 
229
                        endNode = endNode.splitText( endOffset ) ;
 
230
                else
 
231
                {
 
232
                        // If the end container has children and the offset is pointing
 
233
                        // to a child, then we should start from it.
 
234
                        if ( endNode.childNodes.length > 0 )
 
235
                        {
 
236
                                // If the offset points after the last node.
 
237
                                if ( endOffset > endNode.childNodes.length - 1 )
 
238
                                {
 
239
                                        // Let's create a temporary node and mark it for removal.
 
240
                                        endNode = FCKDomTools.InsertAfterNode( endNode.lastChild, this._Document.createTextNode('') ) ;
 
241
                                        removeEndNode = true ;
 
242
                                }
 
243
                                else
 
244
                                        endNode = endNode.childNodes[ endOffset ] ;
 
245
                        }
 
246
                }
 
247
 
 
248
                // For text containers, we must simply split the node. The removal will
 
249
                // be handled by the rest of the code .
 
250
                if ( startNode.nodeType == 3 )
 
251
                {
 
252
                        startNode.splitText( startOffset ) ;
 
253
 
 
254
                        // In cases the end node is the same as the start node, the above
 
255
                        // splitting will also split the end, so me must move the end to
 
256
                        // the second part of the split.
 
257
                        if ( startNode == endNode )
 
258
                                endNode = startNode.nextSibling ;
 
259
                }
 
260
                else
 
261
                {
 
262
                        // If the start container has children and the offset is pointing
 
263
                        // to a child, then we should start from its previous sibling.
 
264
                        if ( startNode.childNodes.length > 0 &&  startOffset <= startNode.childNodes.length - 1 )
 
265
                        {
 
266
                                // If the offset points to the first node, we don't have a
 
267
                                // sibling, so let's use the first one, but mark it for removal.
 
268
                                if ( startOffset == 0 )
 
269
                                {
 
270
                                        // Let's create a temporary node and mark it for removal.
 
271
                                        startNode = startNode.insertBefore( this._Document.createTextNode(''), startNode.firstChild ) ;
 
272
                                        removeStartNode = true ;
 
273
                                }
 
274
                                else
 
275
                                        startNode = startNode.childNodes[ startOffset ].previousSibling ;
 
276
                        }
 
277
                }
 
278
 
 
279
                // Get the parent nodes tree for the start and end boundaries.
 
280
                var startParents        = FCKDomTools.GetParents( startNode ) ;
 
281
                var endParents          = FCKDomTools.GetParents( endNode ) ;
 
282
 
 
283
                // Compare them, to find the top most siblings.
 
284
                var i, topStart, topEnd ;
 
285
 
 
286
                for ( i = 0 ; i < startParents.length ; i++ )
 
287
                {
 
288
                        topStart        = startParents[i] ;
 
289
                        topEnd          = endParents[i] ;
 
290
 
 
291
                        // The compared nodes will match until we find the top most
 
292
                        // siblings (different nodes that have the same parent).
 
293
                        // "i" will hold the index in the parants array for the top
 
294
                        // most element.
 
295
                        if ( topStart != topEnd )
 
296
                                break ;
 
297
                }
 
298
 
 
299
                var clone, levelStartNode, levelClone, currentNode, currentSibling ;
 
300
 
 
301
                if ( docFrag )
 
302
                        clone = docFrag.RootNode ;
 
303
 
 
304
                // Remove all successive sibling nodes for every node in the
 
305
                // startParents tree.
 
306
                for ( var j = i ; j < startParents.length ; j++ )
 
307
                {
 
308
                        levelStartNode = startParents[j] ;
 
309
 
 
310
                        // For Extract and Clone, we must clone this level.
 
311
                        if ( clone && levelStartNode != startNode )             // action = 0 = Delete
 
312
                                levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == startNode ) ) ;
 
313
 
 
314
                        currentNode = levelStartNode.nextSibling ;
 
315
 
 
316
                        while( currentNode )
 
317
                        {
 
318
                                // Stop processing when the current node matches a node in the
 
319
                                // endParents tree or if it is the endNode.
 
320
                                if ( currentNode == endParents[j] || currentNode == endNode )
 
321
                                        break ;
 
322
 
 
323
                                // Cache the next sibling.
 
324
                                currentSibling = currentNode.nextSibling ;
 
325
 
 
326
                                // If clonning, just clone it.
 
327
                                if ( action == 2 )      // 2 = Clone
 
328
                                        clone.appendChild( currentNode.cloneNode( true ) ) ;
 
329
                                else
 
330
                                {
 
331
                                        // Both Delete and Extract will remove the node.
 
332
                                        currentNode.parentNode.removeChild( currentNode ) ;
 
333
 
 
334
                                        // When Extracting, move the removed node to the docFrag.
 
335
                                        if ( action == 1 )      // 1 = Extract
 
336
                                                clone.appendChild( currentNode ) ;
 
337
                                }
 
338
 
 
339
                                currentNode = currentSibling ;
 
340
                        }
 
341
 
 
342
                        if ( clone )
 
343
                                clone = levelClone ;
 
344
                }
 
345
 
 
346
                if ( docFrag )
 
347
                        clone = docFrag.RootNode ;
 
348
 
 
349
                // Remove all previous sibling nodes for every node in the
 
350
                // endParents tree.
 
351
                for ( var k = i ; k < endParents.length ; k++ )
 
352
                {
 
353
                        levelStartNode = endParents[k] ;
 
354
 
 
355
                        // For Extract and Clone, we must clone this level.
 
356
                        if ( action > 0 && levelStartNode != endNode )          // action = 0 = Delete
 
357
                                levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == endNode ) ) ;
 
358
 
 
359
                        // The processing of siblings may have already been done by the parent.
 
360
                        if ( !startParents[k] || levelStartNode.parentNode != startParents[k].parentNode )
 
361
                        {
 
362
                                currentNode = levelStartNode.previousSibling ;
 
363
 
 
364
                                while( currentNode )
 
365
                                {
 
366
                                        // Stop processing when the current node matches a node in the
 
367
                                        // startParents tree or if it is the startNode.
 
368
                                        if ( currentNode == startParents[k] || currentNode == startNode )
 
369
                                                break ;
 
370
 
 
371
                                        // Cache the next sibling.
 
372
                                        currentSibling = currentNode.previousSibling ;
 
373
 
 
374
                                        // If clonning, just clone it.
 
375
                                        if ( action == 2 )      // 2 = Clone
 
376
                                                clone.insertBefore( currentNode.cloneNode( true ), clone.firstChild ) ;
 
377
                                        else
 
378
                                        {
 
379
                                                // Both Delete and Extract will remove the node.
 
380
                                                currentNode.parentNode.removeChild( currentNode ) ;
 
381
 
 
382
                                                // When Extracting, mode the removed node to the docFrag.
 
383
                                                if ( action == 1 )      // 1 = Extract
 
384
                                                        clone.insertBefore( currentNode, clone.firstChild ) ;
 
385
                                        }
 
386
 
 
387
                                        currentNode = currentSibling ;
 
388
                                }
 
389
                        }
 
390
 
 
391
                        if ( clone )
 
392
                                clone = levelClone ;
 
393
                }
 
394
 
 
395
                if ( action == 2 )              // 2 = Clone.
 
396
                {
 
397
                        // No changes in the DOM should be done, so fix the split text (if any).
 
398
 
 
399
                        var startTextNode = this.startContainer ;
 
400
                        if ( startTextNode.nodeType == 3 )
 
401
                        {
 
402
                                startTextNode.data += startTextNode.nextSibling.data ;
 
403
                                startTextNode.parentNode.removeChild( startTextNode.nextSibling ) ;
 
404
                        }
 
405
 
 
406
                        var endTextNode = this.endContainer ;
 
407
                        if ( endTextNode.nodeType == 3 && endTextNode.nextSibling )
 
408
                        {
 
409
                                endTextNode.data += endTextNode.nextSibling.data ;
 
410
                                endTextNode.parentNode.removeChild( endTextNode.nextSibling ) ;
 
411
                        }
 
412
                }
 
413
                else
 
414
                {
 
415
                        // Collapse the range.
 
416
 
 
417
                        // If a node has been partially selected, collapse the range between
 
418
                        // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
 
419
                        if ( topStart && topEnd && ( startNode.parentNode != topStart.parentNode || endNode.parentNode != topEnd.parentNode ) )
 
420
                                this.setStart( topEnd.parentNode, FCKDomTools.GetIndexOf( topEnd ) ) ;
 
421
 
 
422
                        // Collapse it to the start.
 
423
                        this.collapse( true ) ;
 
424
                }
 
425
 
 
426
                // Cleanup any marked node.
 
427
                if( removeStartNode )
 
428
                        startNode.parentNode.removeChild( startNode ) ;
 
429
 
 
430
                if( removeEndNode && endNode.parentNode )
 
431
                        endNode.parentNode.removeChild( endNode ) ;
 
432
        },
 
433
 
 
434
        cloneRange : function()
 
435
        {
 
436
                return FCKW3CRange.CreateFromRange( this._Document, this ) ;
 
437
        },
 
438
 
 
439
        toString : function()
 
440
        {
 
441
                var docFrag = this.cloneContents() ;
 
442
 
 
443
                var tmpDiv = this._Document.createElement( 'div' ) ;
 
444
                docFrag.AppendTo( tmpDiv ) ;
 
445
 
 
446
                return tmpDiv.textContent || tmpDiv.innerText ;
 
447
        }
 
448
} ;