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

Revision 1349, 11.4 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 * Class for working with a selection range, much like the W3C DOM Range, but
22 * it is not intented to be an implementation of the W3C interface.
23 */
24
25var FCKDomRange = function( sourceWindow )
26{
27        this.Window = sourceWindow ;
28}
29
30FCKDomRange.prototype =
31{
32
33        _UpdateElementInfo : function()
34        {
35                if ( !this._Range )
36                        this.Release( true ) ;
37                else
38                {
39                        var eStart      = this._Range.startContainer ;
40                        var eEnd        = this._Range.endContainer ;
41
42                        var oElementPath = new FCKElementPath( eStart ) ;
43                        this.StartContainer             = oElementPath.LastElement ;
44                        this.StartBlock                 = oElementPath.Block ;
45                        this.StartBlockLimit    = oElementPath.BlockLimit ;
46
47                        if ( eStart != eEnd )
48                                oElementPath = new FCKElementPath( eEnd ) ;
49                        this.EndContainer               = oElementPath.LastElement ;
50                        this.EndBlock                   = oElementPath.Block ;
51                        this.EndBlockLimit              = oElementPath.BlockLimit ;
52                }
53        },
54
55        CreateRange : function()
56        {
57                return new FCKW3CRange( this.Window.document ) ;
58        },
59
60        DeleteContents : function()
61        {
62                if ( this._Range )
63                {
64                        this._Range.deleteContents() ;
65                        this._UpdateElementInfo() ;
66                }
67        },
68
69        ExtractContents : function()
70        {
71                if ( this._Range )
72                {
73                        var docFrag = this._Range.extractContents() ;
74                        this._UpdateElementInfo() ;
75                        return docFrag ;
76                }
77        },
78
79        CheckIsCollapsed : function()
80        {
81                if ( this._Range )
82                        return this._Range.collapsed ;
83        },
84
85        Collapse : function( toStart )
86        {
87                if ( this._Range )
88                        this._Range.collapse( toStart ) ;
89
90                this._UpdateElementInfo() ;
91        },
92
93        Clone : function()
94        {
95                var oClone = FCKTools.CloneObject( this ) ;
96
97                if ( this._Range )
98                        oClone._Range = this._Range.cloneRange() ;
99
100                return oClone ;
101        },
102
103        MoveToNodeContents : function( targetNode )
104        {
105                if ( !this._Range )
106                        this._Range = this.CreateRange() ;
107
108                this._Range.selectNodeContents( targetNode ) ;
109
110                this._UpdateElementInfo() ;
111        },
112
113        MoveToElementStart : function( targetElement )
114        {
115                this.SetStart(targetElement,1) ;
116                this.SetEnd(targetElement,1) ;
117        },
118
119        // Moves to the first editing point inside a element. For example, in a
120        // element tree like "<p><b><i></i></b> Text</p>", the start editing point
121        // is "<p><b><i>^</i></b> Text</p>" (inside <i>).
122        MoveToElementEditStart : function( targetElement )
123        {
124                var child ;
125
126                while ( ( child = targetElement.firstChild ) && child.nodeType == 1 && FCKListsLib.EmptyElements[ child.nodeName.toLowerCase() ] == null )
127                        targetElement = child ;
128
129                this.MoveToElementStart( targetElement ) ;
130        },
131
132        InsertNode : function( node )
133        {
134                if ( this._Range )
135                        this._Range.insertNode( node ) ;
136        },
137
138        CheckIsEmpty : function( ignoreEndBRs )
139        {
140                if ( this.CheckIsCollapsed() )
141                        return true ;
142
143                // Inserts the contents of the range in a div tag.
144                var eToolDiv = this.Window.document.createElement( 'div' ) ;
145                this._Range.cloneContents().AppendTo( eToolDiv ) ;
146
147                FCKDomTools.TrimNode( eToolDiv, ignoreEndBRs ) ;
148
149                return ( eToolDiv.innerHTML.length == 0 ) ;
150        },
151
152        CheckStartOfBlock : function()
153        {
154                // Create a clone of the current range.
155                var oTestRange = this.Clone() ;
156
157                // Collapse it to its start point.
158                oTestRange.Collapse( true ) ;
159
160                // Move the start boundary to the start of the block.
161                oTestRange.SetStart( oTestRange.StartBlock || oTestRange.StartBlockLimit, 1 ) ;
162
163                var bIsStartOfBlock = oTestRange.CheckIsEmpty() ;
164
165                oTestRange.Release() ;
166
167                return bIsStartOfBlock ;
168        },
169
170        CheckEndOfBlock : function( refreshSelection )
171        {
172                // Create a clone of the current range.
173                var oTestRange = this.Clone() ;
174
175                // Collapse it to its end point.
176                oTestRange.Collapse( false ) ;
177
178                // Move the end boundary to the end of the block.
179                oTestRange.SetEnd( oTestRange.EndBlock || oTestRange.EndBlockLimit, 2 ) ;
180
181                var bIsEndOfBlock = oTestRange.CheckIsCollapsed() ;
182               
183                if ( !bIsEndOfBlock )
184                {
185                        // Inserts the contents of the range in a div tag.
186                        var eToolDiv = this.Window.document.createElement( 'div' ) ;
187                        oTestRange._Range.cloneContents().AppendTo( eToolDiv ) ;
188                        FCKDomTools.TrimNode( eToolDiv, true ) ;
189                       
190                        // Find out if we are in an empty tree of inline elements, like <b><i><span></span></i></b>
191                        bIsEndOfBlock = true ;
192                        var eLastChild = eToolDiv ;
193                        while ( ( eLastChild = eLastChild.lastChild ) )
194                        {
195                                // Check the following:
196                                //              1. Is there more than one node in the parents children?
197                                //              2. Is the node not an element node?
198                                //              3. Is it not a inline element.
199                                if ( eLastChild.previousSibling || eLastChild.nodeType != 1 || FCKListsLib.InlineChildReqElements[ eLastChild.nodeName.toLowerCase() ] == null )
200                                {
201                                        // So we are not in the end of the range.
202                                        bIsEndOfBlock = false ;
203                                        break ;
204                                }
205                        }
206                }
207               
208                oTestRange.Release() ;
209
210                if ( refreshSelection )
211                        this.Select() ;
212
213                return bIsEndOfBlock ;
214        },
215
216        CreateBookmark : function()
217        {
218                // Create the bookmark info (random IDs).
219                var oBookmark =
220                {
221                        StartId : 'fck_dom_range_start_' + (new Date()).valueOf() + '_' + Math.floor(Math.random()*1000),
222                        EndId   : 'fck_dom_range_end_' + (new Date()).valueOf() + '_' + Math.floor(Math.random()*1000)
223                } ;
224
225                var oDoc = this.Window.document ;
226                var eSpan ;
227                var oClone ;
228
229                // For collapsed ranges, add just the start marker.
230                if ( !this.CheckIsCollapsed() )
231                {
232                        eSpan = oDoc.createElement( 'span' ) ;
233                        eSpan.id = oBookmark.EndId ;
234                        eSpan.innerHTML = '&nbsp;' ;    // For IE, it must have something inside, otherwise it may be removed during operations.
235
236                        oClone = this.Clone() ;
237                        oClone.Collapse( false ) ;
238                        oClone.InsertNode( eSpan ) ;
239                }
240
241                eSpan = oDoc.createElement( 'span' ) ;
242                eSpan.id = oBookmark.StartId ;
243                eSpan.innerHTML = '&nbsp;' ;    // For IE, it must have something inside, otherwise it may be removed during operations.
244
245                oClone = this.Clone() ;
246                oClone.Collapse( true ) ;
247                oClone.InsertNode( eSpan ) ;
248
249                return oBookmark ;
250        },
251
252        MoveToBookmark : function( bookmark, preserveBookmark )
253        {
254                var oDoc = this.Window.document ;
255
256                var eStartSpan  =  oDoc.getElementById( bookmark.StartId ) ;
257                var eEndSpan    =  oDoc.getElementById( bookmark.EndId ) ;
258
259                this.SetStart( eStartSpan, 3 ) ;
260
261                if ( !preserveBookmark )
262                        FCKDomTools.RemoveNode( eStartSpan ) ;
263
264                // If collapsed, the start span will not be available.
265                if ( eEndSpan )
266                {
267                        this.SetEnd( eEndSpan, 3 ) ;
268
269                        if ( !preserveBookmark )
270                                FCKDomTools.RemoveNode( eEndSpan ) ;
271                }
272                else
273                        this.Collapse( true ) ;
274        },
275
276        /*
277         * Moves the position of the start boundary of the range to a specific position
278         * relatively to a element.
279         *              @position:
280         *                      1 = After Start         <target>^contents</target>
281         *                      2 = Before End          <target>contents^</target>
282         *                      3 = Before Start        ^<target>contents</target>
283         *                      4 = After End           <target>contents</target>^
284         */
285        SetStart : function( targetElement, position )
286        {
287                var oRange = this._Range ;
288                if ( !oRange )
289                        oRange = this._Range = this.CreateRange() ;
290
291                switch( position )
292                {
293                        case 1 :                // After Start          <target>^contents</target>
294                                oRange.setStart( targetElement, 0 ) ;
295                                break ;
296
297                        case 2 :                // Before End           <target>contents^</target>
298                                oRange.setStart( targetElement, targetElement.childNodes.length ) ;
299                                break ;
300
301                        case 3 :                // Before Start         ^<target>contents</target>
302                                oRange.setStartBefore( targetElement ) ;
303                                break ;
304
305                        case 4 :                // After End            <target>contents</target>^
306                                oRange.setStartAfter( targetElement ) ;
307                }
308                this._UpdateElementInfo() ;
309        },
310
311        /*
312         * Moves the position of the start boundary of the range to a specific position
313         * relatively to a element.
314         *              @position:
315         *                      1 = After Start         <target>^contents</target>
316         *                      2 = Before End          <target>contents^</target>
317         *                      3 = Before Start        ^<target>contents</target>
318         *                      4 = After End           <target>contents</target>^
319         */
320        SetEnd : function( targetElement, position )
321        {
322                var oRange = this._Range ;
323                if ( !oRange )
324                        oRange = this._Range = this.CreateRange() ;
325
326                switch( position )
327                {
328                        case 1 :                // After Start          <target>^contents</target>
329                                oRange.setEnd( targetElement, 0 ) ;
330                                break ;
331
332                        case 2 :                // Before End           <target>contents^</target>
333                                oRange.setEnd( targetElement, targetElement.childNodes.length ) ;
334                                break ;
335
336                        case 3 :                // Before Start         ^<target>contents</target>
337                                oRange.setEndBefore( targetElement ) ;
338                                break ;
339
340                        case 4 :                // After End            <target>contents</target>^
341                                oRange.setEndAfter( targetElement ) ;
342                }
343                this._UpdateElementInfo() ;
344        },
345
346        Expand : function( unit )
347        {
348                var oNode, oSibling ;
349
350                switch ( unit )
351                {
352                        case 'block_contents' :
353                                if ( this.StartBlock )
354                                        this.SetStart( this.StartBlock, 1 ) ;
355                                else
356                                {
357                                        // Get the start node for the current range.
358                                        oNode = this._Range.startContainer ;
359
360                                        // If it is an element, get the current child node for the range (in the offset).
361                                        // If the offset node is not available, the the first one.
362                                        if ( oNode.nodeType == 1 )
363                                        {
364                                                if ( !( oNode = oNode.childNodes[ this._Range.startOffset ] ) )
365                                                        oNode = oNode.firstChild ;
366                                        }
367
368                                        // Not able to defined the current position.
369                                        if ( !oNode )
370                                                return ;
371
372                                        // We must look for the left boundary, relative to the range
373                                        // start, which is limited by a block element.
374                                        while ( true )
375                                        {
376                                                oSibling = oNode.previousSibling ;
377
378                                                if ( !oSibling )
379                                                {
380                                                        // Continue if we are not yet in the block limit (inside a <b>, for example).
381                                                        if ( oNode.parentNode != this.StartBlockLimit )
382                                                                oNode = oNode.parentNode ;
383                                                        else
384                                                                break ;
385                                                }
386                                                else if ( oSibling.nodeType != 1 || !(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test( oSibling.nodeName.toUpperCase() ) )
387                                                {
388                                                        // Continue if the sibling is not a block tag.
389                                                        oNode = oSibling ;
390                                                }
391                                                else
392                                                        break ;
393                                        }
394
395                                        this._Range.setStartBefore( oNode ) ;
396                                }
397
398                                if ( this.EndBlock )
399                                        this.SetEnd( this.EndBlock, 2 ) ;
400                                else
401                                {
402                                        oNode = this._Range.endContainer ;
403                                        if ( oNode.nodeType == 1 )
404                                                oNode = oNode.childNodes[ this._Range.endOffset ] || oNode.lastChild ;
405
406                                        if ( !oNode )
407                                                return ;
408
409                                        // We must look for the right boundary, relative to the range
410                                        // end, which is limited by a block element.
411                                        while ( true )
412                                        {
413                                                oSibling = oNode.nextSibling ;
414
415                                                if ( !oSibling )
416                                                {
417                                                        // Continue if we are not yet in the block limit (inide a <b>, for example).
418                                                        if ( oNode.parentNode != this.EndBlockLimit )
419                                                                oNode = oNode.parentNode ;
420                                                        else
421                                                                break ;
422                                                }
423                                                else if ( oSibling.nodeType != 1 || !(/^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/).test( oSibling.nodeName.toUpperCase() ) )
424                                                {
425                                                        // Continue if the sibling is not a block tag.
426                                                        oNode = oSibling ;
427                                                }
428                                                else
429                                                        break ;
430                                        }
431
432                                        this._Range.setEndAfter( oNode ) ;
433                                }
434
435                                this._UpdateElementInfo() ;
436                }
437        },
438
439        Release : function( preserveWindow )
440        {
441                if ( !preserveWindow )
442                        this.Window = null ;
443
444                this.StartContainer = null ;
445                this.StartBlock = null ;
446                this.StartBlockLimit = null ;
447                this.EndContainer = null ;
448                this.EndBlock = null ;
449                this.EndBlockLimit = null ;
450                this._Range = null ;
451        }
452} ;
Note: See TracBrowser for help on using the repository browser.