source: sandbox/filemanager/tp/fckeditor/editor/_source/classes/fckw3crange.js @ 1575

Revision 1575, 13.3 KB checked in by amuller, 14 years ago (diff)

Ticket #597 - Implentação, melhorias do modulo gerenciador de arquivos

  • Property svn:executable set to *
Line 
1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2009 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
26var 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
37FCKW3CRange.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
56FCKW3CRange.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
64FCKW3CRange.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} ;
Note: See TracBrowser for help on using the repository browser.