Changeset 869
- Timestamp:
- 09/24/07 13:06:59 (6 years ago)
- Location:
- FCKeditor/trunk/editor/_source
- Files:
-
- 4 edited
-
commandclasses/fckindentcommands.js (modified) (1 diff)
-
commandclasses/fcklistcommands.js (modified) (2 diffs)
-
internals/fckcommands.js (modified) (1 diff)
-
internals/fckdomtools.js (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
FCKeditor/trunk/editor/_source/commandclasses/fckindentcommands.js
r848 r869 229 229 230 230 // Convert the list DOM tree into a one dimensional array. 231 var listArray = FCKDomTools.ListToArray( listNode, null, null,markerObj ) ;231 var listArray = FCKDomTools.ListToArray( listNode, markerObj ) ; 232 232 233 233 // Apply indenting or outdenting on the array. -
FCKeditor/trunk/editor/_source/commandclasses/fcklistcommands.js
r771 r869 22 22 */ 23 23 24 var FCKListCommand = function( name )24 var FCKListCommand = function( name, tagName ) 25 25 { 26 26 this.Name = name ; 27 this.TagName = tagName ; 27 28 } 28 29 … … 31 32 GetState : function() 32 33 { 33 return FCK.GetNamedCommandState( this.Name ) ; 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 = FCKTools.GetElementAscensor( startContainer, this.TagName ) ; 42 if ( listNode ) 43 return FCK_TRISTATE_ON ; 44 else 45 return FCK_TRISTATE_OFF ; 34 46 }, 35 47 36 48 Execute : function() 37 49 { 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 ) 49 { 50 if ( startNode.childNodes.length <= range._Range.startOffset ) 51 startNode = startNode.lastChild ; 52 else 53 startNode = startNode.childNodes[ range._Range.startOffset ] ; 54 } 55 } 56 if ( endNode.nodeType == 1 ) 57 { 58 if ( endNode.firstChild ) 59 { 60 if ( endNode.childNodes.length <= range._Range.endOffset ) 61 endNode = endNode.lastChild ; 62 else 63 endNode = endNode.childNodes[ range._Range.endOffset ] ; 64 } 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 ) ; 50 FCKUndo.SaveUndoStep() ; 51 52 var range = new FCKDomRange( FCK.EditorWindow ) ; 53 range.MoveToSelection() ; 54 var bookmark = range.CreateBookmark() ; 55 56 // Group the blocks up because there are many cases where multiple lists have to be created, 57 // or multiple lists have to be cancelled. 58 var listGroups = [] ; 59 var markerObj = {} ; 60 var iterator = new FCKDomRangeIterator( range ) ; 61 var block ; 62 var state = this.GetState() ; 63 iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ; 64 var nextRangeExists = true ; 65 var rangeQueue = null ; 66 while ( nextRangeExists ) 67 { 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'] ) ) 79 { 80 var groupObj = el._FCK_ListGroupObject ; 81 if ( groupObj ) 82 groupObj.contents.push( block ) ; 83 else 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 ; 91 } 92 } 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 if ( FCKBrowserInfo.IsIE ) 110 nextRangeExists = false ; 111 else 112 { 113 if ( rangeQueue == null ) 114 { 115 rangeQueue = [] ; 116 var selectionObject = FCK.EditorWindow.getSelection() ; 117 if ( selectionObject && listGroups.length == 0 ) 118 rangeQueue.push( selectionObject.getRangeAt( 0 ) ) ; 119 for ( var i = 1 ; selectionObject && i < selectionObject.rangeCount ; i++ ) 120 rangeQueue.push( selectionObject.getRangeAt( i ) ) ; 121 } 122 if ( rangeQueue.length < 1 ) 123 nextRangeExists = false ; 124 else 125 { 126 var internalRange = FCKW3CRange.CreateFromRange( FCK.EditorDocument, rangeQueue.shift() ) ; 127 range._Range = internalRange ; 128 range._UpdateElementInfo() ; 129 if ( range.StartNode.nodeName.IEquals( 'td' ) ) 130 range.SetStart( range.StartNode, 1 ) ; 131 if ( range.EndNode.nodeName.IEquals( 'td' ) ) 132 range.SetEnd( range.EndNode, 2 ) ; 133 iterator = new FCKDomRangeIterator( range ) ; 134 iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ; 135 } 136 } 137 } 138 139 // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element. 140 // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking 141 // at the group that's not rooted at lists. So we have three cases to handle. 142 var listsCreated = [] ; 143 while ( listGroups.length > 0 ) 144 { 145 var groupObj = listGroups.shift() ; 146 if ( state == FCK_TRISTATE_OFF ) 147 { 148 if ( groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) ) 149 this._ChangeListType( groupObj, markerObj, listsCreated ) ; 150 else 151 this._CreateList( groupObj, listsCreated ) ; 152 } 153 else if ( state == FCK_TRISTATE_ON && groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) ) 154 this._RemoveList( groupObj, markerObj ) ; 155 } 156 157 // For all new lists created, merge adjacent, same type lists. 158 while ( listsCreated.length > 0 ) 159 { 160 var listNode = listsCreated.shift() ; 161 var stopFlag = false ; 162 var currentNode = listNode ; 163 while ( ! stopFlag ) 164 { 165 currentNode = currentNode.nextSibling ; 166 if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[\n\r\t ]*$/ ) == 0 ) 167 continue ; 168 stopFlag = true ; 169 } 170 171 if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) ) 172 { 173 currentNode.parentNode.removeChild( currentNode ) ; 174 while ( currentNode.firstChild ) 175 listNode.appendChild( currentNode.removeChild( currentNode.firstChild ) ) ; 176 } 177 } 178 179 // Clean up, restore selection and update toolbar button states. 180 FCKDomTools.ClearElementMarkers( markerObj ) ; 181 range.MoveToBookmark( bookmark ) ; 182 range.Select() ; 183 FCK.Events.FireEvent( 'OnSelectionChange' ) ; 184 }, 185 186 _ChangeListType : function( groupObj, markerObj, listsCreated ) 187 { 188 // This case is easy... 189 // 1. Convert the whole list into a one-dimensional array. 190 // 2. Change the list type by modifying the array. 191 // 3. Recreate the whole list by converting the array to a list. 192 // 4. Replace the original list with the recreated list. 193 var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ; 194 var selectedListItems = [] ; 195 for ( var i = 0 ; i < groupObj.contents.length ; i++ ) 196 { 197 var itemNode = groupObj.contents[i] ; 198 itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ; 199 if ( ! itemNode || itemNode._FCK_ListItem_Processed ) 200 continue ; 201 selectedListItems.push( itemNode ) ; 202 FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ; 203 } 204 var fakeParent = groupObj.root.ownerDocument.createElement( this.TagName ) ; 205 for ( var i = 0 ; i < selectedListItems.length ; i++ ) 206 { 207 var listIndex = selectedListItems[i]._FCK_ListArray_Index ; 208 listArray[listIndex].parent = fakeParent ; 209 } 210 var newList = FCKDomTools.ArrayToList( listArray ) ; 211 if ( newList.listNode.lastChild.nodeName.IEquals( this.TagName) ) 212 listsCreated.push( newList.listNode.lastChild ) ; 213 groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ; 214 }, 215 216 _CreateList : function( groupObj, listsCreated ) 217 { 218 var contents = groupObj.contents ; 219 var doc = groupObj.root.ownerDocument ; 220 var listContents = [] ; 221 222 // It is possible to have the contents returned by DomRangeIterator to be the same as the root. 223 // e.g. when we're running into table cells. 224 // In such a case, enclose the childNodes of contents[0] into a <div>. 225 if ( contents.length == 1 && contents[0] == groupObj.root ) 226 { 227 var divBlock = doc.createElement( 'div' ); 228 while ( contents[0].firstChild ) 229 divBlock.appendChild( contents[0].removeChild( contents[0].firstChild ) ) ; 230 contents[0].appendChild( divBlock ) ; 231 contents[0] = divBlock ; 232 } 233 234 // Calculate the common parent node of all content blocks. 235 var commonParent = groupObj.contents[0].parentNode ; 236 for ( var i = 0 ; i < contents.length ; i++ ) 237 commonParent = FCKDomTools.GetCommonParents( commonParent, contents[i].parentNode ).pop() ; 238 239 // We want to insert things that are in the same tree level only, so calculate the contents again 240 // by expanding the selected blocks to the same tree level. 241 for ( var i = 0 ; i < contents.length ; i++ ) 242 { 243 var contentNode = contents[i] ; 244 while ( contentNode.parentNode ) 245 { 246 if ( contentNode.parentNode == commonParent ) 247 { 248 listContents.push( contentNode ) ; 249 break ; 250 } 251 contentNode = contentNode.parentNode ; 252 } 253 } 254 255 if ( listContents.length < 1 ) 256 return ; 257 258 // Insert the list to the DOM tree. 259 var insertAnchor = listContents[listContents.length - 1].nextSibling ; 260 var listNode = doc.createElement( this.TagName ) ; 261 listsCreated.push( listNode ) ; 262 while ( listContents.length ) 263 { 264 var contentBlock = listContents.shift() ; 265 var docFrag = doc.createDocumentFragment() ; 266 while ( contentBlock.firstChild ) 267 docFrag.appendChild( contentBlock.removeChild( contentBlock.firstChild ) ) ; 268 contentBlock.parentNode.removeChild( contentBlock ) ; 269 var listItem = doc.createElement( 'li' ) ; 270 listItem.appendChild( docFrag ) ; 271 listNode.appendChild( listItem ) ; 272 } 273 commonParent.insertBefore( listNode, insertAnchor ) ; 274 }, 275 276 _RemoveList : function( groupObj, markerObj ) 277 { 278 // This is very much like the change list type operation. 279 // Except that we're changing the selected items' indent to -1 in the list array. 280 var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ; 281 var selectedListItems = [] ; 282 for ( var i = 0 ; i < groupObj.contents.length ; i++ ) 283 { 284 var itemNode = groupObj.contents[i] ; 285 itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ; 286 if ( ! itemNode || itemNode._FCK_ListItem_Processed ) 287 continue ; 288 selectedListItems.push( itemNode ) ; 289 FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ; 290 } 291 292 var lastListIndex = null ; 293 for ( var i = 0 ; i < selectedListItems.length ; i++ ) 294 { 295 var listIndex = selectedListItems[i]._FCK_ListArray_Index ; 296 listArray[listIndex].indent = -1 ; 297 lastListIndex = listIndex ; 298 } 299 300 // After cutting parts of the list out with indent=-1, we still have to maintain the array list 301 // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the 302 // list cannot be converted back to a real DOM list. 303 for ( var i = lastListIndex + 1; i < listArray.length ; i++ ) 304 { 305 if ( listArray[i].indent > listArray[i-1].indent + 1 ) 306 { 307 var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent ; 308 var oldIndent = listArray[i].indent ; 309 while ( listArray[i] && listArray[i].indent >= oldIndent) 310 { 311 listArray[i].indent += indentOffset ; 312 i++ ; 313 } 314 i-- ; 315 } 316 } 317 318 var newList = FCKDomTools.ArrayToList( listArray ) ; 319 // If groupObj.root is the last element in its parent, or its nextSibling is a <br>, then we should 320 // not add a <br> after the final item. So, check for the cases and trim the <br>. 321 if ( groupObj.root.nextSibling == null || groupObj.root.nextSibling.nodeName.IEquals( 'br' ) ) 322 { 323 if ( newList.listNode.lastChild.nodeName.IEquals( 'br' ) ) 324 newList.listNode.removeChild( newList.listNode.lastChild ) ; 325 } 326 groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ; 89 327 } 90 328 }; -
FCKeditor/trunk/editor/_source/internals/fckcommands.js
r850 r869 131 131 132 132 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 ; 135 135 case 'ShowBlocks' : oCommand = new FCKShowBlockCommand( 'ShowBlocks', FCKConfig.StartupShowBlocks ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF ) ; break ; 136 136 -
FCKeditor/trunk/editor/_source/internals/fckdomtools.js
r865 r869 610 610 // This operation should be non-intrusive in the sense that it does not change the DOM tree, 611 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)612 ListToArray : function( listNode, markerObj, baseArray, baseIndentLevel, grandparentNode ) 613 613 { 614 614 if ( ! listNode.nodeName.IEquals( ['ul', 'ol'] ) ) … … 625 625 if ( ! listItem.nodeName.IEquals( 'li' ) ) 626 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 ; 627 var itemObj = { 'parent' : listNode, 'indent' : baseIndentLevel, 'contents' : [] } ; 628 if ( ! grandparentNode ) 629 { 630 itemObj.grandparent = listNode.parentNode ; 631 if ( itemObj.grandparent && itemObj.grandparent.nodeName.IEquals( 'li' ) ) 632 itemObj.grandparent = itemObj.grandparent.parentNode ; 633 } 634 else 635 itemObj.grandparent = grandparentNode ; 630 636 if ( markerObj ) 631 637 this.SetElementMarker( markerObj, listItem, '_FCK_ListArray_Index', baseArray.length ) ; … … 637 643 // Note the recursion here, it pushes inner list items with +1 indentation in the correct 638 644 // order. 639 this.ListToArray( child, baseArray, baseIndentLevel + 1, markerObj) ;645 this.ListToArray( child, markerObj, baseArray, baseIndentLevel + 1, itemObj.grandparent ) ; 640 646 else 641 647 itemObj.contents.push( child ) ; … … 687 693 else 688 694 { 689 if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) )695 if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) && ! item.grandparent.nodeName.IEquals( 'td' ) ) 690 696 currentListItem = doc.createElement( FCKConfig.EnterMode ) ; 691 697 else
Note: See TracChangeset
for help on using the changeset viewer.
