1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2007 Frederico Caldeira Knabben
4 *
5 * == BEGIN LICENSE ==
6 *
7 * Licensed under the terms of any of the following licenses at your
8 * choice:
9 *
10 *  - GNU General Public License Version 2 or later (the "GPL")
11 *    http://www.gnu.org/licenses/gpl.html
12 *
13 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14 *    http://www.gnu.org/licenses/lgpl.html
15 *
16 *  - Mozilla Public License Version 1.1 or later (the "MPL")
17 *    http://www.mozilla.org/MPL/MPL-1.1.html
18 *
19 * == END LICENSE ==
20 *
21 * FCKBlockQuoteCommand Class: adds or removes blockquote tags.
22 */
23
24var FCKBlockQuoteCommand = function()
25{
26}
27
28FCKBlockQuoteCommand.prototype =
29{
30	Execute : function()
31	{
32		FCKUndo.SaveUndoStep() ;
33
34		var state = this.GetState() ;
35		var range = new FCKDomRange( FCK.EditorWindow ) ;
36		range.MoveToSelection() ;
37		var bookmark = range.CreateBookmark() ;
38		var iterator = new FCKDomRangeIterator( range ) ;
39		var block ;
40
41		if ( state == FCK_TRISTATE_OFF )
42		{
43			iterator.EnforceRealBlocks = true ;
44			var paragraphs = [] ;
45			while ( ( block = iterator.GetNextParagraph() ) )
46				paragraphs.push( block ) ;
47
48			// Make sure all paragraphs have the same parent.
49			var commonParent = paragraphs[0].parentNode ;
50			var tmp = [] ;
51			for ( var i = 0 ; i < paragraphs.length ; i++ )
52			{
53				block = paragraphs[i] ;
54				commonParent = FCKDomTools.GetCommonParents( block.parentNode, commonParent ).pop() ;
55			}
56			var lastBlock = null ;
57			while ( paragraphs.length > 0 )
58			{
59				block = paragraphs.shift() ;
60				while ( block.parentNode != commonParent )
61					block = block.parentNode ;
62				if ( block != lastBlock )
63					tmp.push( block ) ;
64				lastBlock = block ;
65			}
66
67			// If any of the selected blocks is a blockquote, remove it to prevent nested blockquotes.
68			while ( tmp.length > 0 )
69			{
70				block = tmp.shift() ;
71				if ( block.nodeName.IEquals( 'blockquote' ) )
72				{
73					var docFrag = block.ownerDocument.createDocumentFragment() ;
74					while ( block.firstChild )
75					{
76						docFrag.appendChild( block.removeChild( block.firstChild ) ) ;
77						paragraphs.push( docFrag.lastChild ) ;
78					}
79					block.parentNode.replaceChild( docFrag, block ) ;
80				}
81				else
82					paragraphs.push( block ) ;
83			}
84
85			// Now we have all the blocks to be included in a new blockquote node.
86			var bqBlock = range.Window.document.createElement( 'blockquote' ) ;
87			commonParent.insertBefore( bqBlock, paragraphs[0] ) ;
88			while ( paragraphs.length > 0 )
89			{
90				block = paragraphs.shift() ;
91				bqBlock.appendChild( block ) ;
92			}
93		}
94		else if ( state == FCK_TRISTATE_ON )
95		{
96			var moveOutNodes = [] ;
97			while ( ( block = iterator.GetNextParagraph() ) )
98			{
99				var bqParent = null ;
100				var bqChild = null ;
101				while ( block.parentNode )
102				{
103					if ( block.parentNode.nodeName.IEquals( 'blockquote' ) )
104					{
105						bqParent = block.parentNode ;
106						bqChild = block ;
107						break ;
108					}
109					block = block.parentNode ;
110				}
111
112				if ( bqParent && bqChild )
113					moveOutNodes.push( bqChild ) ;
114			}
115
116			var movedNodes = [] ;
117			while ( moveOutNodes.length > 0 )
118			{
119				var node = moveOutNodes.shift() ;
120				var bqBlock = node.parentNode ;
121
122				// If the node is located at the beginning or the end, just take it out without splitting.
123				// Otherwise, split the blockquote node and move the paragraph in between the two blockquote nodes.
124				if ( node == node.parentNode.firstChild )
125				{
126					bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock ) ;
127					if ( ! bqBlock.firstChild )
128						bqBlock.parentNode.removeChild( bqBlock ) ;
129				}
130				else if ( node == node.parentNode.lastChild )
131				{
132					bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock.nextSibling ) ;
133					if ( ! bqBlock.firstChild )
134						bqBlock.parentNode.removeChild( bqBlock ) ;
135				}
136				else
137					FCKDomTools.BreakParent( node, node.parentNode, range ) ;
138
139				movedNodes.push( node ) ;
140			}
141
142			if ( FCKConfig.EnterMode.IEquals( 'br' ) )
143			{
144				while ( movedNodes.length )
145				{
146					var node = movedNodes.shift() ;
147					var firstTime = true ;
148					if ( node.nodeName.IEquals( 'div' ) )
149					{
150						var docFrag = node.ownerDocument.createDocumentFragment() ;
151						var needBeginBr = firstTime && node.previousSibling &&
152							!FCKListsLib.BlockBoundaries[node.previousSibling.nodeName.toLowerCase()] ;
153						if ( firstTime && needBeginBr )
154							docFrag.appendChild( node.ownerDocument.createElement( 'br' ) ) ;
155						var needEndBr = node.nextSibling &&
156							!FCKListsLib.BlockBoundaries[node.nextSibling.nodeName.toLowerCase()] ;
157						while ( node.firstChild )
158							docFrag.appendChild( node.removeChild( node.firstChild ) ) ;
159						if ( needEndBr )
160							docFrag.appendChild( node.ownerDocument.createElement( 'br' ) ) ;
161						node.parentNode.replaceChild( docFrag, node ) ;
162						firstTime = false ;
163					}
164				}
165			}
166		}
167		range.MoveToBookmark( bookmark ) ;
168		range.Select() ;
169
170		FCK.Focus() ;
171		FCK.Events.FireEvent( 'OnSelectionChange' ) ;
172	},
173
174	GetState : function()
175	{
176		// Disabled if not WYSIWYG.
177		if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
178			return FCK_TRISTATE_DISABLED ;
179
180		var path = new FCKElementPath( FCKSelection.GetBoundaryParentElement( true ) ) ;
181		var firstBlock = path.Block || path.BlockLimit ;
182
183		if ( !firstBlock || firstBlock.nodeName.toLowerCase() == 'body' )
184			return FCK_TRISTATE_OFF ;
185
186		// See if the first block has a blockquote parent.
187		for ( var i = 0 ; i < path.Elements.length ; i++ )
188		{
189			if ( path.Elements[i].nodeName.IEquals( 'blockquote' ) )
190				return FCK_TRISTATE_ON ;
191		}
192		return FCK_TRISTATE_OFF ;
193	}
194} ;
195