2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
\r
3 * Copyright (C) 2003-2009 Frederico Caldeira Knabben
\r
5 * == BEGIN LICENSE ==
\r
7 * Licensed under the terms of any of the following licenses at your
\r
10 * - GNU General Public License Version 2 or later (the "GPL")
\r
11 * http://www.gnu.org/licenses/gpl.html
\r
13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
\r
14 * http://www.gnu.org/licenses/lgpl.html
\r
16 * - Mozilla Public License Version 1.1 or later (the "MPL")
\r
17 * http://www.mozilla.org/MPL/MPL-1.1.html
\r
21 * FCKBlockQuoteCommand Class: adds or removes blockquote tags.
\r
24 var FCKBlockQuoteCommand = function()
\r
28 FCKBlockQuoteCommand.prototype =
\r
30 Execute : function()
\r
32 FCKUndo.SaveUndoStep() ;
\r
34 var state = this.GetState() ;
\r
36 var range = new FCKDomRange( FCK.EditorWindow ) ;
\r
37 range.MoveToSelection() ;
\r
39 var bookmark = range.CreateBookmark() ;
\r
41 // Kludge for #1592: if the bookmark nodes are in the beginning of
\r
42 // blockquote, then move them to the nearest block element in the
\r
44 if ( FCKBrowserInfo.IsIE )
\r
46 var bStart = range.GetBookmarkNode( bookmark, true ) ;
\r
47 var bEnd = range.GetBookmarkNode( bookmark, false ) ;
\r
52 && bStart.parentNode.nodeName.IEquals( 'blockquote' )
\r
53 && !bStart.previousSibling )
\r
56 while ( ( cursor = cursor.nextSibling ) )
\r
58 if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )
\r
59 FCKDomTools.MoveNode( bStart, cursor, true ) ;
\r
64 && bEnd.parentNode.nodeName.IEquals( 'blockquote' )
\r
65 && !bEnd.previousSibling )
\r
68 while ( ( cursor = cursor.nextSibling ) )
\r
70 if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )
\r
72 if ( cursor.firstChild == bStart )
\r
73 FCKDomTools.InsertAfterNode( bStart, bEnd ) ;
\r
75 FCKDomTools.MoveNode( bEnd, cursor, true ) ;
\r
81 var iterator = new FCKDomRangeIterator( range ) ;
\r
84 if ( state == FCK_TRISTATE_OFF )
\r
86 var paragraphs = [] ;
\r
87 while ( ( block = iterator.GetNextParagraph() ) )
\r
88 paragraphs.push( block ) ;
\r
90 // If no paragraphs, create one from the current selection position.
\r
91 if ( paragraphs.length < 1 )
\r
93 para = range.Window.document.createElement( FCKConfig.EnterMode.IEquals( 'p' ) ? 'p' : 'div' ) ;
\r
94 range.InsertNode( para ) ;
\r
95 para.appendChild( range.Window.document.createTextNode( '\ufeff' ) ) ;
\r
96 range.MoveToBookmark( bookmark ) ;
\r
97 range.MoveToNodeContents( para ) ;
\r
98 range.Collapse( true ) ;
\r
99 bookmark = range.CreateBookmark() ;
\r
100 paragraphs.push( para ) ;
\r
103 // Make sure all paragraphs have the same parent.
\r
104 var commonParent = paragraphs[0].parentNode ;
\r
106 for ( var i = 0 ; i < paragraphs.length ; i++ )
\r
108 block = paragraphs[i] ;
\r
109 commonParent = FCKDomTools.GetCommonParents( block.parentNode, commonParent ).pop() ;
\r
112 // The common parent must not be the following tags: table, tbody, tr, ol, ul.
\r
113 while ( commonParent.nodeName.IEquals( 'table', 'tbody', 'tr', 'ol', 'ul' ) )
\r
114 commonParent = commonParent.parentNode ;
\r
116 // Reconstruct the block list to be processed such that all resulting blocks
\r
117 // satisfy parentNode == commonParent.
\r
118 var lastBlock = null ;
\r
119 while ( paragraphs.length > 0 )
\r
121 block = paragraphs.shift() ;
\r
122 while ( block.parentNode != commonParent )
\r
123 block = block.parentNode ;
\r
124 if ( block != lastBlock )
\r
125 tmp.push( block ) ;
\r
126 lastBlock = block ;
\r
129 // If any of the selected blocks is a blockquote, remove it to prevent nested blockquotes.
\r
130 while ( tmp.length > 0 )
\r
132 block = tmp.shift() ;
\r
133 if ( block.nodeName.IEquals( 'blockquote' ) )
\r
135 var docFrag = FCKTools.GetElementDocument( block ).createDocumentFragment() ;
\r
136 while ( block.firstChild )
\r
138 docFrag.appendChild( block.removeChild( block.firstChild ) ) ;
\r
139 paragraphs.push( docFrag.lastChild ) ;
\r
141 block.parentNode.replaceChild( docFrag, block ) ;
\r
144 paragraphs.push( block ) ;
\r
147 // Now we have all the blocks to be included in a new blockquote node.
\r
148 var bqBlock = range.Window.document.createElement( 'blockquote' ) ;
\r
149 commonParent.insertBefore( bqBlock, paragraphs[0] ) ;
\r
150 while ( paragraphs.length > 0 )
\r
152 block = paragraphs.shift() ;
\r
153 bqBlock.appendChild( block ) ;
\r
156 else if ( state == FCK_TRISTATE_ON )
\r
158 var moveOutNodes = [] ;
\r
159 var elementMarkers = {} ;
\r
160 while ( ( block = iterator.GetNextParagraph() ) )
\r
162 var bqParent = null ;
\r
163 var bqChild = null ;
\r
164 while ( block.parentNode )
\r
166 if ( block.parentNode.nodeName.IEquals( 'blockquote' ) )
\r
168 bqParent = block.parentNode ;
\r
172 block = block.parentNode ;
\r
175 // Remember the blocks that were recorded down in the moveOutNodes array
\r
176 // to prevent duplicates.
\r
177 if ( bqParent && bqChild && !bqChild._fckblockquotemoveout )
\r
179 moveOutNodes.push( bqChild ) ;
\r
180 FCKDomTools.SetElementMarker( elementMarkers, bqChild, '_fckblockquotemoveout', true ) ;
\r
183 FCKDomTools.ClearAllMarkers( elementMarkers ) ;
\r
185 var movedNodes = [] ;
\r
186 var processedBlockquoteBlocks = [], elementMarkers = {} ;
\r
187 var noBlockLeft = function( bqBlock )
\r
189 for ( var i = 0 ; i < bqBlock.childNodes.length ; i++ )
\r
191 if ( FCKListsLib.BlockElements[ bqBlock.childNodes[i].nodeName.toLowerCase() ] )
\r
196 while ( moveOutNodes.length > 0 )
\r
198 var node = moveOutNodes.shift() ;
\r
199 var bqBlock = node.parentNode ;
\r
201 // If the node is located at the beginning or the end, just take it out without splitting.
\r
202 // Otherwise, split the blockquote node and move the paragraph in between the two blockquote nodes.
\r
203 if ( node == node.parentNode.firstChild )
\r
204 bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock ) ;
\r
205 else if ( node == node.parentNode.lastChild )
\r
206 bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock.nextSibling ) ;
\r
208 FCKDomTools.BreakParent( node, node.parentNode, range ) ;
\r
210 // Remember the blockquote node so we can clear it later (if it becomes empty).
\r
211 if ( !bqBlock._fckbqprocessed )
\r
213 processedBlockquoteBlocks.push( bqBlock ) ;
\r
214 FCKDomTools.SetElementMarker( elementMarkers, bqBlock, '_fckbqprocessed', true );
\r
217 movedNodes.push( node ) ;
\r
220 // Clear blockquote nodes that have become empty.
\r
221 for ( var i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )
\r
223 var bqBlock = processedBlockquoteBlocks[i] ;
\r
224 if ( noBlockLeft( bqBlock ) )
\r
225 FCKDomTools.RemoveNode( bqBlock ) ;
\r
227 FCKDomTools.ClearAllMarkers( elementMarkers ) ;
\r
229 if ( FCKConfig.EnterMode.IEquals( 'br' ) )
\r
231 while ( movedNodes.length )
\r
233 var node = movedNodes.shift() ;
\r
234 var firstTime = true ;
\r
235 if ( node.nodeName.IEquals( 'div' ) )
\r
237 var docFrag = FCKTools.GetElementDocument( node ).createDocumentFragment() ;
\r
238 var needBeginBr = firstTime && node.previousSibling &&
\r
239 !FCKListsLib.BlockBoundaries[node.previousSibling.nodeName.toLowerCase()] ;
\r
240 if ( firstTime && needBeginBr )
\r
241 docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;
\r
242 var needEndBr = node.nextSibling &&
\r
243 !FCKListsLib.BlockBoundaries[node.nextSibling.nodeName.toLowerCase()] ;
\r
244 while ( node.firstChild )
\r
245 docFrag.appendChild( node.removeChild( node.firstChild ) ) ;
\r
247 docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;
\r
248 node.parentNode.replaceChild( docFrag, node ) ;
\r
249 firstTime = false ;
\r
254 range.MoveToBookmark( bookmark ) ;
\r
258 FCK.Events.FireEvent( 'OnSelectionChange' ) ;
\r
261 GetState : function()
\r
263 // Disabled if not WYSIWYG.
\r
264 if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
\r
265 return FCK_TRISTATE_DISABLED ;
\r
267 var path = new FCKElementPath( FCKSelection.GetBoundaryParentElement( true ) ) ;
\r
268 var firstBlock = path.Block || path.BlockLimit ;
\r
270 if ( !firstBlock || firstBlock.nodeName.toLowerCase() == 'body' )
\r
271 return FCK_TRISTATE_OFF ;
\r
273 // See if the first block has a blockquote parent.
\r
274 for ( var i = 0 ; i < path.Elements.length ; i++ )
\r
276 if ( path.Elements[i].nodeName.IEquals( 'blockquote' ) )
\r
277 return FCK_TRISTATE_ON ;
\r
279 return FCK_TRISTATE_OFF ;
\r