summaryrefslogtreecommitdiff
path: root/rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js
diff options
context:
space:
mode:
Diffstat (limited to 'rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js')
-rw-r--r--rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js382
1 files changed, 382 insertions, 0 deletions
diff --git a/rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js b/rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js
new file mode 100644
index 0000000..16f71ea
--- /dev/null
+++ b/rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/commandclasses/fcklistcommands.js
@@ -0,0 +1,382 @@
+/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2009 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ * - GNU General Public License Version 2 or later (the "GPL")
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * - Mozilla Public License Version 1.1 or later (the "MPL")
+ * http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Implementation for the "Insert/Remove Ordered/Unordered List" commands.
+ */
+
+var FCKListCommand = function( name, tagName )
+{
+ this.Name = name ;
+ this.TagName = tagName ;
+}
+
+FCKListCommand.prototype =
+{
+ GetState : function()
+ {
+ // Disabled if not WYSIWYG.
+ if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
+ return FCK_TRISTATE_DISABLED ;
+
+ // We'll use the style system's convention to determine list state here...
+ // If the starting block is a descendant of an <ol> or <ul> node, then we're in a list.
+ var startContainer = FCKSelection.GetBoundaryParentElement( true ) ;
+ var listNode = startContainer ;
+ while ( listNode )
+ {
+ if ( listNode.nodeName.IEquals( [ 'ul', 'ol' ] ) )
+ break ;
+ listNode = listNode.parentNode ;
+ }
+ if ( listNode && listNode.nodeName.IEquals( this.TagName ) )
+ return FCK_TRISTATE_ON ;
+ else
+ return FCK_TRISTATE_OFF ;
+ },
+
+ Execute : function()
+ {
+ FCKUndo.SaveUndoStep() ;
+
+ var doc = FCK.EditorDocument ;
+ var range = new FCKDomRange( FCK.EditorWindow ) ;
+ range.MoveToSelection() ;
+ var state = this.GetState() ;
+
+ // Midas lists rule #1 says we can create a list even in an empty document.
+ // But FCKDomRangeIterator wouldn't run if the document is really empty.
+ // So create a paragraph if the document is empty and we're going to create a list.
+ if ( state == FCK_TRISTATE_OFF )
+ {
+ FCKDomTools.TrimNode( doc.body ) ;
+ if ( ! doc.body.firstChild )
+ {
+ var paragraph = doc.createElement( 'p' ) ;
+ doc.body.appendChild( paragraph ) ;
+ range.MoveToNodeContents( paragraph ) ;
+ }
+ }
+
+ var bookmark = range.CreateBookmark() ;
+
+ // Group the blocks up because there are many cases where multiple lists have to be created,
+ // or multiple lists have to be cancelled.
+ var listGroups = [] ;
+ var markerObj = {} ;
+ var iterator = new FCKDomRangeIterator( range ) ;
+ var block ;
+
+ iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
+ var nextRangeExists = true ;
+ var rangeQueue = null ;
+ while ( nextRangeExists )
+ {
+ while ( ( block = iterator.GetNextParagraph() ) )
+ {
+ var path = new FCKElementPath( block ) ;
+ var listNode = null ;
+ var processedFlag = false ;
+ var blockLimit = path.BlockLimit ;
+
+ // First, try to group by a list ancestor.
+ for ( var i = path.Elements.length - 1 ; i >= 0 ; i-- )
+ {
+ var el = path.Elements[i] ;
+ if ( el.nodeName.IEquals( ['ol', 'ul'] ) )
+ {
+ // If we've encountered a list inside a block limit
+ // The last group object of the block limit element should
+ // no longer be valid. Since paragraphs after the list
+ // should belong to a different group of paragraphs before
+ // the list. (Bug #1309)
+ if ( blockLimit._FCK_ListGroupObject )
+ blockLimit._FCK_ListGroupObject = null ;
+
+ var groupObj = el._FCK_ListGroupObject ;
+ if ( groupObj )
+ groupObj.contents.push( block ) ;
+ else
+ {
+ groupObj = { 'root' : el, 'contents' : [ block ] } ;
+ listGroups.push( groupObj ) ;
+ FCKDomTools.SetElementMarker( markerObj, el, '_FCK_ListGroupObject', groupObj ) ;
+ }
+ processedFlag = true ;
+ break ;
+ }
+ }
+
+ if ( processedFlag )
+ continue ;
+
+ // No list ancestor? Group by block limit.
+ var root = blockLimit ;
+ if ( root._FCK_ListGroupObject )
+ root._FCK_ListGroupObject.contents.push( block ) ;
+ else
+ {
+ var groupObj = { 'root' : root, 'contents' : [ block ] } ;
+ FCKDomTools.SetElementMarker( markerObj, root, '_FCK_ListGroupObject', groupObj ) ;
+ listGroups.push( groupObj ) ;
+ }
+ }
+
+ if ( FCKBrowserInfo.IsIE )
+ nextRangeExists = false ;
+ else
+ {
+ if ( rangeQueue == null )
+ {
+ rangeQueue = [] ;
+ var selectionObject = FCKSelection.GetSelection() ;
+ if ( selectionObject && listGroups.length == 0 )
+ rangeQueue.push( selectionObject.getRangeAt( 0 ) ) ;
+ for ( var i = 1 ; selectionObject && i < selectionObject.rangeCount ; i++ )
+ rangeQueue.push( selectionObject.getRangeAt( i ) ) ;
+ }
+ if ( rangeQueue.length < 1 )
+ nextRangeExists = false ;
+ else
+ {
+ var internalRange = FCKW3CRange.CreateFromRange( doc, rangeQueue.shift() ) ;
+ range._Range = internalRange ;
+ range._UpdateElementInfo() ;
+ if ( range.StartNode.nodeName.IEquals( 'td' ) )
+ range.SetStart( range.StartNode, 1 ) ;
+ if ( range.EndNode.nodeName.IEquals( 'td' ) )
+ range.SetEnd( range.EndNode, 2 ) ;
+ iterator = new FCKDomRangeIterator( range ) ;
+ iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
+ }
+ }
+ }
+
+ // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element.
+ // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking
+ // at the group that's not rooted at lists. So we have three cases to handle.
+ var listsCreated = [] ;
+ while ( listGroups.length > 0 )
+ {
+ var groupObj = listGroups.shift() ;
+ if ( state == FCK_TRISTATE_OFF )
+ {
+ if ( groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
+ this._ChangeListType( groupObj, markerObj, listsCreated ) ;
+ else
+ this._CreateList( groupObj, listsCreated ) ;
+ }
+ else if ( state == FCK_TRISTATE_ON && groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
+ this._RemoveList( groupObj, markerObj ) ;
+ }
+
+ // For all new lists created, merge adjacent, same type lists.
+ for ( var i = 0 ; i < listsCreated.length ; i++ )
+ {
+ var listNode = listsCreated[i] ;
+ var stopFlag = false ;
+ var currentNode = listNode ;
+ while ( ! stopFlag )
+ {
+ currentNode = currentNode.nextSibling ;
+ if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[\n\r\t ]*$/ ) == 0 )
+ continue ;
+ stopFlag = true ;
+ }
+
+ if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
+ {
+ currentNode.parentNode.removeChild( currentNode ) ;
+ while ( currentNode.firstChild )
+ listNode.appendChild( currentNode.removeChild( currentNode.firstChild ) ) ;
+ }
+
+ stopFlag = false ;
+ currentNode = listNode ;
+ while ( ! stopFlag )
+ {
+ currentNode = currentNode.previousSibling ;
+ if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[\n\r\t ]*$/ ) == 0 )
+ continue ;
+ stopFlag = true ;
+ }
+ if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
+ {
+ currentNode.parentNode.removeChild( currentNode ) ;
+ while ( currentNode.lastChild )
+ listNode.insertBefore( currentNode.removeChild( currentNode.lastChild ),
+ listNode.firstChild ) ;
+ }
+ }
+
+ // Clean up, restore selection and update toolbar button states.
+ FCKDomTools.ClearAllMarkers( markerObj ) ;
+ range.MoveToBookmark( bookmark ) ;
+ range.Select() ;
+
+ FCK.Focus() ;
+ FCK.Events.FireEvent( 'OnSelectionChange' ) ;
+ },
+
+ _ChangeListType : function( groupObj, markerObj, listsCreated )
+ {
+ // This case is easy...
+ // 1. Convert the whole list into a one-dimensional array.
+ // 2. Change the list type by modifying the array.
+ // 3. Recreate the whole list by converting the array to a list.
+ // 4. Replace the original list with the recreated list.
+ var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
+ var selectedListItems = [] ;
+ for ( var i = 0 ; i < groupObj.contents.length ; i++ )
+ {
+ var itemNode = groupObj.contents[i] ;
+ itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
+ if ( ! itemNode || itemNode._FCK_ListItem_Processed )
+ continue ;
+ selectedListItems.push( itemNode ) ;
+ FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
+ }
+ var fakeParent = FCKTools.GetElementDocument( groupObj.root ).createElement( this.TagName ) ;
+ for ( var i = 0 ; i < selectedListItems.length ; i++ )
+ {
+ var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
+ listArray[listIndex].parent = fakeParent ;
+ }
+ var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
+ for ( var i = 0 ; i < newList.listNode.childNodes.length ; i++ )
+ {
+ if ( newList.listNode.childNodes[i].nodeName.IEquals( this.TagName ) )
+ listsCreated.push( newList.listNode.childNodes[i] ) ;
+ }
+ groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
+ },
+
+ _CreateList : function( groupObj, listsCreated )
+ {
+ var contents = groupObj.contents ;
+ var doc = FCKTools.GetElementDocument( groupObj.root ) ;
+ var listContents = [] ;
+
+ // It is possible to have the contents returned by DomRangeIterator to be the same as the root.
+ // e.g. when we're running into table cells.
+ // In such a case, enclose the childNodes of contents[0] into a <div>.
+ if ( contents.length == 1 && contents[0] == groupObj.root )
+ {
+ var divBlock = doc.createElement( 'div' );
+ while ( contents[0].firstChild )
+ divBlock.appendChild( contents[0].removeChild( contents[0].firstChild ) ) ;
+ contents[0].appendChild( divBlock ) ;
+ contents[0] = divBlock ;
+ }
+
+ // Calculate the common parent node of all content blocks.
+ var commonParent = groupObj.contents[0].parentNode ;
+ for ( var i = 0 ; i < contents.length ; i++ )
+ commonParent = FCKDomTools.GetCommonParents( commonParent, contents[i].parentNode ).pop() ;
+
+ // We want to insert things that are in the same tree level only, so calculate the contents again
+ // by expanding the selected blocks to the same tree level.
+ for ( var i = 0 ; i < contents.length ; i++ )
+ {
+ var contentNode = contents[i] ;
+ while ( contentNode.parentNode )
+ {
+ if ( contentNode.parentNode == commonParent )
+ {
+ listContents.push( contentNode ) ;
+ break ;
+ }
+ contentNode = contentNode.parentNode ;
+ }
+ }
+
+ if ( listContents.length < 1 )
+ return ;
+
+ // Insert the list to the DOM tree.
+ var insertAnchor = listContents[listContents.length - 1].nextSibling ;
+ var listNode = doc.createElement( this.TagName ) ;
+ listsCreated.push( listNode ) ;
+ while ( listContents.length )
+ {
+ var contentBlock = listContents.shift() ;
+ var docFrag = doc.createDocumentFragment() ;
+ while ( contentBlock.firstChild )
+ docFrag.appendChild( contentBlock.removeChild( contentBlock.firstChild ) ) ;
+ contentBlock.parentNode.removeChild( contentBlock ) ;
+ var listItem = doc.createElement( 'li' ) ;
+ listItem.appendChild( docFrag ) ;
+ listNode.appendChild( listItem ) ;
+ }
+ commonParent.insertBefore( listNode, insertAnchor ) ;
+ },
+
+ _RemoveList : function( groupObj, markerObj )
+ {
+ // This is very much like the change list type operation.
+ // Except that we're changing the selected items' indent to -1 in the list array.
+ var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
+ var selectedListItems = [] ;
+ for ( var i = 0 ; i < groupObj.contents.length ; i++ )
+ {
+ var itemNode = groupObj.contents[i] ;
+ itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
+ if ( ! itemNode || itemNode._FCK_ListItem_Processed )
+ continue ;
+ selectedListItems.push( itemNode ) ;
+ FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
+ }
+
+ var lastListIndex = null ;
+ for ( var i = 0 ; i < selectedListItems.length ; i++ )
+ {
+ var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
+ listArray[listIndex].indent = -1 ;
+ lastListIndex = listIndex ;
+ }
+
+ // After cutting parts of the list out with indent=-1, we still have to maintain the array list
+ // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the
+ // list cannot be converted back to a real DOM list.
+ for ( var i = lastListIndex + 1; i < listArray.length ; i++ )
+ {
+ if ( listArray[i].indent > listArray[i-1].indent + 1 )
+ {
+ var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent ;
+ var oldIndent = listArray[i].indent ;
+ while ( listArray[i] && listArray[i].indent >= oldIndent)
+ {
+ listArray[i].indent += indentOffset ;
+ i++ ;
+ }
+ i-- ;
+ }
+ }
+
+ var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
+ // If groupObj.root is the last element in its parent, or its nextSibling is a <br>, then we should
+ // not add a <br> after the final item. So, check for the cases and trim the <br>.
+ if ( groupObj.root.nextSibling == null || groupObj.root.nextSibling.nodeName.IEquals( 'br' ) )
+ {
+ if ( newList.listNode.lastChild.nodeName.IEquals( 'br' ) )
+ newList.listNode.removeChild( newList.listNode.lastChild ) ;
+ }
+ groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
+ }
+};