/*
* 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 ;
}
} ;