--- /dev/null
+/*\r
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
+ * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
+ *\r
+ * == BEGIN LICENSE ==\r
+ *\r
+ * Licensed under the terms of any of the following licenses at your\r
+ * choice:\r
+ *\r
+ * - GNU General Public License Version 2 or later (the "GPL")\r
+ * http://www.gnu.org/licenses/gpl.html\r
+ *\r
+ * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
+ * http://www.gnu.org/licenses/lgpl.html\r
+ *\r
+ * - Mozilla Public License Version 1.1 or later (the "MPL")\r
+ * http://www.mozilla.org/MPL/MPL-1.1.html\r
+ *\r
+ * == END LICENSE ==\r
+ *\r
+ * FCKBlockQuoteCommand Class: adds or removes blockquote tags.\r
+ */\r
+\r
+var FCKBlockQuoteCommand = function()\r
+{\r
+}\r
+\r
+FCKBlockQuoteCommand.prototype =\r
+{\r
+ Execute : function()\r
+ {\r
+ FCKUndo.SaveUndoStep() ;\r
+\r
+ var state = this.GetState() ;\r
+\r
+ var range = new FCKDomRange( FCK.EditorWindow ) ;\r
+ range.MoveToSelection() ;\r
+\r
+ var bookmark = range.CreateBookmark() ;\r
+\r
+ // Kludge for #1592: if the bookmark nodes are in the beginning of\r
+ // blockquote, then move them to the nearest block element in the\r
+ // blockquote.\r
+ if ( FCKBrowserInfo.IsIE )\r
+ {\r
+ var bStart = range.GetBookmarkNode( bookmark, true ) ;\r
+ var bEnd = range.GetBookmarkNode( bookmark, false ) ;\r
+\r
+ var cursor ;\r
+\r
+ if ( bStart\r
+ && bStart.parentNode.nodeName.IEquals( 'blockquote' )\r
+ && !bStart.previousSibling )\r
+ {\r
+ cursor = bStart ;\r
+ while ( ( cursor = cursor.nextSibling ) )\r
+ {\r
+ if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )\r
+ FCKDomTools.MoveNode( bStart, cursor, true ) ;\r
+ }\r
+ }\r
+\r
+ if ( bEnd\r
+ && bEnd.parentNode.nodeName.IEquals( 'blockquote' )\r
+ && !bEnd.previousSibling )\r
+ {\r
+ cursor = bEnd ;\r
+ while ( ( cursor = cursor.nextSibling ) )\r
+ {\r
+ if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )\r
+ {\r
+ if ( cursor.firstChild == bStart )\r
+ FCKDomTools.InsertAfterNode( bStart, bEnd ) ;\r
+ else\r
+ FCKDomTools.MoveNode( bEnd, cursor, true ) ;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ var iterator = new FCKDomRangeIterator( range ) ;\r
+ var block ;\r
+\r
+ if ( state == FCK_TRISTATE_OFF )\r
+ {\r
+ var paragraphs = [] ;\r
+ while ( ( block = iterator.GetNextParagraph() ) )\r
+ paragraphs.push( block ) ;\r
+\r
+ // If no paragraphs, create one from the current selection position.\r
+ if ( paragraphs.length < 1 )\r
+ {\r
+ para = range.Window.document.createElement( FCKConfig.EnterMode.IEquals( 'p' ) ? 'p' : 'div' ) ;\r
+ range.InsertNode( para ) ;\r
+ para.appendChild( range.Window.document.createTextNode( '\ufeff' ) ) ;\r
+ range.MoveToBookmark( bookmark ) ;\r
+ range.MoveToNodeContents( para ) ;\r
+ range.Collapse( true ) ;\r
+ bookmark = range.CreateBookmark() ;\r
+ paragraphs.push( para ) ;\r
+ }\r
+\r
+ // Make sure all paragraphs have the same parent.\r
+ var commonParent = paragraphs[0].parentNode ;\r
+ var tmp = [] ;\r
+ for ( var i = 0 ; i < paragraphs.length ; i++ )\r
+ {\r
+ block = paragraphs[i] ;\r
+ commonParent = FCKDomTools.GetCommonParents( block.parentNode, commonParent ).pop() ;\r
+ }\r
+\r
+ // The common parent must not be the following tags: table, tbody, tr, ol, ul.\r
+ while ( commonParent.nodeName.IEquals( 'table', 'tbody', 'tr', 'ol', 'ul' ) )\r
+ commonParent = commonParent.parentNode ;\r
+\r
+ // Reconstruct the block list to be processed such that all resulting blocks\r
+ // satisfy parentNode == commonParent.\r
+ var lastBlock = null ;\r
+ while ( paragraphs.length > 0 )\r
+ {\r
+ block = paragraphs.shift() ;\r
+ while ( block.parentNode != commonParent )\r
+ block = block.parentNode ;\r
+ if ( block != lastBlock )\r
+ tmp.push( block ) ;\r
+ lastBlock = block ;\r
+ }\r
+\r
+ // If any of the selected blocks is a blockquote, remove it to prevent nested blockquotes.\r
+ while ( tmp.length > 0 )\r
+ {\r
+ block = tmp.shift() ;\r
+ if ( block.nodeName.IEquals( 'blockquote' ) )\r
+ {\r
+ var docFrag = FCKTools.GetElementDocument( block ).createDocumentFragment() ;\r
+ while ( block.firstChild )\r
+ {\r
+ docFrag.appendChild( block.removeChild( block.firstChild ) ) ;\r
+ paragraphs.push( docFrag.lastChild ) ;\r
+ }\r
+ block.parentNode.replaceChild( docFrag, block ) ;\r
+ }\r
+ else\r
+ paragraphs.push( block ) ;\r
+ }\r
+\r
+ // Now we have all the blocks to be included in a new blockquote node.\r
+ var bqBlock = range.Window.document.createElement( 'blockquote' ) ;\r
+ commonParent.insertBefore( bqBlock, paragraphs[0] ) ;\r
+ while ( paragraphs.length > 0 )\r
+ {\r
+ block = paragraphs.shift() ;\r
+ bqBlock.appendChild( block ) ;\r
+ }\r
+ }\r
+ else if ( state == FCK_TRISTATE_ON )\r
+ {\r
+ var moveOutNodes = [] ;\r
+ var elementMarkers = {} ;\r
+ while ( ( block = iterator.GetNextParagraph() ) )\r
+ {\r
+ var bqParent = null ;\r
+ var bqChild = null ;\r
+ while ( block.parentNode )\r
+ {\r
+ if ( block.parentNode.nodeName.IEquals( 'blockquote' ) )\r
+ {\r
+ bqParent = block.parentNode ;\r
+ bqChild = block ;\r
+ break ;\r
+ }\r
+ block = block.parentNode ;\r
+ }\r
+\r
+ // Remember the blocks that were recorded down in the moveOutNodes array\r
+ // to prevent duplicates.\r
+ if ( bqParent && bqChild && !bqChild._fckblockquotemoveout )\r
+ {\r
+ moveOutNodes.push( bqChild ) ;\r
+ FCKDomTools.SetElementMarker( elementMarkers, bqChild, '_fckblockquotemoveout', true ) ;\r
+ }\r
+ }\r
+ FCKDomTools.ClearAllMarkers( elementMarkers ) ;\r
+\r
+ var movedNodes = [] ;\r
+ var processedBlockquoteBlocks = [], elementMarkers = {} ;\r
+ var noBlockLeft = function( bqBlock )\r
+ {\r
+ for ( var i = 0 ; i < bqBlock.childNodes.length ; i++ )\r
+ {\r
+ if ( FCKListsLib.BlockElements[ bqBlock.childNodes[i].nodeName.toLowerCase() ] )\r
+ return false ;\r
+ }\r
+ return true ;\r
+ } ;\r
+ while ( moveOutNodes.length > 0 )\r
+ {\r
+ var node = moveOutNodes.shift() ;\r
+ var bqBlock = node.parentNode ;\r
+\r
+ // If the node is located at the beginning or the end, just take it out without splitting.\r
+ // Otherwise, split the blockquote node and move the paragraph in between the two blockquote nodes.\r
+ if ( node == node.parentNode.firstChild )\r
+ bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock ) ;\r
+ else if ( node == node.parentNode.lastChild )\r
+ bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock.nextSibling ) ;\r
+ else\r
+ FCKDomTools.BreakParent( node, node.parentNode, range ) ;\r
+\r
+ // Remember the blockquote node so we can clear it later (if it becomes empty).\r
+ if ( !bqBlock._fckbqprocessed )\r
+ {\r
+ processedBlockquoteBlocks.push( bqBlock ) ;\r
+ FCKDomTools.SetElementMarker( elementMarkers, bqBlock, '_fckbqprocessed', true );\r
+ }\r
+\r
+ movedNodes.push( node ) ;\r
+ }\r
+\r
+ // Clear blockquote nodes that have become empty.\r
+ for ( var i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )\r
+ {\r
+ var bqBlock = processedBlockquoteBlocks[i] ;\r
+ if ( noBlockLeft( bqBlock ) )\r
+ FCKDomTools.RemoveNode( bqBlock ) ;\r
+ }\r
+ FCKDomTools.ClearAllMarkers( elementMarkers ) ;\r
+\r
+ if ( FCKConfig.EnterMode.IEquals( 'br' ) )\r
+ {\r
+ while ( movedNodes.length )\r
+ {\r
+ var node = movedNodes.shift() ;\r
+ var firstTime = true ;\r
+ if ( node.nodeName.IEquals( 'div' ) )\r
+ {\r
+ var docFrag = FCKTools.GetElementDocument( node ).createDocumentFragment() ;\r
+ var needBeginBr = firstTime && node.previousSibling &&\r
+ !FCKListsLib.BlockBoundaries[node.previousSibling.nodeName.toLowerCase()] ;\r
+ if ( firstTime && needBeginBr )\r
+ docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;\r
+ var needEndBr = node.nextSibling &&\r
+ !FCKListsLib.BlockBoundaries[node.nextSibling.nodeName.toLowerCase()] ;\r
+ while ( node.firstChild )\r
+ docFrag.appendChild( node.removeChild( node.firstChild ) ) ;\r
+ if ( needEndBr )\r
+ docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;\r
+ node.parentNode.replaceChild( docFrag, node ) ;\r
+ firstTime = false ;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ range.MoveToBookmark( bookmark ) ;\r
+ range.Select() ;\r
+\r
+ FCK.Focus() ;\r
+ FCK.Events.FireEvent( 'OnSelectionChange' ) ;\r
+ },\r
+\r
+ GetState : function()\r
+ {\r
+ // Disabled if not WYSIWYG.\r
+ if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )\r
+ return FCK_TRISTATE_DISABLED ;\r
+\r
+ var path = new FCKElementPath( FCKSelection.GetBoundaryParentElement( true ) ) ;\r
+ var firstBlock = path.Block || path.BlockLimit ;\r
+\r
+ if ( !firstBlock || firstBlock.nodeName.toLowerCase() == 'body' )\r
+ return FCK_TRISTATE_OFF ;\r
+\r
+ // See if the first block has a blockquote parent.\r
+ for ( var i = 0 ; i < path.Elements.length ; i++ )\r
+ {\r
+ if ( path.Elements[i].nodeName.IEquals( 'blockquote' ) )\r
+ return FCK_TRISTATE_ON ;\r
+ }\r
+ return FCK_TRISTATE_OFF ;\r
+ }\r
+} ;\r