source: sandbox/filemanager/tp/fckeditor/editor/_source/commandclasses/fcklistcommands.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 * Implementation for the "Insert/Remove Ordered/Unordered List" commands.
22 */
23
24var FCKListCommand = function( name, tagName )
25{
26        this.Name = name ;
27        this.TagName = tagName ;
28}
29
30FCKListCommand.prototype =
31{
32        GetState : function()
33        {
34                // Disabled if not WYSIWYG.
35                if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
36                        return FCK_TRISTATE_DISABLED ;
37
38                // We'll use the style system's convention to determine list state here...
39                // If the starting block is a descendant of an <ol> or <ul> node, then we're in a list.
40                var startContainer = FCKSelection.GetBoundaryParentElement( true ) ;
41                var listNode = startContainer ;
42                while ( listNode )
43                {
44                        if ( listNode.nodeName.IEquals( [ 'ul', 'ol' ] ) )
45                                break ;
46                        listNode = listNode.parentNode ;
47                }
48                if ( listNode && listNode.nodeName.IEquals( this.TagName ) )
49                        return FCK_TRISTATE_ON ;
50                else
51                        return FCK_TRISTATE_OFF ;
52        },
53
54        Execute : function()
55        {
56                FCKUndo.SaveUndoStep() ;
57
58                var doc = FCK.EditorDocument ;
59                var range = new FCKDomRange( FCK.EditorWindow ) ;
60                range.MoveToSelection() ;
61                var state = this.GetState() ;
62
63                // Midas lists rule #1 says we can create a list even in an empty document.
64                // But FCKDomRangeIterator wouldn't run if the document is really empty.
65                // So create a paragraph if the document is empty and we're going to create a list.
66                if ( state == FCK_TRISTATE_OFF )
67                {
68                        FCKDomTools.TrimNode( doc.body ) ;
69                        if ( ! doc.body.firstChild )
70                        {
71                                var paragraph = doc.createElement( 'p' ) ;
72                                doc.body.appendChild( paragraph ) ;
73                                range.MoveToNodeContents( paragraph ) ;
74                        }
75                }
76
77                var bookmark = range.CreateBookmark() ;
78
79                // Group the blocks up because there are many cases where multiple lists have to be created,
80                // or multiple lists have to be cancelled.
81                var listGroups = [] ;
82                var markerObj = {} ;
83                var iterator = new FCKDomRangeIterator( range ) ;
84                var block ;
85
86                iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
87                var nextRangeExists = true ;
88                var rangeQueue = null ;
89                while ( nextRangeExists )
90                {
91                        while ( ( block = iterator.GetNextParagraph() ) )
92                        {
93                                var path = new FCKElementPath( block ) ;
94                                var listNode = null ;
95                                var processedFlag = false ;
96                                var blockLimit = path.BlockLimit ;
97
98                                // First, try to group by a list ancestor.
99                                for ( var i = path.Elements.length - 1 ; i >= 0 ; i-- )
100                                {
101                                        var el = path.Elements[i] ;
102                                        if ( el.nodeName.IEquals( ['ol', 'ul'] ) )
103                                        {
104                                                // If we've encountered a list inside a block limit
105                                                // The last group object of the block limit element should
106                                                // no longer be valid. Since paragraphs after the list
107                                                // should belong to a different group of paragraphs before
108                                                // the list. (Bug #1309)
109                                                if ( blockLimit._FCK_ListGroupObject )
110                                                        blockLimit._FCK_ListGroupObject = null ;
111
112                                                var groupObj = el._FCK_ListGroupObject ;
113                                                if ( groupObj )
114                                                        groupObj.contents.push( block ) ;
115                                                else
116                                                {
117                                                        groupObj = { 'root' : el, 'contents' : [ block ] } ;
118                                                        listGroups.push( groupObj ) ;
119                                                        FCKDomTools.SetElementMarker( markerObj, el, '_FCK_ListGroupObject', groupObj ) ;
120                                                }
121                                                processedFlag = true ;
122                                                break ;
123                                        }
124                                }
125
126                                if ( processedFlag )
127                                        continue ;
128
129                                // No list ancestor? Group by block limit.
130                                var root = blockLimit ;
131                                if ( root._FCK_ListGroupObject )
132                                        root._FCK_ListGroupObject.contents.push( block ) ;
133                                else
134                                {
135                                        var groupObj = { 'root' : root, 'contents' : [ block ] } ;
136                                        FCKDomTools.SetElementMarker( markerObj, root, '_FCK_ListGroupObject', groupObj ) ;
137                                        listGroups.push( groupObj ) ;
138                                }
139                        }
140
141                        if ( FCKBrowserInfo.IsIE )
142                                nextRangeExists = false ;
143                        else
144                        {
145                                if ( rangeQueue == null )
146                                {
147                                        rangeQueue = [] ;
148                                        var selectionObject = FCKSelection.GetSelection() ;
149                                        if ( selectionObject && listGroups.length == 0 )
150                                                rangeQueue.push( selectionObject.getRangeAt( 0 ) ) ;
151                                        for ( var i = 1 ; selectionObject && i < selectionObject.rangeCount ; i++ )
152                                                rangeQueue.push( selectionObject.getRangeAt( i ) ) ;
153                                }
154                                if ( rangeQueue.length < 1 )
155                                        nextRangeExists = false ;
156                                else
157                                {
158                                        var internalRange = FCKW3CRange.CreateFromRange( doc, rangeQueue.shift() ) ;
159                                        range._Range = internalRange ;
160                                        range._UpdateElementInfo() ;
161                                        if ( range.StartNode.nodeName.IEquals( 'td' ) )
162                                                range.SetStart( range.StartNode, 1 ) ;
163                                        if ( range.EndNode.nodeName.IEquals( 'td' ) )
164                                                range.SetEnd( range.EndNode, 2 ) ;
165                                        iterator = new FCKDomRangeIterator( range ) ;
166                                        iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
167                                }
168                        }
169                }
170
171                // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element.
172                // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking
173                // at the group that's not rooted at lists. So we have three cases to handle.
174                var listsCreated = [] ;
175                while ( listGroups.length > 0 )
176                {
177                        var groupObj = listGroups.shift() ;
178                        if ( state == FCK_TRISTATE_OFF )
179                        {
180                                if ( groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
181                                        this._ChangeListType( groupObj, markerObj, listsCreated ) ;
182                                else
183                                        this._CreateList( groupObj, listsCreated ) ;
184                        }
185                        else if ( state == FCK_TRISTATE_ON && groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
186                                this._RemoveList( groupObj, markerObj ) ;
187                }
188
189                // For all new lists created, merge adjacent, same type lists.
190                for ( var i = 0 ; i < listsCreated.length ; i++ )
191                {
192                        var listNode = listsCreated[i] ;
193                        var stopFlag = false ;
194                        var currentNode = listNode ;
195                        while ( ! stopFlag )
196                        {
197                                currentNode = currentNode.nextSibling ;
198                                if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[\n\r\t ]*$/ ) == 0 )
199                                        continue ;
200                                stopFlag = true ;
201                        }
202
203                        if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
204                        {
205                                currentNode.parentNode.removeChild( currentNode ) ;
206                                while ( currentNode.firstChild )
207                                        listNode.appendChild( currentNode.removeChild( currentNode.firstChild ) ) ;
208                        }
209
210                        stopFlag = false ;
211                        currentNode = listNode ;
212                        while ( ! stopFlag )
213                        {
214                                currentNode = currentNode.previousSibling ;
215                                if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[\n\r\t ]*$/ ) == 0 )
216                                        continue ;
217                                stopFlag = true ;
218                        }
219                        if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
220                        {
221                                currentNode.parentNode.removeChild( currentNode ) ;
222                                while ( currentNode.lastChild )
223                                        listNode.insertBefore( currentNode.removeChild( currentNode.lastChild ),
224                                                       listNode.firstChild ) ;
225                        }
226                }
227
228                // Clean up, restore selection and update toolbar button states.
229                FCKDomTools.ClearAllMarkers( markerObj ) ;
230                range.MoveToBookmark( bookmark ) ;
231                range.Select() ;
232
233                FCK.Focus() ;
234                FCK.Events.FireEvent( 'OnSelectionChange' ) ;
235        },
236
237        _ChangeListType : function( groupObj, markerObj, listsCreated )
238        {
239                // This case is easy...
240                // 1. Convert the whole list into a one-dimensional array.
241                // 2. Change the list type by modifying the array.
242                // 3. Recreate the whole list by converting the array to a list.
243                // 4. Replace the original list with the recreated list.
244                var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
245                var selectedListItems = [] ;
246                for ( var i = 0 ; i < groupObj.contents.length ; i++ )
247                {
248                        var itemNode = groupObj.contents[i] ;
249                        itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
250                        if ( ! itemNode || itemNode._FCK_ListItem_Processed )
251                                continue ;
252                        selectedListItems.push( itemNode ) ;
253                        FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
254                }
255                var fakeParent = FCKTools.GetElementDocument( groupObj.root ).createElement( this.TagName ) ;
256                for ( var i = 0 ; i < selectedListItems.length ; i++ )
257                {
258                        var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
259                        listArray[listIndex].parent = fakeParent ;
260                }
261                var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
262                for ( var i = 0 ; i < newList.listNode.childNodes.length ; i++ )
263                {
264                        if ( newList.listNode.childNodes[i].nodeName.IEquals( this.TagName ) )
265                                listsCreated.push( newList.listNode.childNodes[i] ) ;
266                }
267                groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
268        },
269
270        _CreateList : function( groupObj, listsCreated )
271        {
272                var contents = groupObj.contents ;
273                var doc = FCKTools.GetElementDocument( groupObj.root ) ;
274                var listContents = [] ;
275
276                // It is possible to have the contents returned by DomRangeIterator to be the same as the root.
277                // e.g. when we're running into table cells.
278                // In such a case, enclose the childNodes of contents[0] into a <div>.
279                if ( contents.length == 1 && contents[0] == groupObj.root )
280                {
281                        var divBlock = doc.createElement( 'div' );
282                        while ( contents[0].firstChild )
283                                divBlock.appendChild( contents[0].removeChild( contents[0].firstChild ) ) ;
284                        contents[0].appendChild( divBlock ) ;
285                        contents[0] = divBlock ;
286                }
287
288                // Calculate the common parent node of all content blocks.
289                var commonParent = groupObj.contents[0].parentNode ;
290                for ( var i = 0 ; i < contents.length ; i++ )
291                        commonParent = FCKDomTools.GetCommonParents( commonParent, contents[i].parentNode ).pop() ;
292
293                // We want to insert things that are in the same tree level only, so calculate the contents again
294                // by expanding the selected blocks to the same tree level.
295                for ( var i = 0 ; i < contents.length ; i++ )
296                {
297                        var contentNode = contents[i] ;
298                        while ( contentNode.parentNode )
299                        {
300                                if ( contentNode.parentNode == commonParent )
301                                {
302                                        listContents.push( contentNode ) ;
303                                        break ;
304                                }
305                                contentNode = contentNode.parentNode ;
306                        }
307                }
308
309                if ( listContents.length < 1 )
310                        return ;
311
312                // Insert the list to the DOM tree.
313                var insertAnchor = listContents[listContents.length - 1].nextSibling ;
314                var listNode = doc.createElement( this.TagName ) ;
315                listsCreated.push( listNode ) ;
316                while ( listContents.length )
317                {
318                        var contentBlock = listContents.shift() ;
319                        var docFrag = doc.createDocumentFragment() ;
320                        while ( contentBlock.firstChild )
321                                docFrag.appendChild( contentBlock.removeChild( contentBlock.firstChild ) ) ;
322                        contentBlock.parentNode.removeChild( contentBlock ) ;
323                        var listItem = doc.createElement( 'li' ) ;
324                        listItem.appendChild( docFrag ) ;
325                        listNode.appendChild( listItem ) ;
326                }
327                commonParent.insertBefore( listNode, insertAnchor ) ;
328        },
329
330        _RemoveList : function( groupObj, markerObj )
331        {
332                // This is very much like the change list type operation.
333                // Except that we're changing the selected items' indent to -1 in the list array.
334                var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
335                var selectedListItems = [] ;
336                for ( var i = 0 ; i < groupObj.contents.length ; i++ )
337                {
338                        var itemNode = groupObj.contents[i] ;
339                        itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
340                        if ( ! itemNode || itemNode._FCK_ListItem_Processed )
341                                continue ;
342                        selectedListItems.push( itemNode ) ;
343                        FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
344                }
345
346                var lastListIndex = null ;
347                for ( var i = 0 ; i < selectedListItems.length ; i++ )
348                {
349                        var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
350                        listArray[listIndex].indent = -1 ;
351                        lastListIndex = listIndex ;
352                }
353
354                // After cutting parts of the list out with indent=-1, we still have to maintain the array list
355                // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the
356                // list cannot be converted back to a real DOM list.
357                for ( var i = lastListIndex + 1; i < listArray.length ; i++ )
358                {
359                        if ( listArray[i].indent > listArray[i-1].indent + 1 )
360                        {
361                                var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent ;
362                                var oldIndent = listArray[i].indent ;
363                                while ( listArray[i] && listArray[i].indent >= oldIndent)
364                                {
365                                        listArray[i].indent += indentOffset ;
366                                        i++ ;
367                                }
368                                i-- ;
369                        }
370                }
371
372                var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
373                // If groupObj.root is the last element in its parent, or its nextSibling is a <br>, then we should
374                // not add a <br> after the final item. So, check for the cases and trim the <br>.
375                if ( groupObj.root.nextSibling == null || groupObj.root.nextSibling.nodeName.IEquals( 'br' ) )
376                {
377                        if ( newList.listNode.lastChild.nodeName.IEquals( 'br' ) )
378                                newList.listNode.removeChild( newList.listNode.lastChild ) ;
379                }
380                groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
381        }
382};
Note: See TracBrowser for help on using the repository browser.