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