PageRenderTime 100ms CodeModel.GetById 45ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/pigeoncms/Plugins/fckeditor/editor/_source/internals/fck.js

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