Changeset 846


Ignore:
Timestamp:
09/19/2007 04:50:29 PM (8 years ago)
Author:
martinkou
Message:

Added array modeling logic for HTML lists in fckdomtools.js.
Fixed #1231 : Indenting and outdenting of normal blocks are now done by CSS attributes instead of by <blockquote>,
Fixed #1232 : Indenting and outdenting of list items are now XHTML 1.1 compliant.

Location:
FCKeditor/trunk/editor/_source
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • FCKeditor/trunk/editor/_source/commandclasses/fckindentcommands.js

    r837 r846  
    8585                var listNode = FCKDomTools.GetCommonParentNode( startContainer, endContainer, ['ul','ol'] ) ;
    8686
    87                 if ( ( ! FCKIndentCommand._UseIndentClasses && this.Name.IEquals( 'indent' ) ) || listNode )
     87                if ( listNode )
     88                {
     89                        if ( this.Name.IEquals( 'outdent' ) )
     90                                return FCK_TRISTATE_OFF ;
     91                        var firstItem = FCKTools.GetElementAscensor( startContainer, 'li' ) ;
     92                        if ( !firstItem || !firstItem.previousSibling )
     93                                return FCK_TRISTATE_DISABLED ;
     94                        return FCK_TRISTATE_OFF ;
     95                }
     96                if ( ! FCKIndentCommand._UseIndentClasses && this.Name.IEquals( 'indent' ) )
    8897                        return FCK_TRISTATE_OFF;
    8998
     
    166175                                currentOffset += this.Offset ;
    167176                                currentOffset = Math.max( currentOffset, 0 ) ;
    168                                 block.style[this.IndentCSSProperty] = currentOffset + 'px' ;
     177                                currentOffset = Math.ceil( currentOffset / this.Offset ) * this.Offset ;
     178                                block.style[this.IndentCSSProperty] = currentOffset ? currentOffset + 'px' : '' ;
     179                                if ( block.getAttribute( 'style' ) == '' )
     180                                        block.removeAttribute( 'style' ) ;
    169181                        }
    170182                }
     
    177189                var startContainer = range.StartContainer ;
    178190                var endContainer = range.EndContainer ;
    179                 while ( startContainer && ! startContainer.nodeName.IEquals( ['li', 'ul', 'ol'] ) )
     191                while ( startContainer && startContainer.parentNode != listNode )
    180192                        startContainer = startContainer.parentNode ;
    181                 while ( endContainer && ! endContainer.nodeName.IEquals( ['li', 'ul', 'ol'] ) )
     193                while ( endContainer && endContainer.parentNode != listNode )
    182194                        endContainer = endContainer.parentNode ;
    183195
     
    185197                        return ;
    186198
    187                 range.SetStart( startContainer, 1 ) ;
    188                 range.SetEnd( endContainer, 2 ) ;
    189 
    190                 // Now we can iterate over the individual items.
    191                 var iterator = new FCKDomRangeIterator( range ) ;
    192                 var block ;
     199                // Now we can iterate over the individual items on the same tree depth.
     200                var block = startContainer ;
    193201                var itemsToMove = [] ;
    194                 while ( ( block = iterator.GetNextParagraph() ) )
    195                 {
    196                         if ( block.parentNode == listNode )
    197                                 itemsToMove.push( block ) ;
     202                var stopFlag = false ;
     203                while ( stopFlag == false )
     204                {
     205                        if ( block == endContainer )
     206                                stopFlag = true ;
     207                        itemsToMove.push( block ) ;
     208                        block = block.nextSibling ;
    198209                }
    199210                if ( itemsToMove.length < 1 )
    200211                        return ;
     212
     213                // Do indent or outdent operations on the array model of the list, not the list's DOM tree itself.
     214                // The array model demands that it knows as much as possible about the surrounding lists, we need
     215                // to feed it the further ancestor node that is still a list.
     216                var listParents = FCKDomTools.GetParents( listNode ) ;
     217                for ( var i = 0 ; i < listParents.length ; i++ )
     218                {
     219                        if ( listParents[i].nodeName.IEquals( ['ul', 'ol'] ) )
     220                        {
     221                                listNode = listParents[i] ;
     222                                break ;
     223                        }
     224                }
     225                var indentOffset = this.Name.IEquals( 'indent' ) ? 1 : -1 ;
     226                var startItem = itemsToMove[0] ;
    201227                var lastItem = itemsToMove[ itemsToMove.length - 1 ] ;
    202 
    203                 if ( this.Name.IEquals( 'indent' ) )
    204                 {
    205                         // Indent is easy... just clone the list node, add under the original, and
    206                         // move the contents inside the clone.
    207                         var indentList = listNode.cloneNode( false ) ;
    208                         FCKDomTools.InsertAfterNode( lastItem, indentList ) ;
    209                         while ( itemsToMove.length > 0 )
    210                                 indentList.appendChild( itemsToMove.shift() ) ;
    211                 }
    212                 else if ( this.Name.IEquals( 'outdent' ) )
    213                 {
    214                         var startItem = itemsToMove[0] ;
    215 
    216                         // If we're in the middle of the list, we will have to split the list into two.
    217                         if ( startItem.previousSibling && lastItem.nextSibling )
    218                         {
    219                                 range.SetStart( startItem, 3 ) ;
    220                                 range.SetEnd( startItem, 3 ) ;
    221                                 listNode = range.SplitBlock().NextBlock ;
    222                         }
    223 
    224                         // Remove the list items.
    225                         for ( var i = 0 ; i < itemsToMove.length ; i++ )
    226                                 itemsToMove[i].parentNode.removeChild( itemsToMove[i] )  ;
    227 
    228                         // If the parent of listNode is not another list, then we need to convert the
    229                         // listItems into paragraphs, depending on EnterMode.
    230                         if ( ! listNode.parentNode.nodeName.IEquals( ['ul', 'ol'] ) )
    231                         {
    232                                 for ( var i = 0 ; i < itemsToMove.length ; i++ )
     228                var markerObj = {} ;
     229
     230                // Convert the list DOM tree into a one dimensional array.
     231                var listArray = FCKDomTools.ListToArray( listNode, null, null, markerObj ) ;
     232
     233                // Apply indenting or outdenting on the array.
     234                var baseIndent = listArray[lastItem._FCK_ListArray_Index].indent ;
     235                for ( var i = startItem._FCK_ListArray_Index ; i <= lastItem._FCK_ListArray_Index ; i++ )
     236                        listArray[i].indent += indentOffset ;
     237                for ( var i = lastItem._FCK_ListArray_Index + 1 ; i < listArray.length && listArray[i].indent > baseIndent ; i++ )
     238                        listArray[i].indent += indentOffset ;
     239
     240                /* For debug use only
     241                var PrintArray = function( listArray, doc )
     242                {
     243                        var s = [] ;
     244                        for ( var i = 0 ; i < listArray.length ; i++ )
     245                        {
     246                                for ( var j in listArray[i] )
    233247                                {
    234                                         var container ;
    235                                         if ( FCKConfig.EnterMode.IEquals( ['p', 'div'] ) )
    236                                                 container = listNode.ownerDocument.createElement( FCKConfig.EnterMode ) ;
     248                                        if ( j != 'contents' )
     249                                                s.push( j + ":" + listArray[i][j] + "; " ) ;
    237250                                        else
    238251                                        {
    239                                                 container = listNode.ownerDocument.createDocumentFragment() ;
    240                                                 container.appendChild( listNode.ownerDocument.createElement( 'br' ) ) ;
     252                                                var docFrag = doc.createDocumentFragment() ;
     253                                                var tmpNode = doc.createElement( 'span' ) ;
     254                                                for ( var k = 0 ; k < listArray[i][j].length ; k++ )
     255                                                        docFrag.appendChild( listArray[i][j][k].cloneNode( true ) ) ;
     256                                                tmpNode.appendChild( docFrag ) ;
     257                                                s.push( j + ":" + tmpNode.innerHTML + "; ") ;
    241258                                        }
    242 
    243                                         var listItem = itemsToMove[i] ;
    244                                         while ( listItem.lastChild )
    245                                         {
    246                                                 var node = listItem.removeChild( listItem.lastChild ) ;
    247                                                 container.insertBefore( node, container.firstChild ) ;
    248                                         }
    249                                         itemsToMove[i] = container ;
    250259                                }
    251                         }
    252 
    253                         // Insert the content nodes to listNode's parent.
    254                         while ( itemsToMove.length > 0 )
    255                                 listNode.parentNode.insertBefore( itemsToMove.shift(), listNode ) ;
    256 
    257                         // Cleanup: if the listNode becomes empty, remove it.
    258                         if ( ! listNode.lastChild )
    259                                 listNode.parentNode.removeChild( listNode ) ;
    260                 }
     260                                s.push( '\n' ) ;
     261                        }
     262                        alert( s.join('') ) ;
     263                }
     264                PrintArray( listArray, FCK.EditorDocument ) ;
     265                */
     266
     267                // Convert the array back to a DOM forest (yes we might have a few subtrees now).
     268                // And replace the old list with the new forest.
     269                var newList = FCKDomTools.ArrayToList( listArray ) ;
     270                listNode.parentNode.replaceChild( newList.listNode, listNode ) ;
     271
     272                // Clean up the markers.
     273                FCKDomTools.ClearElementMarkers( markerObj ) ;
    261274        }
    262275} ;
  • FCKeditor/trunk/editor/_source/internals/fckdomtools.js

    r824 r846  
    575575               
    576576                return element ;
     577        },
     578
     579        ClearElementJSProperty : function( element, attrName )
     580        {
     581                if ( FCKBrowserInfo.IsIE )
     582                        element.removeAttribute( attrName ) ;
     583                else
     584                        delete element[attrName] ;
     585        },
     586
     587        SetElementMarker : function ( markObj, element, attrName, value)
     588        {
     589                var id = String( parseInt( Math.random() * 0xfffffff, 10 ) ) ;
     590                element._FCKMarkerId = id ;
     591                element[attrName] = value ;
     592                if ( ! markObj[id] )
     593                        markObj[id] = { 'element' : element, 'markers' : {} } ;
     594                markObj[id]['markers'][attrName] = value ;
     595        },
     596
     597        ClearElementMarkers : function( markObj )
     598        {
     599                for ( var i in markObj )
     600                {
     601                        var element = markObj[i]['element'] ;
     602                        this.ClearElementJSProperty( element, '_FCKMarkerId' ) ;
     603                        for ( var j in markObj[i]['markers'] )
     604                                this.ClearElementJSProperty( element, j ) ;
     605                        delete markObj[i] ;
     606                }
     607        },
     608
     609        // Convert a DOM list tree into a data structure that is easier to manipulate.
     610        // This operation should be non-intrusive in the sense that it does not change the DOM tree,
     611        // with the exception that it may add some markers to the list item nodes when markerObj is specified.
     612        ListToArray : function( listNode, baseArray, baseIndentLevel, markerObj )
     613        {
     614                if ( ! listNode.nodeName.IEquals( ['ul', 'ol'] ) )
     615                        return [] ;
     616
     617                if ( ! baseIndentLevel )
     618                        baseIndentLevel = 0 ;
     619                if ( ! baseArray )
     620                        baseArray = [] ;
     621                // Iterate over all list items to get their contents and look for inner lists.
     622                for ( var i = 0 ; i < listNode.childNodes.length ; i++ )
     623                {
     624                        var listItem = listNode.childNodes[i] ;
     625                        if ( ! listItem.nodeName.IEquals( 'li' ) )
     626                                continue ;
     627                        var itemObj = { 'grandparent' : listNode.parentNode, 'parent' : listNode, 'indent' : baseIndentLevel, 'contents' : [] } ;
     628                        if ( itemObj.grandparent && itemObj.grandparent.nodeName.IEquals( 'li' ) )
     629                                itemObj.grandparent = itemObj.grandparent.parentNode ;
     630                        if ( markerObj )
     631                                this.SetElementMarker( markerObj, listItem, '_FCK_ListArray_Index', baseArray.length ) ;
     632                        baseArray.push( itemObj ) ;
     633                        for ( var j = 0 ; j < listItem.childNodes.length ; j++ )
     634                        {
     635                                var child = listItem.childNodes[j] ;
     636                                if ( child.nodeName.IEquals( ['ul', 'ol'] ) )
     637                                        // Note the recursion here, it pushes inner list items with +1 indentation in the correct
     638                                        // order.
     639                                        this.ListToArray( child, baseArray, baseIndentLevel + 1, markerObj ) ;
     640                                else
     641                                        itemObj.contents.push( child ) ;
     642                        }
     643                }
     644                return baseArray ;
     645        },
     646
     647        // Convert our internal representation of a list back to a DOM tree.
     648        ArrayToList : function( listArray, baseIndex )
     649        {
     650                if ( baseIndex == undefined )
     651                        baseIndex = 0 ;
     652                if ( ! listArray || listArray.length < baseIndex + 1 )
     653                        return null ;
     654                var retval = listArray[baseIndex].parent.ownerDocument.createDocumentFragment() ;
     655                var rootNode = listArray[baseIndex].parent.cloneNode( false ) ;
     656                retval.appendChild( rootNode ) ;
     657                var currentIndex = baseIndex ;
     658                var indentLevel = listArray[baseIndex].indent ;
     659                var currentListItem = null ;
     660                while ( true )
     661                {
     662                        var item = listArray[currentIndex] ;
     663                        if ( item.indent == indentLevel )
     664                        {
     665                                if ( ! rootNode || listArray[currentIndex].parent.nodeName != rootNode.nodeName )
     666                                {
     667                                        rootNode = listArray[currentIndex].parent.cloneNode( false ) ;
     668                                        retval.appendChild( rootNode ) ;
     669                                }
     670                                currentListItem = rootNode.ownerDocument.createElement( 'li' ) ;
     671                                rootNode.appendChild( currentListItem ) ;
     672                                for ( var i = 0 ; i < item.contents.length ; i++ )
     673                                        currentListItem.appendChild( item.contents[i].cloneNode( true ) ) ;
     674                                currentIndex++ ;
     675                        }
     676                        else if ( item.indent == Math.max( indentLevel, 0 ) + 1 )
     677                        {
     678                                var listData = this.ArrayToList( listArray, currentIndex ) ;
     679                                currentListItem.appendChild( listData.listNode ) ;
     680                                currentIndex = listData.nextIndex ;
     681                        }
     682                        else if ( item.indent == -1 && baseIndex == 0 && item.grandparent )
     683                        {
     684                                var currentListItem ;
     685                                if ( item.grandparent.nodeName.IEquals( ['ul', 'ol'] ) )
     686                                        currentListItem = rootNode.ownerDocument.createElement( 'li' ) ;
     687                                else
     688                                {
     689                                        if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) )
     690                                                currentListItem = rootNode.ownerDocument.createElement( FCKConfig.EnterMode ) ;
     691                                        else
     692                                                currentListItem = rootNode.ownerDocument.createDocumentFragment() ;
     693                                }
     694                                item.grandparent.appendChild( currentListItem )  ;
     695                                for ( var i = 0 ; i < item.contents.length ; i++ )
     696                                        currentListItem.appendChild( item.contents[i].cloneNode( true ) ) ;
     697                                if ( currentListItem.nodeType == 11 )
     698                                        currentListItem.appendChild( currentListItem.ownerDocument.createElement( 'br' ) ) ;
     699                                retval.appendChild( currentListItem ) ;
     700                                rootNode = null ;
     701                                currentIndex++ ;
     702                        }
     703                        else
     704                                break ;
     705
     706                        if ( listArray.length <= currentIndex || Math.max( listArray[currentIndex].indent, 0 ) < indentLevel )
     707                        {
     708                                break ;
     709                        }
     710                }
     711                return { 'listNode' : retval, 'nextIndex' : currentIndex } ;
    577712        }
    578713} ;
     714
Note: See TracChangeset for help on using the changeset viewer.
© 2003 – 2012 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy