1 | /*
|
---|
2 | * FCKeditor - The text editor for Internet - http://www.fckeditor.net
|
---|
3 | * Copyright (C) 2003-2009 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 |
|
---|
24 | var FCKBlockQuoteCommand = function()
|
---|
25 | {
|
---|
26 | }
|
---|
27 |
|
---|
28 | FCKBlockQuoteCommand.prototype =
|
---|
29 | {
|
---|
30 | Execute : function()
|
---|
31 | {
|
---|
32 | FCKUndo.SaveUndoStep() ;
|
---|
33 |
|
---|
34 | var state = this.GetState() ;
|
---|
35 |
|
---|
36 | var range = new FCKDomRange( FCK.EditorWindow ) ;
|
---|
37 | range.MoveToSelection() ;
|
---|
38 |
|
---|
39 | var bookmark = range.CreateBookmark() ;
|
---|
40 |
|
---|
41 | // Kludge for #1592: if the bookmark nodes are in the beginning of
|
---|
42 | // blockquote, then move them to the nearest block element in the
|
---|
43 | // blockquote.
|
---|
44 | if ( FCKBrowserInfo.IsIE )
|
---|
45 | {
|
---|
46 | var bStart = range.GetBookmarkNode( bookmark, true ) ;
|
---|
47 | var bEnd = range.GetBookmarkNode( bookmark, false ) ;
|
---|
48 |
|
---|
49 | var cursor ;
|
---|
50 |
|
---|
51 | if ( bStart
|
---|
52 | && bStart.parentNode.nodeName.IEquals( 'blockquote' )
|
---|
53 | && !bStart.previousSibling )
|
---|
54 | {
|
---|
55 | cursor = bStart ;
|
---|
56 | while ( ( cursor = cursor.nextSibling ) )
|
---|
57 | {
|
---|
58 | if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )
|
---|
59 | FCKDomTools.MoveNode( bStart, cursor, true ) ;
|
---|
60 | }
|
---|
61 | }
|
---|
62 |
|
---|
63 | if ( bEnd
|
---|
64 | && bEnd.parentNode.nodeName.IEquals( 'blockquote' )
|
---|
65 | && !bEnd.previousSibling )
|
---|
66 | {
|
---|
67 | cursor = bEnd ;
|
---|
68 | while ( ( cursor = cursor.nextSibling ) )
|
---|
69 | {
|
---|
70 | if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )
|
---|
71 | {
|
---|
72 | if ( cursor.firstChild == bStart )
|
---|
73 | FCKDomTools.InsertAfterNode( bStart, bEnd ) ;
|
---|
74 | else
|
---|
75 | FCKDomTools.MoveNode( bEnd, cursor, true ) ;
|
---|
76 | }
|
---|
77 | }
|
---|
78 | }
|
---|
79 | }
|
---|
80 |
|
---|
81 | var iterator = new FCKDomRangeIterator( range ) ;
|
---|
82 | var block ;
|
---|
83 |
|
---|
84 | if ( state == FCK_TRISTATE_OFF )
|
---|
85 | {
|
---|
86 | var paragraphs = [] ;
|
---|
87 | while ( ( block = iterator.GetNextParagraph() ) )
|
---|
88 | paragraphs.push( block ) ;
|
---|
89 |
|
---|
90 | // If no paragraphs, create one from the current selection position.
|
---|
91 | if ( paragraphs.length < 1 )
|
---|
92 | {
|
---|
93 | para = range.Window.document.createElement( FCKConfig.EnterMode.IEquals( 'p' ) ? 'p' : 'div' ) ;
|
---|
94 | range.InsertNode( para ) ;
|
---|
95 | para.appendChild( range.Window.document.createTextNode( '\ufeff' ) ) ;
|
---|
96 | range.MoveToBookmark( bookmark ) ;
|
---|
97 | range.MoveToNodeContents( para ) ;
|
---|
98 | range.Collapse( true ) ;
|
---|
99 | bookmark = range.CreateBookmark() ;
|
---|
100 | paragraphs.push( para ) ;
|
---|
101 | }
|
---|
102 |
|
---|
103 | // Make sure all paragraphs have the same parent.
|
---|
104 | var commonParent = paragraphs[0].parentNode ;
|
---|
105 | var tmp = [] ;
|
---|
106 | for ( var i = 0 ; i < paragraphs.length ; i++ )
|
---|
107 | {
|
---|
108 | block = paragraphs[i] ;
|
---|
109 | commonParent = FCKDomTools.GetCommonParents( block.parentNode, commonParent ).pop() ;
|
---|
110 | }
|
---|
111 |
|
---|
112 | // The common parent must not be the following tags: table, tbody, tr, ol, ul.
|
---|
113 | while ( commonParent.nodeName.IEquals( 'table', 'tbody', 'tr', 'ol', 'ul' ) )
|
---|
114 | commonParent = commonParent.parentNode ;
|
---|
115 |
|
---|
116 | // Reconstruct the block list to be processed such that all resulting blocks
|
---|
117 | // satisfy parentNode == commonParent.
|
---|
118 | var lastBlock = null ;
|
---|
119 | while ( paragraphs.length > 0 )
|
---|
120 | {
|
---|
121 | block = paragraphs.shift() ;
|
---|
122 | while ( block.parentNode != commonParent )
|
---|
123 | block = block.parentNode ;
|
---|
124 | if ( block != lastBlock )
|
---|
125 | tmp.push( block ) ;
|
---|
126 | lastBlock = block ;
|
---|
127 | }
|
---|
128 |
|
---|
129 | // If any of the selected blocks is a blockquote, remove it to prevent nested blockquotes.
|
---|
130 | while ( tmp.length > 0 )
|
---|
131 | {
|
---|
132 | block = tmp.shift() ;
|
---|
133 | if ( block.nodeName.IEquals( 'blockquote' ) )
|
---|
134 | {
|
---|
135 | var docFrag = FCKTools.GetElementDocument( block ).createDocumentFragment() ;
|
---|
136 | while ( block.firstChild )
|
---|
137 | {
|
---|
138 | docFrag.appendChild( block.removeChild( block.firstChild ) ) ;
|
---|
139 | paragraphs.push( docFrag.lastChild ) ;
|
---|
140 | }
|
---|
141 | block.parentNode.replaceChild( docFrag, block ) ;
|
---|
142 | }
|
---|
143 | else
|
---|
144 | paragraphs.push( block ) ;
|
---|
145 | }
|
---|
146 |
|
---|
147 | // Now we have all the blocks to be included in a new blockquote node.
|
---|
148 | var bqBlock = range.Window.document.createElement( 'blockquote' ) ;
|
---|
149 | commonParent.insertBefore( bqBlock, paragraphs[0] ) ;
|
---|
150 | while ( paragraphs.length > 0 )
|
---|
151 | {
|
---|
152 | block = paragraphs.shift() ;
|
---|
153 | bqBlock.appendChild( block ) ;
|
---|
154 | }
|
---|
155 | }
|
---|
156 | else if ( state == FCK_TRISTATE_ON )
|
---|
157 | {
|
---|
158 | var moveOutNodes = [] ;
|
---|
159 | var elementMarkers = {} ;
|
---|
160 | while ( ( block = iterator.GetNextParagraph() ) )
|
---|
161 | {
|
---|
162 | var bqParent = null ;
|
---|
163 | var bqChild = null ;
|
---|
164 | while ( block.parentNode )
|
---|
165 | {
|
---|
166 | if ( block.parentNode.nodeName.IEquals( 'blockquote' ) )
|
---|
167 | {
|
---|
168 | bqParent = block.parentNode ;
|
---|
169 | bqChild = block ;
|
---|
170 | break ;
|
---|
171 | }
|
---|
172 | block = block.parentNode ;
|
---|
173 | }
|
---|
174 |
|
---|
175 | // Remember the blocks that were recorded down in the moveOutNodes array
|
---|
176 | // to prevent duplicates.
|
---|
177 | if ( bqParent && bqChild && !bqChild._fckblockquotemoveout )
|
---|
178 | {
|
---|
179 | moveOutNodes.push( bqChild ) ;
|
---|
180 | FCKDomTools.SetElementMarker( elementMarkers, bqChild, '_fckblockquotemoveout', true ) ;
|
---|
181 | }
|
---|
182 | }
|
---|
183 | FCKDomTools.ClearAllMarkers( elementMarkers ) ;
|
---|
184 |
|
---|
185 | var movedNodes = [] ;
|
---|
186 | var processedBlockquoteBlocks = [], elementMarkers = {} ;
|
---|
187 | var noBlockLeft = function( bqBlock )
|
---|
188 | {
|
---|
189 | for ( var i = 0 ; i < bqBlock.childNodes.length ; i++ )
|
---|
190 | {
|
---|
191 | if ( FCKListsLib.BlockElements[ bqBlock.childNodes[i].nodeName.toLowerCase() ] )
|
---|
192 | return false ;
|
---|
193 | }
|
---|
194 | return true ;
|
---|
195 | } ;
|
---|
196 | while ( moveOutNodes.length > 0 )
|
---|
197 | {
|
---|
198 | var node = moveOutNodes.shift() ;
|
---|
199 | var bqBlock = node.parentNode ;
|
---|
200 |
|
---|
201 | // If the node is located at the beginning or the end, just take it out without splitting.
|
---|
202 | // Otherwise, split the blockquote node and move the paragraph in between the two blockquote nodes.
|
---|
203 | if ( node == node.parentNode.firstChild )
|
---|
204 | bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock ) ;
|
---|
205 | else if ( node == node.parentNode.lastChild )
|
---|
206 | bqBlock.parentNode.insertBefore( bqBlock.removeChild( node ), bqBlock.nextSibling ) ;
|
---|
207 | else
|
---|
208 | FCKDomTools.BreakParent( node, node.parentNode, range ) ;
|
---|
209 |
|
---|
210 | // Remember the blockquote node so we can clear it later (if it becomes empty).
|
---|
211 | if ( !bqBlock._fckbqprocessed )
|
---|
212 | {
|
---|
213 | processedBlockquoteBlocks.push( bqBlock ) ;
|
---|
214 | FCKDomTools.SetElementMarker( elementMarkers, bqBlock, '_fckbqprocessed', true );
|
---|
215 | }
|
---|
216 |
|
---|
217 | movedNodes.push( node ) ;
|
---|
218 | }
|
---|
219 |
|
---|
220 | // Clear blockquote nodes that have become empty.
|
---|
221 | for ( var i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )
|
---|
222 | {
|
---|
223 | var bqBlock = processedBlockquoteBlocks[i] ;
|
---|
224 | if ( noBlockLeft( bqBlock ) )
|
---|
225 | FCKDomTools.RemoveNode( bqBlock ) ;
|
---|
226 | }
|
---|
227 | FCKDomTools.ClearAllMarkers( elementMarkers ) ;
|
---|
228 |
|
---|
229 | if ( FCKConfig.EnterMode.IEquals( 'br' ) )
|
---|
230 | {
|
---|
231 | while ( movedNodes.length )
|
---|
232 | {
|
---|
233 | var node = movedNodes.shift() ;
|
---|
234 | var firstTime = true ;
|
---|
235 | if ( node.nodeName.IEquals( 'div' ) )
|
---|
236 | {
|
---|
237 | var docFrag = FCKTools.GetElementDocument( node ).createDocumentFragment() ;
|
---|
238 | var needBeginBr = firstTime && node.previousSibling &&
|
---|
239 | !FCKListsLib.BlockBoundaries[node.previousSibling.nodeName.toLowerCase()] ;
|
---|
240 | if ( firstTime && needBeginBr )
|
---|
241 | docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;
|
---|
242 | var needEndBr = node.nextSibling &&
|
---|
243 | !FCKListsLib.BlockBoundaries[node.nextSibling.nodeName.toLowerCase()] ;
|
---|
244 | while ( node.firstChild )
|
---|
245 | docFrag.appendChild( node.removeChild( node.firstChild ) ) ;
|
---|
246 | if ( needEndBr )
|
---|
247 | docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;
|
---|
248 | node.parentNode.replaceChild( docFrag, node ) ;
|
---|
249 | firstTime = false ;
|
---|
250 | }
|
---|
251 | }
|
---|
252 | }
|
---|
253 | }
|
---|
254 | range.MoveToBookmark( bookmark ) ;
|
---|
255 | range.Select() ;
|
---|
256 |
|
---|
257 | FCK.Focus() ;
|
---|
258 | FCK.Events.FireEvent( 'OnSelectionChange' ) ;
|
---|
259 | },
|
---|
260 |
|
---|
261 | GetState : function()
|
---|
262 | {
|
---|
263 | // Disabled if not WYSIWYG.
|
---|
264 | if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
|
---|
265 | return FCK_TRISTATE_DISABLED ;
|
---|
266 |
|
---|
267 | var path = new FCKElementPath( FCKSelection.GetBoundaryParentElement( true ) ) ;
|
---|
268 | var firstBlock = path.Block || path.BlockLimit ;
|
---|
269 |
|
---|
270 | if ( !firstBlock || firstBlock.nodeName.toLowerCase() == 'body' )
|
---|
271 | return FCK_TRISTATE_OFF ;
|
---|
272 |
|
---|
273 | // See if the first block has a blockquote parent.
|
---|
274 | for ( var i = 0 ; i < path.Elements.length ; i++ )
|
---|
275 | {
|
---|
276 | if ( path.Elements[i].nodeName.IEquals( 'blockquote' ) )
|
---|
277 | return FCK_TRISTATE_ON ;
|
---|
278 | }
|
---|
279 | return FCK_TRISTATE_OFF ;
|
---|
280 | }
|
---|
281 | } ;
|
---|