~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/web/static/htdocs/applets/FCKeditor/editor/_source/classes/fckw3crange.js

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

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-2010 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 implementation 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 cloning (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
 
 
265
 
                        // If the offset points to the first node, we don't have a
266
 
                        // sibling, so let's use the first one, but mark it for removal.
267
 
                        if ( startOffset == 0 )
268
 
                        {
269
 
                                // Let's create a temporary node and mark it for removal.
270
 
                                startNode = startNode.insertBefore( this._Document.createTextNode(''), startNode.firstChild ) ;
271
 
                                removeStartNode = true ;
272
 
                        }
273
 
                        else if ( startOffset > startNode.childNodes.length - 1 )
274
 
                        {
275
 
                                // Let's create a temporary node and mark it for removal.
276
 
                                startNode = startNode.appendChild( this._Document.createTextNode('') ) ;
277
 
                                removeStartNode = true ;
278
 
                        }
279
 
                        else
280
 
                                startNode = startNode.childNodes[ startOffset ].previousSibling ;
281
 
                }
282
 
 
283
 
                // Get the parent nodes tree for the start and end boundaries.
284
 
                var startParents        = FCKDomTools.GetParents( startNode ) ;
285
 
                var endParents          = FCKDomTools.GetParents( endNode ) ;
286
 
 
287
 
                // Compare them, to find the top most siblings.
288
 
                var i, topStart, topEnd ;
289
 
 
290
 
                for ( i = 0 ; i < startParents.length ; i++ )
291
 
                {
292
 
                        topStart        = startParents[i] ;
293
 
                        topEnd          = endParents[i] ;
294
 
 
295
 
                        // The compared nodes will match until we find the top most
296
 
                        // siblings (different nodes that have the same parent).
297
 
                        // "i" will hold the index in the parents array for the top
298
 
                        // most element.
299
 
                        if ( topStart != topEnd )
300
 
                                break ;
301
 
                }
302
 
 
303
 
                var clone, levelStartNode, levelClone, currentNode, currentSibling ;
304
 
 
305
 
                if ( docFrag )
306
 
                        clone = docFrag.RootNode ;
307
 
 
308
 
                // Remove all successive sibling nodes for every node in the
309
 
                // startParents tree.
310
 
                for ( var j = i ; j < startParents.length ; j++ )
311
 
                {
312
 
                        levelStartNode = startParents[j] ;
313
 
 
314
 
                        // For Extract and Clone, we must clone this level.
315
 
                        if ( clone && levelStartNode != startNode )             // action = 0 = Delete
316
 
                                levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == startNode ) ) ;
317
 
 
318
 
                        currentNode = levelStartNode.nextSibling ;
319
 
 
320
 
                        while( currentNode )
321
 
                        {
322
 
                                // Stop processing when the current node matches a node in the
323
 
                                // endParents tree or if it is the endNode.
324
 
                                if ( currentNode == endParents[j] || currentNode == endNode )
325
 
                                        break ;
326
 
 
327
 
                                // Cache the next sibling.
328
 
                                currentSibling = currentNode.nextSibling ;
329
 
 
330
 
                                // If cloning, just clone it.
331
 
                                if ( action == 2 )      // 2 = Clone
332
 
                                        clone.appendChild( currentNode.cloneNode( true ) ) ;
333
 
                                else
334
 
                                {
335
 
                                        // Both Delete and Extract will remove the node.
336
 
                                        currentNode.parentNode.removeChild( currentNode ) ;
337
 
 
338
 
                                        // When Extracting, move the removed node to the docFrag.
339
 
                                        if ( action == 1 )      // 1 = Extract
340
 
                                                clone.appendChild( currentNode ) ;
341
 
                                }
342
 
 
343
 
                                currentNode = currentSibling ;
344
 
                        }
345
 
 
346
 
                        if ( clone )
347
 
                                clone = levelClone ;
348
 
                }
349
 
 
350
 
                if ( docFrag )
351
 
                        clone = docFrag.RootNode ;
352
 
 
353
 
                // Remove all previous sibling nodes for every node in the
354
 
                // endParents tree.
355
 
                for ( var k = i ; k < endParents.length ; k++ )
