source: branches/1.2/workflow/js/fckeditor/editor/_source/classes/fckw3crange.js @ 1349

Revision 1349, 12.7 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

  • Property svn:executable set to *
Line 
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
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 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
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 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} ;
Note: See TracBrowser for help on using the repository browser.