[ Index ] |
|
Code source de FCKeditor 2.4 |
1 /* 2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net 3 * Copyright (C) 2003-2007 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 * File Name: fckenterkey.js 22 * Controls the [Enter] keystroke behavior in a document. 23 * 24 * File Authors: 25 * Frederico Caldeira Knabben (www.fckeditor.net) 26 */ 27 28 /* 29 * Constructor. 30 * @targetDocument : the target document. 31 * @enterMode : the behavior for the <Enter> keystroke. 32 * May be "p", "div", "br". Default is "p". 33 * @shiftEnterMode : the behavior for the <Shift>+<Enter> keystroke. 34 * May be "p", "div", "br". Defaults to "br". 35 */ 36 var FCKEnterKey = function( targetWindow, enterMode, shiftEnterMode ) 37 { 38 this.Window = targetWindow ; 39 this.EnterMode = enterMode || 'p' ; 40 this.ShiftEnterMode = shiftEnterMode || 'br' ; 41 42 // Setup the Keystroke Handler. 43 var oKeystrokeHandler = new FCKKeystrokeHandler( false ) ; 44 oKeystrokeHandler._EnterKey = this ; 45 oKeystrokeHandler.OnKeystroke = FCKEnterKey_OnKeystroke ; 46 47 oKeystrokeHandler.SetKeystrokes( [ 48 [ 13 , 'Enter' ], 49 [ SHIFT + 13, 'ShiftEnter' ], 50 [ 8 , 'Backspace' ], 51 [ 46 , 'Delete' ] 52 ] ) ; 53 54 oKeystrokeHandler.AttachToElement( targetWindow.document ) ; 55 } 56 57 58 function FCKEnterKey_OnKeystroke( keyCombination, keystrokeValue ) 59 { 60 var oEnterKey = this._EnterKey ; 61 62 try 63 { 64 switch ( keystrokeValue ) 65 { 66 case 'Enter' : 67 return oEnterKey.DoEnter() ; 68 break ; 69 70 case 'ShiftEnter' : 71 return oEnterKey.DoShiftEnter() ; 72 break ; 73 74 case 'Backspace' : 75 return oEnterKey.DoBackspace() ; 76 break ; 77 78 case 'Delete' : 79 return oEnterKey.DoDelete() ; 80 } 81 } 82 catch (e) 83 { 84 // If for any reason we are not able to handle it, go 85 // ahead with the browser default behavior. 86 } 87 88 return false ; 89 } 90 91 /* 92 * Executes the <Enter> key behavior. 93 */ 94 FCKEnterKey.prototype.DoEnter = function( mode, hasShift ) 95 { 96 this._HasShift = ( hasShift === true ) ; 97 98 var sMode = mode || this.EnterMode ; 99 100 if ( sMode == 'br' ) 101 return this._ExecuteEnterBr() ; 102 else 103 return this._ExecuteEnterBlock( sMode ) ; 104 } 105 106 /* 107 * Executes the <Shift>+<Enter> key behavior. 108 */ 109 FCKEnterKey.prototype.DoShiftEnter = function() 110 { 111 return this.DoEnter( this.ShiftEnterMode, true ) ; 112 } 113 114 /* 115 * Executes the <Backspace> key behavior. 116 */ 117 FCKEnterKey.prototype.DoBackspace = function() 118 { 119 var bCustom = false ; 120 121 // Get the current selection. 122 var oRange = new FCKDomRange( this.Window ) ; 123 oRange.MoveToSelection() ; 124 125 if ( !oRange.CheckIsCollapsed() ) 126 return false ; 127 128 129 var oStartBlock = oRange.StartBlock ; 130 var oEndBlock = oRange.EndBlock ; 131 132 // The selection boundaries must be in the same "block limit" element 133 if ( oRange.StartBlockLimit == oRange.EndBlockLimit && oStartBlock && oEndBlock ) 134 { 135 if ( !oRange.CheckIsCollapsed() ) 136 { 137 var bEndOfBlock = oRange.CheckEndOfBlock() ; 138 139 oRange.DeleteContents() ; 140 141 if ( oStartBlock != oEndBlock ) 142 { 143 oRange.SetStart(oEndBlock,1) ; 144 oRange.SetEnd(oEndBlock,1) ; 145 146 // if ( bEndOfBlock ) 147 // oEndBlock.parentNode.removeChild( oEndBlock ) ; 148 } 149 150 oRange.Select() ; 151 152 bCustom = ( oStartBlock == oEndBlock ) ; 153 } 154 155 if ( oRange.CheckStartOfBlock() ) 156 { 157 var oCurrentBlock = oRange.StartBlock ; 158 159 var ePrevious = FCKDomTools.GetPreviousSourceElement( oCurrentBlock, true, [ 'BODY', oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ; 160 161 bCustom = this._ExecuteBackspace( oRange, ePrevious, oCurrentBlock ) ; 162 } 163 else if ( FCKBrowserInfo.IsGecko ) 164 { 165 // Firefox looses the selection when executing CheckStartOfBlock, so we must reselect. 166 oRange.Select() ; 167 } 168 } 169 170 oRange.Release() ; 171 return bCustom ; 172 } 173 174 FCKEnterKey.prototype._ExecuteBackspace = function( range, previous, currentBlock ) 175 { 176 var bCustom = false ; 177 178 // We could be in a nested LI. 179 if ( !previous && currentBlock.nodeName.IEquals( 'LI' ) && currentBlock.parentNode.parentNode.nodeName.IEquals( 'LI' ) ) 180 { 181 previous = currentBlock.parentNode.parentNode ; 182 currentBlock = FCKListHandler.OutdentListItem( currentBlock ) ; 183 } 184 185 if ( previous && previous.nodeName.IEquals( 'LI' ) ) 186 { 187 var oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ; 188 189 while ( oNestedList ) 190 { 191 previous = FCKDomTools.GetLastChild( oNestedList, 'LI' ) ; 192 oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ; 193 } 194 } 195 196 if ( previous && currentBlock ) 197 { 198 // If we are in a LI, and the previous block is not an LI, we must 199 if ( currentBlock.nodeName.IEquals( 'LI' ) && !previous.nodeName.IEquals( 'LI' ) ) 200 currentBlock = FCKListHandler.OutdentListItem( currentBlock ) ; 201 202 // Take a reference to the parent for post processing cleanup. 203 var oCurrentParent = currentBlock.parentNode ; 204 205 if ( previous.nodeName.IEquals( 'TABLE', 'HR' ) ) 206 { 207 FCKDomTools.RemoveNode( previous ) ; 208 bCustom = true ; 209 } 210 else 211 { 212 // Remove the current block. 213 FCKDomTools.RemoveNode( currentBlock ) ; 214 215 // Remove any empty tag left by the block removal. 216 while ( oCurrentParent.innerHTML.Trim().length == 0 ) 217 { 218 var oParent = oCurrentParent.parentNode ; 219 oParent.removeChild( oCurrentParent ) ; 220 oCurrentParent = oParent ; 221 } 222 223 // Cleanup the previous and the current elements. 224 FCKDomTools.TrimNode( currentBlock ) ; 225 FCKDomTools.TrimNode( previous ) ; 226 227 // Append a space to the previous. 228 // Maybe it is not always desirable... 229 // previous.appendChild( this.Window.document.createTextNode( ' ' ) ) ; 230 231 // Set the range to the end of the previous element and bookmark it. 232 range.SetStart( previous, 2 ) ; 233 range.Collapse( true ) ; 234 var oBookmark = range.CreateBookmark() ; 235 236 // Move the contents of the block to the previous element and delete it. 237 FCKDomTools.MoveChildren( currentBlock, previous ) ; 238 239 // Place the selection at the bookmark. 240 range.MoveToBookmark( oBookmark ) ; 241 range.Select() ; 242 243 bCustom = true ; 244 } 245 } 246 247 return bCustom ; 248 } 249 250 /* 251 * Executes the <Delete> key behavior. 252 */ 253 FCKEnterKey.prototype.DoDelete = function() 254 { 255 // The <Delete> has the same effect as the <Backspace>, so we have the same 256 // results if we just move to the next block and apply the same <Backspace> logic. 257 258 var bCustom = false ; 259 260 // Get the current selection. 261 var oRange = new FCKDomRange( this.Window ) ; 262 oRange.MoveToSelection() ; 263 264 // There is just one special case for collapsed selections at the end of a block. 265 if ( oRange.CheckIsCollapsed() && oRange.CheckEndOfBlock( FCKBrowserInfo.IsGecko ) ) 266 { 267 var oCurrentBlock = oRange.StartBlock ; 268 269 var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ; 270 271 bCustom = this._ExecuteBackspace( oRange, oCurrentBlock, eNext ) ; 272 } 273 274 oRange.Release() ; 275 return bCustom ; 276 } 277 278 FCKEnterKey.prototype._ExecuteEnterBlock = function( blockTag ) 279 { 280 // Get the current selection. 281 var oRange = new FCKDomRange( this.Window ) ; 282 oRange.MoveToSelection() ; 283 284 // The selection boundaries must be in the same "block limit" element. 285 if ( oRange.StartBlockLimit == oRange.EndBlockLimit ) 286 { 287 // If the StartBlock or EndBlock are not available (for text without a 288 // block tag), we must fix them, by moving the text to a block. 289 if ( !oRange.StartBlock ) 290 this._FixBlock( oRange, true, blockTag ) ; 291 292 if ( !oRange.EndBlock ) 293 this._FixBlock( oRange, false, blockTag ) ; 294 295 // Get the current blocks. 296 var eStartBlock = oRange.StartBlock ; 297 var eEndBlock = oRange.EndBlock ; 298 299 // Delete the current selection. 300 if ( !oRange.CheckIsEmpty() ) 301 oRange.DeleteContents() ; 302 303 // If the selection boundaries are in the same block element 304 if ( eStartBlock == eEndBlock ) 305 { 306 var eNewBlock ; 307 308 var bIsStartOfBlock = oRange.CheckStartOfBlock() ; 309 var bIsEndOfBlock = oRange.CheckEndOfBlock() ; 310 311 if ( bIsStartOfBlock && !bIsEndOfBlock ) 312 { 313 eNewBlock = eStartBlock.cloneNode(false) ; 314 315 if ( FCKBrowserInfo.IsGeckoLike ) 316 eNewBlock.innerHTML = GECKO_BOGUS ; 317 318 // Place the new block before the current block element. 319 eStartBlock.parentNode.insertBefore( eNewBlock, eStartBlock ) ; 320 321 // This is tricky, but to make the new block visible correctly 322 // we must select it. 323 if ( FCKBrowserInfo.IsIE ) 324 { 325 // Move the selection to the new block. 326 oRange.MoveToNodeContents( eNewBlock ) ; 327 328 oRange.Select() ; 329 } 330 331 // Move the selection to the new block. 332 oRange.MoveToElementStart( eStartBlock ) ; 333 } 334 else 335 { 336 // Check if the selection is at the end of the block. 337 if ( bIsEndOfBlock ) 338 { 339 var sStartBlockTag = eStartBlock.tagName.toUpperCase() ; 340 341 // If the entire block is selected, and we are in a LI, let's decrease its indentation. 342 if ( bIsStartOfBlock && sStartBlockTag == 'LI' ) 343 { 344 var eOutdented = FCKListHandler.OutdentListItem( eStartBlock ) ; 345 oRange.MoveToElementStart( eOutdented ) ; 346 } 347 else 348 { 349 // If is a header tag, create a new block element. 350 if ( (/^H[1-6]$/).test( sStartBlockTag ) ) 351 eNewBlock = this.Window.document.createElement( blockTag ) ; 352 // Otherwise, duplicate the current block. 353 else 354 eNewBlock = eStartBlock.cloneNode(false) ; 355 356 if ( FCKBrowserInfo.IsGeckoLike ) 357 { 358 eNewBlock.innerHTML = GECKO_BOGUS ; 359 360 // If the entire block is selected, let's add a bogus in the start block. 361 if ( bIsStartOfBlock ) 362 eStartBlock.innerHTML = GECKO_BOGUS ; 363 } 364 } 365 } 366 else 367 { 368 // Extract the contents of the block from the selection point to the end of its contents. 369 oRange.SetEnd( eStartBlock, 2 ) ; 370 var eDocFrag = oRange.ExtractContents() ; 371 372 // Duplicate the block element after it. 373 eNewBlock = eStartBlock.cloneNode(false) ; 374 375 // It could be that we are in a LI with a child UL/OL. Insert a bogus to give us space to type. 376 FCKDomTools.TrimNode( eDocFrag.RootNode ) ; 377 if ( eDocFrag.RootNode.firstChild.nodeType == 1 && eDocFrag.RootNode.firstChild.tagName.toUpperCase().Equals( 'UL', 'OL' ) ) 378 eNewBlock.innerHTML = GECKO_BOGUS ; 379 380 // Place the extracted contents in the duplicated block. 381 eDocFrag.AppendTo( eNewBlock ) ; 382 383 if ( FCKBrowserInfo.IsGecko ) 384 { 385 // In Gecko, the last child node must be a bogus <br>. 386 var eLastChild = FCKDomTools.GetLastChild( eNewBlock ) ; 387 388 if ( !eLastChild || eLastChild.nodeName.toLowerCase() != 'br' || eLastChild.getAttribute( 'type', 2 ) != '_moz' ) 389 eNewBlock.appendChild( FCKTools.CreateBogusBR( this.Window.document ) ) ; 390 } 391 } 392 393 if ( eNewBlock ) 394 { 395 FCKDomTools.InsertAfterNode( eStartBlock, eNewBlock ) ; 396 397 // Move the selection to the new block. 398 oRange.MoveToElementStart( eNewBlock ) ; 399 400 if ( FCKBrowserInfo.IsGecko ) 401 eNewBlock.scrollIntoView( false ) ; 402 } 403 } 404 } 405 else 406 { 407 // Move the selection to the end block. 408 oRange.MoveToElementStart( eEndBlock ) ; 409 } 410 411 oRange.Select() ; 412 } 413 414 // Release the resources used by the range. 415 oRange.Release() ; 416 417 return true ; 418 } 419 420 FCKEnterKey.prototype._ExecuteEnterBr = function( blockTag ) 421 { 422 // Get the current selection. 423 var oRange = new FCKDomRange( this.Window ) ; 424 oRange.MoveToSelection() ; 425 426 // The selection boundaries must be in the same "block limit" element. 427 if ( oRange.StartBlockLimit == oRange.EndBlockLimit ) 428 { 429 oRange.DeleteContents() ; 430 431 // Get the new selection (it is collapsed at this point). 432 oRange.MoveToSelection() ; 433 434 var bIsStartOfBlock = oRange.CheckStartOfBlock() ; 435 var bIsEndOfBlock = oRange.CheckEndOfBlock() ; 436 437 var sStartBlockTag = oRange.StartBlock ? oRange.StartBlock.tagName.toUpperCase() : '' ; 438 439 var bHasShift = this._HasShift ; 440 441 if ( !bHasShift && sStartBlockTag == 'LI' ) 442 return this._ExecuteEnterBlock( null ) ; 443 444 // If we are at the end of a header block. 445 if ( !bHasShift && bIsEndOfBlock && (/^H[1-6]$/).test( sStartBlockTag ) ) 446 { 447 FCKDebug.Output( 'BR - Header' ) ; 448 449 // Insert a BR after the current paragraph. 450 FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createElement( 'br' ) ) ; 451 452 // The space is required by Gecko only to make the cursor blink. 453 if ( FCKBrowserInfo.IsGecko ) 454 FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createTextNode( '' ) ) ; 455 456 // IE and Gecko have different behaviors regarding the position. 457 oRange.SetStart( oRange.StartBlock.nextSibling, FCKBrowserInfo.IsIE ? 3 : 1 ) ; 458 } 459 else 460 { 461 FCKDebug.Output( 'BR - No Header' ) ; 462 463 var eBr = this.Window.document.createElement( 'br' ) ; 464 465 oRange.InsertNode( eBr ) ; 466 467 // The space is required by Gecko only to make the cursor blink. 468 if ( FCKBrowserInfo.IsGecko ) 469 FCKDomTools.InsertAfterNode( eBr, this.Window.document.createTextNode( '' ) ) ; 470 471 // If we are at the end of a block, we must be sure the bogus node is available in that block. 472 if ( bIsEndOfBlock && FCKBrowserInfo.IsGecko ) 473 { 474 var eLastBr = FCKDomTools.GetLastChild( eBr.parentNode, 'BR' ) ; 475 476 if ( eLastBr && eLastBr.getAttribute( 'type', 2 ) != '_moz' ) 477 eBr.parentNode.appendChild( FCKTools.CreateBogusBR( this.Window.document ) ) ; 478 } 479 480 if ( FCKBrowserInfo.IsIE ) 481 oRange.SetStart( eBr, 4 ) ; 482 else 483 oRange.SetStart( eBr.nextSibling, 1 ) ; 484 485 } 486 487 // This collapse guarantees the cursor will be blinking. 488 oRange.Collapse( true ) ; 489 490 oRange.Select() ; 491 } 492 493 // Release the resources used by the range. 494 oRange.Release() ; 495 496 return true ; 497 } 498 499 // Transform a block without a block tag in a valid block (orphan text in the body or td, usually). 500 FCKEnterKey.prototype._FixBlock = function( range, isStart, blockTag ) 501 { 502 // Bookmark the range so we can restore it later. 503 var oBookmark = range.CreateBookmark() ; 504 505 // Collapse the range to the requested ending boundary. 506 range.Collapse( isStart ) ; 507 508 // Expands it to the block contents. 509 range.Expand( 'block_contents' ) ; 510 511 // Create the fixed block. 512 var oFixedBlock = this.Window.document.createElement( blockTag ) ; 513 514 // Move the contents of the temporary range to the fixed block. 515 range.ExtractContents().AppendTo( oFixedBlock ) ; 516 FCKDomTools.TrimNode( oFixedBlock ) ; 517 518 // Insert the fixed block into the DOM. 519 range.InsertNode( oFixedBlock ) ; 520 521 // Move the range back to the bookmarked place. 522 range.MoveToBookmark( oBookmark ) ; 523 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 15:28:05 2007 | par Balluche grâce à PHPXref 0.7 |