356
 
                {
357
 
                        levelStartNode = endParents[k] ;
358
 
 
359
 
                        // For Extract and Clone, we must clone this level.
360
 
                        if ( action > 0 && levelStartNode != endNode )          // action = 0 = Delete
361
 
                                levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == endNode ) ) ;
362
 
 
363
 
                        // The processing of siblings may have already been done by the parent.
364
 
                        if ( !startParents[k] || levelStartNode.parentNode != startParents[k].parentNode )
365
 
                        {
366
 
                                currentNode = levelStartNode.previousSibling ;
367
 
 
368
 
                                while( currentNode )
369
 
                                {
370
 
                                        // Stop processing when the current node matches a node in the
371
 
                                        // startParents tree or if it is the startNode.
372
 
                                        if ( currentNode == startParents[k] || currentNode == startNode )
373
 
                                                break ;
374
 
 
375
 
                                        // Cache the next sibling.
376
 
                                        currentSibling = currentNode.previousSibling ;
377
 
 
378
 
                                        // If cloning, just clone it.
379
 
                                        if ( action == 2 )      // 2 = Clone
380
 
                                                clone.insertBefore( currentNode.cloneNode( true ), clone.firstChild ) ;
381
 
                                        else
382
 
                                        {
383
 
                                                // Both Delete and Extract will remove the node.
384
 
                                                currentNode.parentNode.removeChild( currentNode ) ;
385
 
 
386
 
                                                // When Extracting, mode the removed node to the docFrag.
387
 
                                                if ( action == 1 )      // 1 = Extract
388
 
                                                        clone.insertBefore( currentNode, clone.firstChild ) ;
389
 
                                        }
390
 
 
391
 
                                        currentNode = currentSibling ;
392
 
                                }
393
 
                        }
394
 
 
395
 
                        if ( clone )
396
 
                                clone = levelClone ;
397
 
                }
398
 
 
399
 
                if ( action == 2 )              // 2 = Clone.
400
 
                {
401
 
                        // No changes in the DOM should be done, so fix the split text (if any).
402
 
 
403
 
                        var startTextNode = this.startContainer ;
404
 
                        if ( startTextNode.nodeType == 3 )
405
 
                        {
406
 
                                startTextNode.data += startTextNode.nextSibling.data ;
407
 
                                startTextNode.parentNode.removeChild( startTextNode.nextSibling ) ;
408
 
                        }
409
 
 
410
 
                        var endTextNode = this.endContainer ;
411
 
                        if ( endTextNode.nodeType == 3 && endTextNode.nextSibling )
412
 
                        {
413
 
                                endTextNode.data += endTextNode.nextSibling.data ;
414
 
                                endTextNode.parentNode.removeChild( endTextNode.nextSibling ) ;
415
 
                        }
416
 
                }
417
 
                else
418
 
                {
419
 
                        // Collapse the range.
420
 
 
421
 
                        // If a node has been partially selected, collapse the range between
422
 
                        // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
423
 
                        if ( topStart && topEnd && ( startNode.parentNode != topStart.parentNode || endNode.parentNode != topEnd.parentNode ) )
424
 
                        {
425
 
                                var endIndex = FCKDomTools.GetIndexOf( topEnd ) ;
426
 
 
427
 
                                // If the start node is to be removed, we must correct the
428
 
                                // index to reflect the removal.
429
 
                                if ( removeStartNode && topEnd.parentNode == startNode.parentNode )
430
 
                                        endIndex-- ;
431
 
 
432
 
                                this.setStart( topEnd.parentNode, endIndex ) ;
433
 
                        }
434
 
 
435
 
                        // Collapse it to the start.
436
 
                        this.collapse( true ) ;
437
 
                }
438
 
 
439
 
                // Cleanup any marked node.
440
 
                if( removeStartNode )
441
 
                        startNode.parentNode.removeChild( startNode ) ;
442
 
 
443
 
                if( removeEndNode && endNode.parentNode )
444
 
                        endNode.parentNode.removeChild( endNode ) ;
445
 
        },
446
 
 
447
 
        cloneRange : function()
448
 
        {
449
 
                return FCKW3CRange.CreateFromRange( this._Document, this ) ;
450
 
        }
451
 
} ;