[1575] | 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 | } ;
|
---|