import rt 3.8.7
[freeside.git] / rt / share / html / NoAuth / RichText / FCKeditor / editor / _source / commandclasses / fckblockquotecommand.js
1 /*\r
2  * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
3  * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
4  *\r
5  * == BEGIN LICENSE ==\r
6  *\r
7  * Licensed under the terms of any of the following licenses at your\r
8  * choice:\r
9  *\r
10  *  - GNU General Public License Version 2 or later (the "GPL")\r
11  *    http://www.gnu.org/licenses/gpl.html\r
12  *\r
13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
14  *    http://www.gnu.org/licenses/lgpl.html\r
15  *\r
16  *  - Mozilla Public License Version 1.1 or later (the "MPL")\r
17  *    http://www.mozilla.org/MPL/MPL-1.1.html\r
18  *\r
19  * == END LICENSE ==\r
20  *\r
21  * FCKBlockQuoteCommand Class: adds or removes blockquote tags.\r
22  */\r
23 \r
24 var FCKBlockQuoteCommand = function()\r
25 {\r
26 }\r
27 \r
28 FCKBlockQuoteCommand.prototype =\r
29 {\r
30         Execute : function()\r
31         {\r
32                 FCKUndo.SaveUndoStep() ;\r
33 \r
34                 var state = this.GetState() ;\r
35 \r
36                 var range = new FCKDomRange( FCK.EditorWindow ) ;\r
37                 range.MoveToSelection() ;\r
38 \r
39                 var bookmark = range.CreateBookmark() ;\r
40 \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
43                 // blockquote.\r
44                 if ( FCKBrowserInfo.IsIE )\r
45                 {\r
46                         var bStart      = range.GetBookmarkNode( bookmark, true ) ;\r
47                         var bEnd        = range.GetBookmarkNode( bookmark, false ) ;\r
48 \r
49                         var cursor ;\r
50 \r
51                         if ( bStart\r
52                                         && bStart.parentNode.nodeName.IEquals( 'blockquote' )\r
53                                         && !bStart.previousSibling )\r
54                         {\r
55                                 cursor = bStart ;\r
56                                 while ( ( cursor = cursor.nextSibling ) )\r
57                                 {\r
58                                         if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )\r
59                                                 FCKDomTools.MoveNode( bStart, cursor, true ) ;\r
60                                 }\r
61                         }\r
62 \r
63                         if ( bEnd\r
64                                         && bEnd.parentNode.nodeName.IEquals( 'blockquote' )\r
65                                         && !bEnd.previousSibling )\r
66                         {\r
67                                 cursor = bEnd ;\r
68                                 while ( ( cursor = cursor.nextSibling ) )\r
69                                 {\r
70                                         if ( FCKListsLib.BlockElements[ cursor.nodeName.toLowerCase() ] )\r
71                                         {\r
72                                                 if ( cursor.firstChild == bStart )\r
73                                                         FCKDomTools.InsertAfterNode( bStart, bEnd ) ;\r
74                                                 else\r
75                                                         FCKDomTools.MoveNode( bEnd, cursor, true ) ;\r
76                                         }\r
77                                 }\r
78                         }\r
79                 }\r
80 \r
81                 var iterator = new FCKDomRangeIterator( range ) ;\r
82                 var block ;\r
83 \r
84                 if ( state == FCK_TRISTATE_OFF )\r
85                 {\r
86                         var paragraphs = [] ;\r
87                         while ( ( block = iterator.GetNextParagraph() ) )\r
88                                 paragraphs.push( block ) ;\r
89 \r
90                         // If no paragraphs, create one from the current selection position.\r
91                         if ( paragraphs.length < 1 )\r
92                         {\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
101                         }\r
102 \r
103                         // Make sure all paragraphs have the same parent.\r
104                         var commonParent = paragraphs[0].parentNode ;\r
105                         var tmp = [] ;\r
106                         for ( var i = 0 ; i < paragraphs.length ; i++ )\r
107                         {\r
108                                 block = paragraphs[i] ;\r
109                                 commonParent = FCKDomTools.GetCommonParents( block.parentNode, commonParent ).pop() ;\r
110                         }\r
111 \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
115 \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
120                         {\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
127                         }\r
128 \r
129                         // If any of the selected blocks is a blockquote, remove it to prevent nested blockquotes.\r
130                         while ( tmp.length > 0 )\r
131                         {\r
132                                 block = tmp.shift() ;\r
133                                 if ( block.nodeName.IEquals( 'blockquote' ) )\r
134                                 {\r
135                                         var docFrag = FCKTools.GetElementDocument( block ).createDocumentFragment() ;\r
136                                         while ( block.firstChild )\r
137                                         {\r
138                                                 docFrag.appendChild( block.removeChild( block.firstChild ) ) ;\r
139                                                 paragraphs.push( docFrag.lastChild ) ;\r
140                                         }\r
141                                         block.parentNode.replaceChild( docFrag, block ) ;\r
142                                 }\r
143                                 else\r
144                                         paragraphs.push( block ) ;\r
145                         }\r
146 \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
151                         {\r
152                                 block = paragraphs.shift() ;\r
153                                 bqBlock.appendChild( block ) ;\r
154                         }\r
155                 }\r
156                 else if ( state == FCK_TRISTATE_ON )\r
157                 {\r
158                         var moveOutNodes = [] ;\r
159                         var elementMarkers = {} ;\r
160                         while ( ( block = iterator.GetNextParagraph() ) )\r
161                         {\r
162                                 var bqParent = null ;\r
163                                 var bqChild = null ;\r
164                                 while ( block.parentNode )\r
165                                 {\r
166                                         if ( block.parentNode.nodeName.IEquals( 'blockquote' ) )\r
167                                         {\r
168                                                 bqParent = block.parentNode ;\r
169                                                 bqChild = block ;\r
170                                                 break ;\r
171                                         }\r
172                                         block = block.parentNode ;\r
173                                 }\r
174 \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
178                                 {\r
179                                         moveOutNodes.push( bqChild ) ;\r
180                                         FCKDomTools.SetElementMarker( elementMarkers, bqChild, '_fckblockquotemoveout', true ) ;\r
181                                 }\r
182                         }\r
183                         FCKDomTools.ClearAllMarkers( elementMarkers ) ;\r
184 \r
185                         var movedNodes = [] ;\r
186                         var processedBlockquoteBlocks = [], elementMarkers = {} ;\r
187                         var noBlockLeft = function( bqBlock )\r
188                         {\r
189                                 for ( var i = 0 ; i < bqBlock.childNodes.length ; i++ )\r
190                                 {\r
191                                         if ( FCKListsLib.BlockElements[ bqBlock.childNodes[i].nodeName.toLowerCase() ] )\r
192                                                 return false ;\r
193                                 }\r
194                                 return true ;\r
195                         } ;\r
196                         while ( moveOutNodes.length > 0 )\r
197                         {\r
198                                 var node = moveOutNodes.shift() ;\r
199                                 var bqBlock = node.parentNode ;\r
200 \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
207                                 else\r
208                                         FCKDomTools.BreakParent( node, node.parentNode, range ) ;\r
209 \r
210                                 // Remember the blockquote node so we can clear it later (if it becomes empty).\r
211                                 if ( !bqBlock._fckbqprocessed )\r
212                                 {\r
213                                         processedBlockquoteBlocks.push( bqBlock ) ;\r
214                                         FCKDomTools.SetElementMarker( elementMarkers, bqBlock, '_fckbqprocessed', true );\r
215                                 }\r
216 \r
217                                 movedNodes.push( node ) ;\r
218                         }\r
219 \r
220                         // Clear blockquote nodes that have become empty.\r
221                         for ( var i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )\r
222                         {\r
223                                 var bqBlock = processedBlockquoteBlocks[i] ;\r
224                                 if ( noBlockLeft( bqBlock ) )\r
225                                         FCKDomTools.RemoveNode( bqBlock ) ;\r
226                         }\r
227                         FCKDomTools.ClearAllMarkers( elementMarkers ) ;\r
228 \r
229                         if ( FCKConfig.EnterMode.IEquals( 'br' ) )\r
230                         {\r
231                                 while ( movedNodes.length )\r
232                                 {\r
233                                         var node = movedNodes.shift() ;\r
234                                         var firstTime = true ;\r
235                                         if ( node.nodeName.IEquals( 'div' ) )\r
236                                         {\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
246                                                 if ( needEndBr )\r
247                                                         docFrag.appendChild( FCKTools.GetElementDocument( node ).createElement( 'br' ) ) ;\r
248                                                 node.parentNode.replaceChild( docFrag, node ) ;\r
249                                                 firstTime = false ;\r
250                                         }\r
251                                 }\r
252                         }\r
253                 }\r
254                 range.MoveToBookmark( bookmark ) ;\r
255                 range.Select() ;\r
256 \r
257                 FCK.Focus() ;\r
258                 FCK.Events.FireEvent( 'OnSelectionChange' ) ;\r
259         },\r
260 \r
261         GetState : function()\r
262         {\r
263                 // Disabled if not WYSIWYG.\r
264                 if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )\r
265                         return FCK_TRISTATE_DISABLED ;\r
266 \r
267                 var path = new FCKElementPath( FCKSelection.GetBoundaryParentElement( true ) ) ;\r
268                 var firstBlock = path.Block || path.BlockLimit ;\r
269 \r
270                 if ( !firstBlock || firstBlock.nodeName.toLowerCase() == 'body' )\r
271                         return FCK_TRISTATE_OFF ;\r
272 \r
273                 // See if the first block has a blockquote parent.\r
274                 for ( var i = 0 ; i < path.Elements.length ; i++ )\r
275                 {\r
276                         if ( path.Elements[i].nodeName.IEquals( 'blockquote' ) )\r
277                                 return FCK_TRISTATE_ON ;\r
278                 }\r
279                 return FCK_TRISTATE_OFF ;\r
280         }\r
281 } ;\r