Changeset 858


Ignore:
Timestamp:
09/21/07 15:56:59 (8 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 – 2015 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy