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