Changeset 858


Ignore:
Timestamp:
09/21/07 15:56:59 (7 years ago)
Author:
martinkou
Message:

Uploaded custom list command implementation based on the array list model.

Location:
FCKeditor/branches/features/custom_list_command
Files:
2 edited
2 copied

Legend:

Unmodified
Added
Removed
  • FCKeditor/branches/features/custom_list_command/editor/_source/commandclasses/fcklistcommands.js

    r771 r858  
    2222 */ 
    2323 
    24 var FCKListCommand = function( name ) 
     24// TODO: 
     25// 1. Handle unioning of lists and paragraphs. 
     26// 2. Creating separate lists inside table cells isn't working. 
     27 
     28var FCKListCommand = function( name, tagName ) 
    2529{ 
    2630        this.Name = name ; 
     31        this.TagName = tagName ; 
    2732} 
    2833 
     
    3136        GetState : function() 
    3237        { 
    33                 return FCK.GetNamedCommandState( this.Name ) ; 
     38                // Disabled if not WYSIWYG. 
     39                if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow ) 
     40                        return FCK_TRISTATE_DISABLED ; 
     41 
     42                // We'll use the style system's convention to determine list state here... 
     43                // If the starting block is a descendant of an <ol> or <ul> node, then we're in a list. 
     44                var startContainer = FCKSelection.GetBoundaryParentElement( true ) ; 
     45                var listNode = FCKTools.GetElementAscensor( startContainer, this.TagName ) ; 
     46                if ( listNode ) 
     47                        return FCK_TRISTATE_ON ; 
     48                else 
     49                        return FCK_TRISTATE_OFF ; 
    3450        }, 
    3551 
    3652        Execute : function() 
    3753        { 
    38                 if ( FCKBrowserInfo.IsIE && this.GetState() == FCK_TRISTATE_OFF ) 
    39                 { 
    40                         // IE does not split selected <br> tags when it is making lists. (See #428) 
    41                         // So, pre-split the blocks for IE. 
    42                         var range = new FCKDomRange( FCK.EditorWindow ) ; 
    43                         range.MoveToSelection() ; 
    44                         var startNode = range._Range.startContainer ; 
    45                         var endNode = range._Range.endContainer ; 
    46                         if ( startNode.nodeType == 1 ) 
    47                         { 
    48                                 if ( startNode.firstChild ) 
     54                FCKUndo.SaveUndoStep() ; 
     55 
     56                var range = new FCKDomRange( FCK.EditorWindow ) ; 
     57                range.MoveToSelection() ; 
     58                var bookmark = range.CreateBookmark() ; 
     59 
     60                // Group the blocks up because there are many cases where multiple lists have to be created, 
     61                // or multiple lists have to be cancelled. 
     62                var listGroups = [] ; 
     63                var markerObj = {} ; 
     64                var iterator = new FCKDomRangeIterator( range ) ; 
     65                var block ; 
     66                var state = this.GetState() ; 
     67                iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ; 
     68                while ( ( block = iterator.GetNextParagraph() ) ) 
     69                { 
     70                        var path = new FCKElementPath( block ) ; 
     71                        var listNode = null ; 
     72                        var processedFlag = false ; 
     73 
     74                        // First, try to group by a list ancestor. 
     75                        for ( var i = path.Elements.length - 1 ; i >= 0 ; i-- ) 
     76                        { 
     77                                var el = path.Elements[i] ; 
     78                                if ( el.nodeName.IEquals( ['ol', 'ul'] ) ) 
    4979                                { 
    50                                         if ( startNode.childNodes.length <= range._Range.startOffset ) 
    51                                                 startNode = startNode.lastChild ; 
     80                                        var groupObj = el._FCK_ListGroupObject ; 
     81                                        if ( groupObj ) 
     82                                                groupObj.contents.push( block ) ; 
    5283                                        else 
    53                                                 startNode = startNode.childNodes[ range._Range.startOffset ] ; 
     84                                        { 
     85                                                groupObj = { 'root' : el, 'contents' : [ block ] } ; 
     86                                                listGroups.push( groupObj ) ; 
     87                                                FCKDomTools.SetElementMarker( markerObj, el, '_FCK_ListGroupObject', groupObj ) ; 
     88                                        } 
     89                                        processedFlag = true ; 
     90                                        break ; 
    5491                                } 
    5592                        } 
    56                         if ( endNode.nodeType == 1 ) 
    57                         { 
    58                                 if ( endNode.firstChild ) 
     93 
     94                        if ( processedFlag ) 
     95                                continue ; 
     96 
     97                        // No list ancestor? Group by block limit. 
     98                        var root = path.BlockLimit ; 
     99                        if ( root._FCK_ListGroupObject ) 
     100                                root._FCK_ListGroupObject.contents.push( block ) ; 
     101                        else 
     102                        { 
     103                                var groupObj = { 'root' : root, 'contents' : [ block ] } ; 
     104                                FCKDomTools.SetElementMarker( markerObj, root, '_FCK_ListGroupObject', groupObj ) ; 
     105                                listGroups.push( groupObj ) ; 
     106                        } 
     107                } 
     108 
     109                // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element. 
     110                // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking 
     111                // at the group that's not rooted at lists. So we have three cases to handle. 
     112                while ( listGroups.length > 0 ) 
     113                { 
     114                        var groupObj = listGroups.shift() ; 
     115                        if ( state == FCK_TRISTATE_OFF ) 
     116                        { 
     117                                if ( groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) ) 
     118                                        this._ChangeListType( groupObj, markerObj ) ; 
     119                                else 
     120                                        this._CreateList( groupObj ) ; 
     121                        } 
     122                        else if ( state == FCK_TRISTATE_ON && groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) ) 
     123                                this._RemoveList( groupObj, markerObj ) ; 
     124                } 
     125 
     126                // Clean up, restore selection and update toolbar button states. 
     127                FCKDomTools.ClearElementMarkers( markerObj ) ; 
     128                range.MoveToBookmark( bookmark ) ; 
     129                range.Select() ; 
     130                FCK.Events.FireEvent( 'OnSelectionChange' ) ; 
     131        }, 
     132 
     133        _ChangeListType : function( groupObj, markerObj ) 
     134        { 
     135                // This case is easy... 
     136                // 1. Convert the whole list into a one-dimensional array. 
     137                // 2. Change the list type by modifying the array. 
     138                // 3. Recreate the whole list by converting the array to a list. 
     139                // 4. Replace the original list with the recreated list. 
     140                var listArray = FCKDomTools.ListToArray( groupObj.root, null, null, markerObj ) ; 
     141                var selectedListItems = [] ; 
     142                for ( var i = 0 ; i < groupObj.contents.length ; i++ ) 
     143                { 
     144                        var itemNode = groupObj.contents[i] ; 
     145                        itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ; 
     146                        if ( ! itemNode || itemNode._FCK_ListItem_Processed ) 
     147                                continue ; 
     148                        selectedListItems.push( itemNode ) ; 
     149                        FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ; 
     150                } 
     151                var fakeParent = groupObj.root.ownerDocument.createElement( this.TagName ) ; 
     152                for ( var i = 0 ; i < selectedListItems.length ; i++ ) 
     153                { 
     154                        var listIndex = selectedListItems[i]._FCK_ListArray_Index ; 
     155                        listArray[listIndex].parent = fakeParent ; 
     156                } 
     157                var newList = FCKDomTools.ArrayToList( listArray ) ; 
     158                groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ; 
     159        }, 
     160 
     161        _CreateList : function( groupObj ) 
     162        { 
     163                var contents = groupObj.contents ; 
     164                var commonParent = groupObj.contents[0].parentNode ; 
     165                var listContents = [] ; 
     166                for ( var i = 0 ; i < contents.length ; i++ ) 
     167                        commonParent = FCKDomTools.GetCommonParents( commonParent, contents[i].parentNode ).pop() ; 
     168                for ( var i = 0 ; i < contents.length ; i++ ) 
     169                { 
     170                        var contentNode = contents[i] ; 
     171                        while ( contentNode.parentNode ) 
     172                        {  
     173                                if ( contentNode.parentNode == commonParent ) 
    59174                                { 
    60                                         if ( endNode.childNodes.length <= range._Range.endOffset ) 
    61                                                 endNode = endNode.lastChild ; 
    62                                         else 
    63                                                 endNode = endNode.childNodes[ range._Range.endOffset ] ; 
     175                                        listContents.push( contentNode ) ; 
     176                                        break ; 
    64177                                } 
    65                         } 
    66  
    67                         var brNodes = [] ; 
    68                         var curNode = startNode ; 
    69                         while ( curNode && curNode != endNode ) 
    70                         { 
    71                                 if ( curNode.nodeType == 1 && curNode.tagName.toLowerCase() == 'br' ) 
    72                                         brNodes.push( curNode ) ; 
    73                                 curNode = FCKTools.GetNextNode( curNode ) ; 
    74                         } 
    75  
    76                         for ( var i = brNodes.length - 1 ; i >= 0 ; i-- ) 
    77                         { 
    78                                 range.SetStart( brNodes[i], 3 ) ; 
    79                                 range.SetEnd( brNodes[i], 3 ) ; 
    80                                 brNodes[i].parentNode.removeChild( brNodes[i] ) ; 
    81                                 range.SplitBlock() ; 
    82                         } 
    83  
    84                         range.SetStart( startNode, 1 ) ; 
    85                         range.SetEnd( endNode, 4 ) ; 
    86                         range.Select() ; 
    87                 } 
    88                 FCK.ExecuteNamedCommand( this.Name ) ; 
     178                                contentNode = contentNode.parentNode ; 
     179                        } 
     180                } 
     181 
     182                if ( listContents.length < 1 ) 
     183                        return ; 
     184 
     185                var insertAnchor = listContents[listContents.length - 1].nextSibling ; 
     186                var doc = commonParent.ownerDocument ; 
     187                var listNode = doc.createElement( this.TagName ) ; 
     188                while ( listContents.length ) 
     189                { 
     190                        var contentBlock = listContents.shift() ; 
     191                        var docFrag = doc.createDocumentFragment() ; 
     192                        while ( contentBlock.firstChild ) 
     193                                docFrag.appendChild( contentBlock.removeChild( contentBlock.firstChild ) ) ; 
     194                        contentBlock.parentNode.removeChild( contentBlock ) ; 
     195                        var listItem = doc.createElement( 'li' ) ;  
     196                        listItem.appendChild( docFrag ) ; 
     197                        listNode.appendChild( listItem ) ; 
     198                } 
     199                commonParent.insertBefore( listNode, insertAnchor ) ; 
     200        }, 
     201 
     202        _RemoveList : function( groupObj, markerObj ) 
     203        { 
     204                // This is very much like the change list type operation. 
     205                // Except that we're changing the selected items' indent to -1 in the list array. 
     206                var listArray = FCKDomTools.ListToArray( groupObj.root, null, null, markerObj ) ; 
     207                var selectedListItems = [] ; 
     208                for ( var i = 0 ; i < groupObj.contents.length ; i++ ) 
     209                { 
     210                        var itemNode = groupObj.contents[i] ; 
     211                        itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ; 
     212                        if ( ! itemNode || itemNode._FCK_ListItem_Processed ) 
     213                                continue ; 
     214                        selectedListItems.push( itemNode ) ; 
     215                        FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ; 
     216                } 
     217                 
     218                var lastListIndex = null ; 
     219                for ( var i = 0 ; i < selectedListItems.length ; i++ ) 
     220                { 
     221                        var listIndex = selectedListItems[i]._FCK_ListArray_Index ; 
     222                        listArray[listIndex].indent = -1 ; 
     223                        lastListIndex = listIndex ; 
     224                } 
     225 
     226                // After cutting parts of the list out with indent=-1, we still have to maintain the array list 
     227                // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the 
     228                // list cannot be converted back to a real DOM list. 
     229                for ( var i = lastListIndex + 1; i < listArray.length ; i++ ) 
     230                { 
     231                        if ( listArray[i].indent > listArray[i-1].indent + 1 ) 
     232                        { 
     233                                var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent ; 
     234                                var oldIndent = listArray[i].indent ; 
     235                                while ( listArray[i] && listArray[i].indent >= oldIndent) 
     236                                { 
     237                                        listArray[i].indent += indentOffset ; 
     238                                        i++ ; 
     239                                } 
     240                                i-- ; 
     241                        } 
     242                } 
     243 
     244                var newList = FCKDomTools.ArrayToList( listArray ) ; 
     245                groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ; 
    89246        } 
    90247}; 
  • FCKeditor/branches/features/custom_list_command/editor/_source/internals/fckcommands.js

    r850 r858  
    131131 
    132132                case 'SelectAll'                        : oCommand = new FCKSelectAllCommand() ; break ; 
    133                 case 'InsertOrderedList'        : oCommand = new FCKListCommand( 'insertorderedlist' ) ; break ; 
    134                 case 'InsertUnorderedList'      : oCommand = new FCKListCommand( 'insertunorderedlist' ) ; break ; 
     133                case 'InsertOrderedList'        : oCommand = new FCKListCommand( 'insertorderedlist', 'ol' ) ; break ; 
     134                case 'InsertUnorderedList'      : oCommand = new FCKListCommand( 'insertunorderedlist', 'ul' ) ; break ; 
    135135                case 'ShowBlocks' : oCommand = new FCKShowBlockCommand( 'ShowBlocks', FCKConfig.StartupShowBlocks ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF ) ; break ; 
    136136 
Note: See TracChangeset for help on using the changeset viewer.
© 2003 – 2012 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy