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 * Creation and initialization of the "FCK" object. This is the main object 22 * that represents an editor instance. 23 */ 24 25// FCK represents the active editor instance. 26var FCK = 27{ 28 Name : FCKURLParams[ 'InstanceName' ], 29 Status : FCK_STATUS_NOTLOADED, 30 EditMode : FCK_EDITMODE_WYSIWYG, 31 Toolbar : null, 32 HasFocus : false, 33 DataProcessor : new FCKDataProcessor(), 34 35 AttachToOnSelectionChange : function( functionPointer ) 36 { 37 this.Events.AttachEvent( 'OnSelectionChange', functionPointer ) ; 38 }, 39 40 GetLinkedFieldValue : function() 41 { 42 return this.LinkedField.value ; 43 }, 44 45 GetParentForm : function() 46 { 47 return this.LinkedField.form ; 48 } , 49 50 // # START : IsDirty implementation 51 52 StartupValue : '', 53 54 IsDirty : function() 55 { 56 if ( this.EditMode == FCK_EDITMODE_SOURCE ) 57 return ( this.StartupValue != this.EditingArea.Textarea.value ) ; 58 else 59 return ( this.StartupValue != this.EditorDocument.body.innerHTML ) ; 60 }, 61 62 ResetIsDirty : function() 63 { 64 if ( this.EditMode == FCK_EDITMODE_SOURCE ) 65 this.StartupValue = this.EditingArea.Textarea.value ; 66 else if ( this.EditorDocument.body ) 67 this.StartupValue = this.EditorDocument.body.innerHTML ; 68 }, 69 70 // # END : IsDirty implementation 71 72 StartEditor : function() 73 { 74 this.TempBaseTag = FCKConfig.BaseHref.length > 0 ? '<base href="' + FCKConfig.BaseHref + '" _fcktemp="true"></base>' : '' ; 75 76 // Setup the keystroke handler. 77 var oKeystrokeHandler = FCK.KeystrokeHandler = new FCKKeystrokeHandler() ; 78 oKeystrokeHandler.OnKeystroke = _FCK_KeystrokeHandler_OnKeystroke ; 79 80 // Set the config keystrokes. 81 oKeystrokeHandler.SetKeystrokes( FCKConfig.Keystrokes ) ; 82 83 // In IE7, if the editor tries to access the clipboard by code, a dialog is 84 // shown to the user asking if the application is allowed to access or not. 85 // Due to the IE implementation of it, the KeystrokeHandler will not work 86 //well in this case, so we must leave the pasting keys to have their default behavior. 87 if ( FCKBrowserInfo.IsIE7 ) 88 { 89 if ( ( CTRL + 86 /*V*/ ) in oKeystrokeHandler.Keystrokes ) 90 oKeystrokeHandler.SetKeystrokes( [ CTRL + 86, true ] ) ; 91 92 if ( ( SHIFT + 45 /*INS*/ ) in oKeystrokeHandler.Keystrokes ) 93 oKeystrokeHandler.SetKeystrokes( [ SHIFT + 45, true ] ) ; 94 } 95 96 // Retain default behavior for Ctrl-Backspace. (Bug #362) 97 oKeystrokeHandler.SetKeystrokes( [ CTRL + 8, true ] ) ; 98 99 this.EditingArea = new FCKEditingArea( document.getElementById( 'xEditingArea' ) ) ; 100 this.EditingArea.FFSpellChecker = FCKConfig.FirefoxSpellChecker ; 101 102 // Set the editor's startup contents. 103 this.SetData( this.GetLinkedFieldValue(), true ) ; 104 105 // Tab key handling for source mode. 106 FCKTools.AddEventListener( document, "keydown", this._TabKeyHandler ) ; 107 }, 108 109 Focus : function() 110 { 111 FCK.EditingArea.Focus() ; 112 }, 113 114 SetStatus : function( newStatus ) 115 { 116 this.Status = newStatus ; 117 118 if ( newStatus == FCK_STATUS_ACTIVE ) 119 { 120 FCKFocusManager.AddWindow( window, true ) ; 121 122 if ( FCKBrowserInfo.IsIE ) 123 FCKFocusManager.AddWindow( window.frameElement, true ) ; 124 125 // Force the focus in the editor. 126 if ( FCKConfig.StartupFocus ) 127 FCK.Focus() ; 128 } 129 130 this.Events.FireEvent( 'OnStatusChange', newStatus ) ; 131 132 }, 133 134 // Fixes the body by moving all inline and text nodes to appropriate block 135 // elements. 136 FixBody : function() 137 { 138 var sBlockTag = FCKConfig.EnterMode ; 139 140 // In 'br' mode, no fix must be done. 141 if ( sBlockTag != 'p' && sBlockTag != 'div' ) 142 return ; 143 144 var oDocument = this.EditorDocument ; 145 146 if ( !oDocument ) 147 return ; 148 149 var oBody = oDocument.body ; 150 151 if ( !oBody ) 152 return ; 153 154 FCKDomTools.TrimNode( oBody ) ; 155 156 var oNode = oBody.firstChild ; 157 var oNewBlock ; 158 159 while ( oNode ) 160 { 161 var bMoveNode = false ; 162 163 switch ( oNode.nodeType ) 164 { 165 // Element Node. 166 case 1 : 167 if ( !FCKListsLib.BlockElements[ oNode.nodeName.toLowerCase() ] ) 168 bMoveNode = true ; 169 break ; 170 171 // Text Node. 172 case 3 : 173 // Ignore space only or empty text. 174 if ( oNewBlock || oNode.nodeValue.Trim().length > 0 ) 175 bMoveNode = true ; 176 } 177 178 if ( bMoveNode ) 179 { 180 var oParent = oNode.parentNode ; 181 182 if ( !oNewBlock ) 183 oNewBlock = oParent.insertBefore( oDocument.createElement( sBlockTag ), oNode ) ; 184 185 oNewBlock.appendChild( oParent.removeChild( oNode ) ) ; 186 187 oNode = oNewBlock.nextSibling ; 188 } 189 else 190 { 191 if ( oNewBlock ) 192 { 193 FCKDomTools.TrimNode( oNewBlock ) ; 194 oNewBlock = null ; 195 } 196 oNode = oNode.nextSibling ; 197 } 198 } 199 200 if ( oNewBlock ) 201 FCKDomTools.TrimNode( oNewBlock ) ; 202 }, 203 204 GetData : function( format ) 205 { 206 // We assume that if the user is in source editing, the editor value must 207 // represent the exact contents of the source, as the user wanted it to be. 208 if ( FCK.EditMode == FCK_EDITMODE_SOURCE ) 209 return FCK.EditingArea.Textarea.value ; 210 211 this.FixBody() ; 212 213 var oDoc = FCK.EditorDocument ; 214 if ( !oDoc ) 215 return null ; 216 217 var isFullPage = FCKConfig.FullPage ; 218 219 // Call the Data Processor to generate the output data. 220 var data = FCK.DataProcessor.ConvertToDataFormat( 221 isFullPage ? oDoc.documentElement : oDoc.body, 222 !isFullPage, 223 FCKConfig.IgnoreEmptyParagraphValue, 224 format ) ; 225 226 // Restore protected attributes. 227 data = FCK.ProtectEventsRestore( data ) ; 228 229 if ( FCKBrowserInfo.IsIE ) 230 data = data.replace( FCKRegexLib.ToReplace, '$1' ) ; 231 232 if ( isFullPage ) 233 { 234 if ( FCK.DocTypeDeclaration && FCK.DocTypeDeclaration.length > 0 ) 235 data = FCK.DocTypeDeclaration + '\n' + data ; 236 237 if ( FCK.XmlDeclaration && FCK.XmlDeclaration.length > 0 ) 238 data = FCK.XmlDeclaration + '\n' + data ; 239 } 240 241 return FCKConfig.ProtectedSource.Revert( data ) ; 242 }, 243 244 UpdateLinkedField : function() 245 { 246 var value = FCK.GetXHTML( FCKConfig.FormatOutput ) ; 247 248 if ( FCKConfig.HtmlEncodeOutput ) 249 value = FCKTools.HTMLEncode( value ) ; 250 251 FCK.LinkedField.value = value ; 252 FCK.Events.FireEvent( 'OnAfterLinkedFieldUpdate' ) ; 253 }, 254 255 RegisteredDoubleClickHandlers : new Object(), 256 257 OnDoubleClick : function( element ) 258 { 259 var oHandler = FCK.RegisteredDoubleClickHandlers[ element.tagName ] ; 260 if ( oHandler ) 261 oHandler( element ) ; 262 }, 263 264 // Register objects that can handle double click operations. 265 RegisterDoubleClickHandler : function( handlerFunction, tag ) 266 { 267 FCK.RegisteredDoubleClickHandlers[ tag.toUpperCase() ] = handlerFunction ; 268 }, 269 270 OnAfterSetHTML : function() 271 { 272 FCKDocumentProcessor.Process( FCK.EditorDocument ) ; 273 FCKUndo.SaveUndoStep() ; 274 275 FCK.Events.FireEvent( 'OnSelectionChange' ) ; 276 FCK.Events.FireEvent( 'OnAfterSetHTML' ) ; 277 }, 278 279 // Saves URLs on links and images on special attributes, so they don't change when 280 // moving around. 281 ProtectUrls : function( html ) 282 { 283 // <A> href 284 html = html.replace( FCKRegexLib.ProtectUrlsA , '$& _fcksavedurl=$1' ) ; 285 286 // <IMG> src 287 html = html.replace( FCKRegexLib.ProtectUrlsImg , '$& _fcksavedurl=$1' ) ; 288 289 return html ; 290 }, 291 292 // Saves event attributes (like onclick) so they don't get executed while 293 // editing. 294 ProtectEvents : function( html ) 295 { 296 return html.replace( FCKRegexLib.TagsWithEvent, _FCK_ProtectEvents_ReplaceTags ) ; 297 }, 298 299 ProtectEventsRestore : function( html ) 300 { 301 return html.replace( FCKRegexLib.ProtectedEvents, _FCK_ProtectEvents_RestoreEvents ) ; 302 }, 303 304 ProtectTags : function( html ) 305 { 306 var sTags = FCKConfig.ProtectedTags ; 307 308 // IE doesn't support <abbr> and it breaks it. Let's protect it. 309 if ( FCKBrowserInfo.IsIE ) 310 sTags += sTags.length > 0 ? '|ABBR|XML|EMBED' : 'ABBR|XML|EMBED' ; 311 312 var oRegex ; 313 if ( sTags.length > 0 ) 314 { 315 oRegex = new RegExp( '<(' + sTags + ')(?!\w|:)', 'gi' ) ; 316 html = html.replace( oRegex, '<FCK:$1' ) ; 317 318 oRegex = new RegExp( '<\/(' + sTags + ')>', 'gi' ) ; 319 html = html.replace( oRegex, '<\/FCK:$1>' ) ; 320 } 321 322 // Protect some empty elements. We must do it separately because the 323 // original tag may not contain the closing slash, like <hr>: 324 // - <meta> tags get executed, so if you have a redirect meta, the 325 // content will move to the target page. 326 // - <hr> may destroy the document structure if not well 327 // positioned. The trick is protect it here and restore them in 328 // the FCKDocumentProcessor. 329 sTags = 'META' ; 330 if ( FCKBrowserInfo.IsIE ) 331 sTags += '|HR' ; 332 333 oRegex = new RegExp( '<((' + sTags + ')(?=\\s|>|/)[\\s\\S]*?)/?>', 'gi' ) ; 334 html = html.replace( oRegex, '<FCK:$1 />' ) ; 335 336 return html ; 337 }, 338 339 SetData : function( data, resetIsDirty ) 340 { 341 this.EditingArea.Mode = FCK.EditMode ; 342 343 if ( FCK.EditMode == FCK_EDITMODE_WYSIWYG ) 344 { 345 // Save the resetIsDirty for later use (async) 346 this._ForceResetIsDirty = ( resetIsDirty === true ) ; 347 348 // Protect parts of the code that must remain untouched (and invisible) 349 // during editing. 350 data = FCKConfig.ProtectedSource.Protect( data ) ; 351 352 // Call the Data Processor to transform the data. 353 data = FCK.DataProcessor.ConvertToHtml( data ) ; 354 355 // Fix for invalid self-closing tags (see #152). 356 data = data.replace( FCKRegexLib.InvalidSelfCloseTags, '$1></$2>' ) ; 357 358 // Protect event attributes (they could get fired in the editing area). 359 data = FCK.ProtectEvents( data ) ; 360 361 // Protect some things from the browser itself. 362 data = FCK.ProtectUrls( data ) ; 363 data = FCK.ProtectTags( data ) ; 364 365 // Insert the base tag (FCKConfig.BaseHref), if not exists in the source. 366 // The base must be the first tag in the HEAD, to get relative 367 // links on styles, for example. 368 if ( FCK.TempBaseTag.length > 0 && !FCKRegexLib.HasBaseTag.test( data ) ) 369 data = data.replace( FCKRegexLib.HeadOpener, '$&' + FCK.TempBaseTag ) ; 370 371 // Build the HTML for the additional things we need on <head>. 372 var sHeadExtra = '' ; 373 374 if ( !FCKConfig.FullPage ) 375 sHeadExtra += _FCK_GetEditorAreaStyleTags() ; 376 377 if ( FCKBrowserInfo.IsIE ) 378 sHeadExtra += FCK._GetBehaviorsStyle() ; 379 else if ( FCKConfig.ShowBorders ) 380 sHeadExtra += '<link href="' + FCKConfig.FullBasePath + 'css/fck_showtableborders_gecko.css" rel="stylesheet" type="text/css" _fcktemp="true" />' ; 381 382 sHeadExtra += '<link href="' + FCKConfig.FullBasePath + 'css/fck_internal.css" rel="stylesheet" type="text/css" _fcktemp="true" />' ; 383 384 // Attention: do not change it before testing it well (sample07)! 385 // This is tricky... if the head ends with <meta ... content type>, 386 // Firefox will break. But, it works if we place our extra stuff as 387 // the last elements in the HEAD. 388 data = data.replace( FCKRegexLib.HeadCloser, sHeadExtra + '$&' ) ; 389 390 // Load the HTML in the editing area. 391 this.EditingArea.OnLoad = _FCK_EditingArea_OnLoad ; 392 this.EditingArea.Start( data ) ; 393 } 394 else 395 { 396 // Remove the references to the following elements, as the editing area 397 // IFRAME will be removed. 398 FCK.EditorWindow = null ; 399 FCK.EditorDocument = null ; 400 FCKDomTools.PaddingNode = null ; 401 402 this.EditingArea.OnLoad = null ; 403 this.EditingArea.Start( data ) ; 404 405 // Enables the context menu in the textarea. 406 this.EditingArea.Textarea._FCKShowContextMenu = true ; 407 408 // Removes the enter key handler. 409 FCK.EnterKeyHandler = null ; 410 411 if ( resetIsDirty ) 412 this.ResetIsDirty() ; 413 414 // Listen for keystroke events. 415 FCK.KeystrokeHandler.AttachToElement( this.EditingArea.Textarea ) ; 416 417 this.EditingArea.Textarea.focus() ; 418 419 FCK.Events.FireEvent( 'OnAfterSetHTML' ) ; 420 } 421 422 if ( FCKBrowserInfo.IsGecko ) 423 window.onresize() ; 424 }, 425 426 // For the FocusManager 427 HasFocus : false, 428 429 430 // This collection is used by the browser specific implementations to tell 431 // which named commands must be handled separately. 432 RedirectNamedCommands : new Object(), 433 434 ExecuteNamedCommand : function( commandName, commandParameter, noRedirect, noSaveUndo ) 435 { 436 if ( !noSaveUndo ) 437 FCKUndo.SaveUndoStep() ; 438 439 if ( !noRedirect && FCK.RedirectNamedCommands[ commandName ] != null ) 440 FCK.ExecuteRedirectedNamedCommand( commandName, commandParameter ) ; 441 else 442 { 443 FCK.Focus() ; 444 FCK.EditorDocument.execCommand( commandName, false, commandParameter ) ; 445 FCK.Events.FireEvent( 'OnSelectionChange' ) ; 446 } 447 448 if ( !noSaveUndo ) 449 FCKUndo.SaveUndoStep() ; 450 }, 451 452 GetNamedCommandState : function( commandName ) 453 { 454 try 455 { 456 457 // Bug #50 : Safari never returns positive state for the Paste command, override that. 458 if ( FCKBrowserInfo.IsSafari && FCK.EditorWindow && commandName.IEquals( 'Paste' ) ) 459 return FCK_TRISTATE_OFF ; 460 461 if ( !FCK.EditorDocument.queryCommandEnabled( commandName ) ) 462 return FCK_TRISTATE_DISABLED ; 463 else 464 { 465 return FCK.EditorDocument.queryCommandState( commandName ) ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF ; 466 } 467 } 468 catch ( e ) 469 { 470 return FCK_TRISTATE_OFF ; 471 } 472 }, 473 474 GetNamedCommandValue : function( commandName ) 475 { 476 var sValue = '' ; 477 var eState = FCK.GetNamedCommandState( commandName ) ; 478 479 if ( eState == FCK_TRISTATE_DISABLED ) 480 return null ; 481 482 try 483 { 484 sValue = this.EditorDocument.queryCommandValue( commandName ) ; 485 } 486 catch(e) {} 487 488 return sValue ? sValue : '' ; 489 }, 490 491 Paste : function( _callListenersOnly ) 492 { 493 // First call 'OnPaste' listeners. 494 if ( FCK.Status != FCK_STATUS_COMPLETE || !FCK.Events.FireEvent( 'OnPaste' ) ) 495 return false ; 496 497 // Then call the default implementation. 498 return _callListenersOnly || FCK._ExecPaste() ; 499 }, 500 501 PasteFromWord : function() 502 { 503 FCKDialog.OpenDialog( 'FCKDialog_Paste', FCKLang.PasteFromWord, 'dialog/fck_paste.html', 400, 330, 'Word' ) ; 504 }, 505 506 Preview : function() 507 { 508 var iWidth = FCKConfig.ScreenWidth * 0.8 ; 509 var iHeight = FCKConfig.ScreenHeight * 0.7 ; 510 var iLeft = ( FCKConfig.ScreenWidth - iWidth ) / 2 ; 511 var oWindow = window.open( '', null, 'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width=' + iWidth + ',height=' + iHeight + ',left=' + iLeft ) ; 512 513 var sHTML ; 514 515 if ( FCKConfig.FullPage ) 516 { 517 if ( FCK.TempBaseTag.length > 0 ) 518 sHTML = FCK.TempBaseTag + FCK.GetXHTML() ; 519 else 520 sHTML = FCK.GetXHTML() ; 521 } 522 else 523 { 524 sHTML = 525 FCKConfig.DocType + 526 '<html dir="' + FCKConfig.ContentLangDirection + '">' + 527 '<head>' + 528 FCK.TempBaseTag + 529 '<title>' + FCKLang.Preview + '</title>' + 530 _FCK_GetEditorAreaStyleTags() + 531 '</head><body' + FCKConfig.GetBodyAttributes() + '>' + 532 FCK.GetXHTML() + 533 '</body></html>' ; 534 } 535 536 oWindow.document.write( sHTML ); 537 oWindow.document.close(); 538 }, 539 540 SwitchEditMode : function( noUndo ) 541 { 542 var bIsWysiwyg = ( FCK.EditMode == FCK_EDITMODE_WYSIWYG ) ; 543 544 // Save the current IsDirty state, so we may restore it after the switch. 545 var bIsDirty = FCK.IsDirty() ; 546 547 var sHtml ; 548 549 // Update the HTML in the view output to show. 550 if ( bIsWysiwyg ) 551 { 552 FCKCommands.GetCommand( 'ShowBlocks' ).SaveState() ; 553 if ( !noUndo && FCKBrowserInfo.IsIE ) 554 FCKUndo.SaveUndoStep() ; 555 556 sHtml = FCK.GetXHTML( FCKConfig.FormatSource ) ; 557 558 if ( sHtml == null ) 559 return false ; 560 } 561 else 562 sHtml = this.EditingArea.Textarea.value ; 563 564 FCK.EditMode = bIsWysiwyg ? FCK_EDITMODE_SOURCE : FCK_EDITMODE_WYSIWYG ; 565 566 FCK.SetData( sHtml, !bIsDirty ) ; 567 568 // Set the Focus. 569 FCK.Focus() ; 570 571 // Update the toolbar (Running it directly causes IE to fail). 572 FCKTools.RunFunction( FCK.ToolbarSet.RefreshModeState, FCK.ToolbarSet ) ; 573 574 return true ; 575 }, 576 577 InsertElement : function( element ) 578 { 579 // The parameter may be a string (element name), so transform it in an element. 580 if ( typeof element == 'string' ) 581 element = this.EditorDocument.createElement( element ) ; 582 583 var elementName = element.nodeName.toLowerCase() ; 584 585 // Create a range for the selection. V3 will have a new selection 586 // object that may internally supply this feature. 587 var range = new FCKDomRange( this.EditorWindow ) ; 588 589 if ( FCKListsLib.BlockElements[ elementName ] != null ) 590 { 591 range.SplitBlock() ; 592 range.InsertNode( element ) ; 593 594 var next = FCKDomTools.GetNextSourceElement( element, false, null, [ 'hr','br','param','img','area','input' ] ) ; 595 596 // Be sure that we have something after the new element, so we can move the cursor there. 597 if ( !next && FCKConfig.EnterMode != 'br') 598 { 599 next = this.EditorDocument.body.appendChild( this.EditorDocument.createElement( FCKConfig.EnterMode ) ) ; 600 601 if ( FCKBrowserInfo.IsGeckoLike ) 602 FCKTools.AppendBogusBr( next ) ; 603 } 604 605 if ( FCKListsLib.EmptyElements[ elementName ] == null ) 606 range.MoveToElementEditStart( element ) ; 607 else if ( next ) 608 range.MoveToElementEditStart( next ) ; 609 else 610 range.MoveToPosition( element, 4 ) ; 611 612 if ( FCKBrowserInfo.IsGecko ) 613 { 614 if ( next ) 615 next.scrollIntoView( false ) ; 616 element.scrollIntoView( false ) ; 617 } 618 } 619 else 620 { 621 // Delete the current selection and insert the node. 622 range.MoveToSelection() ; 623 range.DeleteContents() ; 624 range.InsertNode( element ) ; 625 626 // Move the selection right after the new element. 627 // DISCUSSION: Should we select the element instead? 628 range.SetStart( element, 4 ) ; 629 range.SetEnd( element, 4 ) ; 630 } 631 632 range.Select() ; 633 range.Release() ; 634 635 // REMOVE IT: The focus should not really be set here. It is up to the 636 // calling code to reset the focus if needed. 637 this.Focus() ; 638 639 return element ; 640 }, 641 642 _InsertBlockElement : function( blockElement ) 643 { 644 }, 645 646 _IsFunctionKey : function( keyCode ) 647 { 648 // keys that are captured but do not change editor contents 649 if ( keyCode >= 16 && keyCode <= 20 ) 650 // shift, ctrl, alt, pause, capslock 651 return true ; 652 if ( keyCode == 27 || ( keyCode >= 33 && keyCode <= 40 ) ) 653 // esc, page up, page down, end, home, left, up, right, down 654 return true ; 655 if ( keyCode == 45 ) 656 // insert, no effect on FCKeditor, yet 657 return true ; 658 return false ; 659 }, 660 661 _KeyDownListener : function( evt ) 662 { 663 if (! evt) 664 evt = FCK.EditorWindow.event ; 665 if ( FCK.EditorWindow ) 666 { 667 if ( !FCK._IsFunctionKey(evt.keyCode) // do not capture function key presses, like arrow keys or shift/alt/ctrl 668 && !(evt.ctrlKey || evt.metaKey) // do not capture Ctrl hotkeys, as they have their snapshot capture logic 669 && !(evt.keyCode == 46) ) // do not capture Del, it has its own capture logic in fckenterkey.js 670 FCK._KeyDownUndo() ; 671 } 672 return true ; 673 }, 674 675 _KeyDownUndo : function() 676 { 677 if ( !FCKUndo.Typing ) 678 { 679 FCKUndo.SaveUndoStep() ; 680 FCKUndo.Typing = true ; 681 FCK.Events.FireEvent( "OnSelectionChange" ) ; 682 } 683 684 FCKUndo.TypesCount++ ; 685 FCKUndo.Changed = 1 ; 686 687 if ( FCKUndo.TypesCount > FCKUndo.MaxTypes ) 688 { 689 FCKUndo.TypesCount = 0 ; 690 FCKUndo.SaveUndoStep() ; 691 } 692 }, 693 694 _TabKeyHandler : function( evt ) 695 { 696 if ( ! evt ) 697 evt = window.event ; 698 699 var keystrokeValue = evt.keyCode ; 700 701 // Pressing <Tab> in source mode should produce a tab space in the text area, not 702 // changing the focus to something else. 703 if ( keystrokeValue == 9 && FCK.EditMode != FCK_EDITMODE_WYSIWYG ) 704 { 705 if ( FCKBrowserInfo.IsIE ) 706 { 707 var range = document.selection.createRange() ; 708 if ( range.parentElement() != FCK.EditingArea.Textarea ) 709 return true ; 710 range.text = '\t' ; 711 range.select() ; 712 } 713 else 714 { 715 var a = [] ; 716 var el = FCK.EditingArea.Textarea ; 717 var selStart = el.selectionStart ; 718 var selEnd = el.selectionEnd ; 719 a.push( el.value.substr(0, selStart ) ) ; 720 a.push( '\t' ) ; 721 a.push( el.value.substr( selEnd ) ) ; 722 el.value = a.join( '' ) ; 723 el.setSelectionRange( selStart + 1, selStart + 1 ) ; 724 } 725 726 if ( evt.preventDefault ) 727 return evt.preventDefault() ; 728 729 return evt.returnValue = false ; 730 } 731 732 return true ; 733 } 734} ; 735 736FCK.Events = new FCKEvents( FCK ) ; 737 738// DEPRECATED in favor or "GetData". 739FCK.GetHTML = FCK.GetXHTML = FCK.GetData ; 740 741// DEPRECATED in favor of "SetData". 742FCK.SetHTML = FCK.SetData ; 743 744// InsertElementAndGetIt and CreateElement are Deprecated : returns the same value as InsertElement. 745FCK.InsertElementAndGetIt = FCK.CreateElement = FCK.InsertElement ; 746 747// Replace all events attributes (like onclick). 748function _FCK_ProtectEvents_ReplaceTags( tagMatch ) 749{ 750 return tagMatch.replace( FCKRegexLib.EventAttributes, _FCK_ProtectEvents_ReplaceEvents ) ; 751} 752 753// Replace an event attribute with its respective __fckprotectedatt attribute. 754// The original event markup will be encoded and saved as the value of the new 755// attribute. 756function _FCK_ProtectEvents_ReplaceEvents( eventMatch, attName ) 757{ 758 return ' ' + attName + '_fckprotectedatt="' + encodeURIComponent( eventMatch ) + '"' ; 759} 760 761function _FCK_ProtectEvents_RestoreEvents( match, encodedOriginal ) 762{ 763 return decodeURIComponent( encodedOriginal ) ; 764} 765 766function _FCK_MouseEventsListener( evt ) 767{ 768 if ( ! evt ) 769 evt = window.event ; 770 if ( evt.type == 'mousedown' ) 771 FCK.MouseDownFlag = true ; 772 else if ( evt.type == 'mouseup' ) 773 FCK.MouseDownFlag = false ; 774 else if ( evt.type == 'mousemove' ) 775 FCK.Events.FireEvent( 'OnMouseMove', evt ) ; 776} 777 778function _FCK_PaddingNodeListener() 779{ 780 if ( FCKConfig.EnterMode.IEquals( 'br' ) ) 781 return ; 782 FCKDomTools.EnforcePaddingNode( FCK.EditorDocument, FCKConfig.EnterMode ) ; 783 784 if ( ! FCKBrowserInfo.IsIE && FCKDomTools.PaddingNode ) 785 { 786 var sel = FCK.EditorWindow.getSelection() ; 787 if ( sel && sel.rangeCount == 1 ) 788 { 789 var range = sel.getRangeAt( 0 ) ; 790 if ( range.collapsed && range.startContainer == FCK.EditorDocument.body && range.startOffset == 0 ) 791 { 792 range.selectNodeContents( FCKDomTools.PaddingNode ) ; 793 range.collapse( true ) ; 794 sel.removeAllRanges() ; 795 sel.addRange( range ) ; 796 } 797 } 798 } 799} 800 801function _FCK_EditingArea_OnLoad() 802{ 803 // Get the editor's window and document (DOM) 804 FCK.EditorWindow = FCK.EditingArea.Window ; 805 FCK.EditorDocument = FCK.EditingArea.Document ; 806 807 FCK.InitializeBehaviors() ; 808 FCK.AttachToOnSelectionChange( _FCK_PaddingNodeListener ) ; 809 810 // Listen for mousedown and mouseup events for tracking drag and drops. 811 FCK.MouseDownFlag = false ; 812 FCKTools.AddEventListener( FCK.EditorDocument, 'mousemove', _FCK_MouseEventsListener ) ; 813 FCKTools.AddEventListener( FCK.EditorDocument, 'mousedown', _FCK_MouseEventsListener ) ; 814 FCKTools.AddEventListener( FCK.EditorDocument, 'mouseup', _FCK_MouseEventsListener ) ; 815 816 // Most of the CTRL key combos do not work under Safari for onkeydown and onkeypress (See #1119) 817 // But we can use the keyup event to override some of these... 818 if ( FCKBrowserInfo.IsSafari ) 819 { 820 var undoFunc = function( evt ) 821 { 822 if ( ! ( evt.ctrlKey || evt.metaKey ) ) 823 return ; 824 if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG ) 825 return ; 826 switch ( evt.keyCode ) 827 { 828 case 89: 829 FCKUndo.Redo() ; 830 break ; 831 case 90: 832 FCKUndo.Undo() ; 833 break ; 834 } 835 } 836 837 FCKTools.AddEventListener( FCK.EditorDocument, 'keyup', undoFunc ) ; 838 } 839 840 // Create the enter key handler 841 FCK.EnterKeyHandler = new FCKEnterKey( FCK.EditorWindow, FCKConfig.EnterMode, FCKConfig.ShiftEnterMode, FCKConfig.TabSpaces ) ; 842 843 // Listen for keystroke events. 844 FCK.KeystrokeHandler.AttachToElement( FCK.EditorDocument ) ; 845 846 if ( FCK._ForceResetIsDirty ) 847 FCK.ResetIsDirty() ; 848 849 // This is a tricky thing for IE. In some cases, even if the cursor is 850 // blinking in the editing, the keystroke handler doesn't catch keyboard 851 // events. We must activate the editing area to make it work. (#142). 852 if ( FCKBrowserInfo.IsIE && FCK.HasFocus ) 853 FCK.EditorDocument.body.setActive() ; 854 855 FCK.OnAfterSetHTML() ; 856 857 // Restore show blocks status. 858 FCKCommands.GetCommand( 'ShowBlocks' ).RestoreState() ; 859 860 // Check if it is not a startup call, otherwise complete the startup. 861 if ( FCK.Status != FCK_STATUS_NOTLOADED ) 862 return ; 863 864 if ( FCKConfig.Debug ) 865 FCKDebug._GetWindow() ; 866 867 FCK.SetStatus( FCK_STATUS_ACTIVE ) ; 868} 869 870function _FCK_GetEditorAreaStyleTags() 871{ 872 var sTags = '' ; 873 var aCSSs = FCKConfig.EditorAreaCSS ; 874 var sStyles = FCKConfig.EditorAreaStyles ; 875 876 for ( var i = 0 ; i < aCSSs.length ; i++ ) 877 sTags += '<link href="' + aCSSs[i] + '" rel="stylesheet" type="text/css" />' ; 878 879 if ( sStyles && sStyles.length > 0 ) 880 sTags += "<style>" + sStyles + "</style>" ; 881 882 return sTags ; 883} 884 885function _FCK_KeystrokeHandler_OnKeystroke( keystroke, keystrokeValue ) 886{ 887 if ( FCK.Status != FCK_STATUS_COMPLETE ) 888 return false ; 889 890 if ( FCK.EditMode == FCK_EDITMODE_WYSIWYG ) 891 { 892 switch ( keystrokeValue ) 893 { 894 case 'Paste' : 895 return !FCK.Paste() ; 896 897 case 'Cut' : 898 FCKUndo.SaveUndoStep() ; 899 return false ; 900 } 901 } 902 else 903 { 904 // In source mode, some actions must have their default behavior. 905 if ( keystrokeValue.Equals( 'Paste', 'Undo', 'Redo', 'SelectAll', 'Cut' ) ) 906 return false ; 907 } 908 909 // The return value indicates if the default behavior of the keystroke must 910 // be cancelled. Let's do that only if the Execute() call explicitly returns "false". 911 var oCommand = FCK.Commands.GetCommand( keystrokeValue ) ; 912 return ( oCommand.Execute.apply( oCommand, FCKTools.ArgumentsToArray( arguments, 2 ) ) !== false ) ; 913} 914 915// Set the FCK.LinkedField reference to the field that will be used to post the 916// editor data. 917(function() 918{ 919 // There is a bug on IE... getElementById returns any META tag that has the 920 // name set to the ID you are looking for. So the best way in to get the array 921 // by names and look for the correct one. 922 // As ASP.Net generates a ID that is different from the Name, we must also 923 // look for the field based on the ID (the first one is the ID). 924 925 var oDocument = window.parent.document ; 926 927 // Try to get the field using the ID. 928 var eLinkedField = oDocument.getElementById( FCK.Name ) ; 929 930 var i = 0; 931 while ( eLinkedField || i == 0 ) 932 { 933 if ( eLinkedField && eLinkedField.tagName.toLowerCase().Equals( 'input', 'textarea' ) ) 934 { 935 FCK.LinkedField = eLinkedField ; 936 break ; 937 } 938 939 eLinkedField = oDocument.getElementsByName( FCK.Name )[i++] ; 940 } 941})() ; 942 943var FCKTempBin = 944{ 945 Elements : new Array(), 946 947 AddElement : function( element ) 948 { 949 var iIndex = this.Elements.length ; 950 this.Elements[ iIndex ] = element ; 951 return iIndex ; 952 }, 953 954 RemoveElement : function( index ) 955 { 956 var e = this.Elements[ index ] ; 957 this.Elements[ index ] = null ; 958 return e ; 959 }, 960 961 Reset : function() 962 { 963 var i = 0 ; 964 while ( i < this.Elements.length ) 965 this.Elements[ i++ ] = null ; 966 this.Elements.length = 0 ; 967 } 968} ; 969 970 971 972// # Focus Manager: Manages the focus in the editor. 973var FCKFocusManager = FCK.FocusManager = 974{ 975 IsLocked : false, 976 977 AddWindow : function( win, sendToEditingArea ) 978 { 979 var oTarget ; 980 981 if ( FCKBrowserInfo.IsIE ) 982 oTarget = win.nodeType == 1 ? win : win.frameElement ? win.frameElement : win.document ; 983 else if ( FCKBrowserInfo.IsSafari ) 984 oTarget = win ; 985 else 986 oTarget = win.document ; 987 988 FCKTools.AddEventListener( oTarget, 'blur', FCKFocusManager_Win_OnBlur ) ; 989 FCKTools.AddEventListener( oTarget, 'focus', sendToEditingArea ? FCKFocusManager_Win_OnFocus_Area : FCKFocusManager_Win_OnFocus ) ; 990 }, 991 992 RemoveWindow : function( win ) 993 { 994 if ( FCKBrowserInfo.IsIE ) 995 oTarget = win.nodeType == 1 ? win : win.frameElement ? win.frameElement : win.document ; 996 else 997 oTarget = win.document ; 998 999 FCKTools.RemoveEventListener( oTarget, 'blur', FCKFocusManager_Win_OnBlur ) ; 1000 FCKTools.RemoveEventListener( oTarget, 'focus', FCKFocusManager_Win_OnFocus_Area ) ; 1001 FCKTools.RemoveEventListener( oTarget, 'focus', FCKFocusManager_Win_OnFocus ) ; 1002 }, 1003 1004 Lock : function() 1005 { 1006 this.IsLocked = true ; 1007 }, 1008 1009 Unlock : function() 1010 { 1011 if ( this._HasPendingBlur ) 1012 FCKFocusManager._Timer = window.setTimeout( FCKFocusManager_FireOnBlur, 100 ) ; 1013 1014 this.IsLocked = false ; 1015 }, 1016 1017 _ResetTimer : function() 1018 { 1019 this._HasPendingBlur = false ; 1020 1021 if ( this._Timer ) 1022 { 1023 window.clearTimeout( this._Timer ) ; 1024 delete this._Timer ; 1025 } 1026 } 1027} ; 1028 1029function FCKFocusManager_Win_OnBlur() 1030{ 1031 if ( typeof(FCK) != 'undefined' && FCK.HasFocus ) 1032 { 1033 FCKFocusManager._ResetTimer() ; 1034 FCKFocusManager._Timer = window.setTimeout( FCKFocusManager_FireOnBlur, 100 ) ; 1035 } 1036} 1037 1038function FCKFocusManager_FireOnBlur() 1039{ 1040 if ( FCKFocusManager.IsLocked ) 1041 FCKFocusManager._HasPendingBlur = true ; 1042 else 1043 { 1044 FCK.HasFocus = false ; 1045 FCK.Events.FireEvent( "OnBlur" ) ; 1046 } 1047} 1048 1049function FCKFocusManager_Win_OnFocus_Area() 1050{ 1051 FCK.Focus() ; 1052 FCKFocusManager_Win_OnFocus() ; 1053} 1054 1055function FCKFocusManager_Win_OnFocus() 1056{ 1057 FCKFocusManager._ResetTimer() ; 1058 1059 if ( !FCK.HasFocus && !FCKFocusManager.IsLocked ) 1060 { 1061 FCK.HasFocus = true ; 1062 FCK.Events.FireEvent( "OnFocus" ) ; 1063 } 1064} 1065 1066