Click here to Skip to main content
15,894,180 members
Articles / Web Development / XHTML

Integrating FCKeditor in ASP.NET

Rate me:
Please Sign up or sign in to vote.
4.43/5 (15 votes)
20 Jul 2008CPOL2 min read 270.1K   10.1K   64  
Integrating FCKeditor in ASP.NET
/*
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 * Copyright (C) 2003-2008 Frederico Caldeira Knabben
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses at your
 * choice:
 *
 *  - GNU General Public License Version 2 or later (the "GPL")
 *    http://www.gnu.org/licenses/gpl.html
 *
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 *    http://www.gnu.org/licenses/lgpl.html
 *
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 *
 * == END LICENSE ==
 *
 * Creation and initialization of the "FCK" object. This is the main object
 * that represents an editor instance.
 */

// FCK represents the active editor instance.
var FCK =
{
	Name			: FCKURLParams[ 'InstanceName' ],
	Status			: FCK_STATUS_NOTLOADED,
	EditMode		: FCK_EDITMODE_WYSIWYG,
	Toolbar			: null,
	HasFocus		: false,
	DataProcessor	: new FCKDataProcessor(),

	GetInstanceObject	: (function()
	{
		var w = window ;
		return function( name )
		{
			return w[name] ;
		}
	})(),

	AttachToOnSelectionChange : function( functionPointer )
	{
		this.Events.AttachEvent( 'OnSelectionChange', functionPointer ) ;
	},

	GetLinkedFieldValue : function()
	{
		return this.LinkedField.value ;
	},

	GetParentForm : function()
	{
		return this.LinkedField.form ;
	} ,

	// # START : IsDirty implementation

	StartupValue : '',

	IsDirty : function()
	{
		if ( this.EditMode == FCK_EDITMODE_SOURCE )
			return ( this.StartupValue != this.EditingArea.Textarea.value ) ;
		else
		{
			// It can happen switching between design and source mode in Gecko
			if ( ! this.EditorDocument )
				return false ;

			return ( this.StartupValue != this.EditorDocument.body.innerHTML ) ;
		}
	},

	ResetIsDirty : function()
	{
		if ( this.EditMode == FCK_EDITMODE_SOURCE )
			this.StartupValue = this.EditingArea.Textarea.value ;
		else if ( this.EditorDocument.body )
			this.StartupValue = this.EditorDocument.body.innerHTML ;
	},

	// # END : IsDirty implementation

	StartEditor : function()
	{
		this.TempBaseTag = FCKConfig.BaseHref.length > 0 ? '<base href="' + FCKConfig.BaseHref + '" _fcktemp="true"></base>' : '' ;

		// Setup the keystroke handler.
		var oKeystrokeHandler = FCK.KeystrokeHandler = new FCKKeystrokeHandler() ;
		oKeystrokeHandler.OnKeystroke = _FCK_KeystrokeHandler_OnKeystroke ;

		// Set the config keystrokes.
		oKeystrokeHandler.SetKeystrokes( FCKConfig.Keystrokes ) ;

		// In IE7, if the editor tries to access the clipboard by code, a dialog is
		// shown to the user asking if the application is allowed to access or not.
		// Due to the IE implementation of it, the KeystrokeHandler will not work
		//well in this case, so we must leave the pasting keys to have their default behavior.
		if ( FCKBrowserInfo.IsIE7 )
		{
			if ( ( CTRL + 86 /*V*/ ) in oKeystrokeHandler.Keystrokes )
				oKeystrokeHandler.SetKeystrokes( [ CTRL + 86, true ] ) ;

			if ( ( SHIFT + 45 /*INS*/ ) in oKeystrokeHandler.Keystrokes )
				oKeystrokeHandler.SetKeystrokes( [ SHIFT + 45, true ] ) ;
		}

		// Retain default behavior for Ctrl-Backspace. (Bug #362)
		oKeystrokeHandler.SetKeystrokes( [ CTRL + 8, true ] ) ;

		this.EditingArea = new FCKEditingArea( document.getElementById( 'xEditingArea' ) ) ;
		this.EditingArea.FFSpellChecker = FCKConfig.FirefoxSpellChecker ;

		// Set the editor's startup contents.
		this.SetData( this.GetLinkedFieldValue(), true ) ;

		// Tab key handling for source mode.
		FCKTools.AddEventListener( document, "keydown", this._TabKeyHandler ) ;

		// Add selection change listeners. They must be attached only once.
		this.AttachToOnSelectionChange( _FCK_PaddingNodeListener ) ;
		if ( FCKBrowserInfo.IsGecko )
			this.AttachToOnSelectionChange( this._ExecCheckEmptyBlock ) ;

	},

	Focus : function()
	{
		FCK.EditingArea.Focus() ;
	},

	SetStatus : function( newStatus )
	{
		this.Status = newStatus ;

		if ( newStatus == FCK_STATUS_ACTIVE )
		{
			FCKFocusManager.AddWindow( window, true ) ;

			if ( FCKBrowserInfo.IsIE )
				FCKFocusManager.AddWindow( window.frameElement, true ) ;

			// Force the focus in the editor.
			if ( FCKConfig.StartupFocus )
				FCK.Focus() ;
		}

		this.Events.FireEvent( 'OnStatusChange', newStatus ) ;

	},

	// Fixes the body by moving all inline and text nodes to appropriate block
	// elements.
	FixBody : function()
	{
		var sBlockTag = FCKConfig.EnterMode ;

		// In 'br' mode, no fix must be done.
		if ( sBlockTag != 'p' && sBlockTag != 'div' )
			return ;

		var oDocument = this.EditorDocument ;

		if ( !oDocument )
			return ;

		var oBody = oDocument.body ;

		if ( !oBody )
			return ;

		FCKDomTools.TrimNode( oBody ) ;

		var oNode = oBody.firstChild ;
		var oNewBlock ;

		while ( oNode )
		{
			var bMoveNode = false ;

			switch ( oNode.nodeType )
			{
				// Element Node.
				case 1 :
					var nodeName = oNode.nodeName.toLowerCase() ;
					if ( !FCKListsLib.BlockElements[ nodeName ] &&
							nodeName != 'li' &&
							!oNode.getAttribute('_fckfakelement') &&
							oNode.getAttribute('_moz_dirty') == null )
						bMoveNode = true ;
					break ;

				// Text Node.
				case 3 :
					// Ignore space only or empty text.
					if ( oNewBlock || oNode.nodeValue.Trim().length > 0 )
						bMoveNode = true ;
					break;

				// Comment Node
				case 8 :
					if ( oNewBlock )
						bMoveNode = true ;
					break;
			}

			if ( bMoveNode )
			{
				var oParent = oNode.parentNode ;

				if ( !oNewBlock )
					oNewBlock = oParent.insertBefore( oDocument.createElement( sBlockTag ), oNode ) ;

				oNewBlock.appendChild( oParent.removeChild( oNode ) ) ;

				oNode = oNewBlock.nextSibling ;
			}
			else
			{
				if ( oNewBlock )
				{
					FCKDomTools.TrimNode( oNewBlock ) ;
					oNewBlock = null ;
				}
				oNode = oNode.nextSibling ;
			}
		}

		if ( oNewBlock )
			FCKDomTools.TrimNode( oNewBlock ) ;
	},

	GetData : function( format )
	{
		// We assume that if the user is in source editing, the editor value must
		// represent the exact contents of the source, as the user wanted it to be.
		if ( FCK.EditMode == FCK_EDITMODE_SOURCE )
				return FCK.EditingArea.Textarea.value ;

		this.FixBody() ;

		var oDoc = FCK.EditorDocument ;
		if ( !oDoc )
			return null ;

		var isFullPage = FCKConfig.FullPage ;

		// Call the Data Processor to generate the output data.
		var data = FCK.DataProcessor.ConvertToDataFormat(
			isFullPage ? oDoc.documentElement : oDoc.body,
			!isFullPage,
			FCKConfig.IgnoreEmptyParagraphValue,
			format ) ;

		// Restore protected attributes.
		data = FCK.ProtectEventsRestore( data ) ;

		if ( FCKBrowserInfo.IsIE )
			data = data.replace( FCKRegexLib.ToReplace, '$1' ) ;

		if ( isFullPage )
		{
			if ( FCK.DocTypeDeclaration && FCK.DocTypeDeclaration.length > 0 )
				data = FCK.DocTypeDeclaration + '\n' + data ;

			if ( FCK.XmlDeclaration && FCK.XmlDeclaration.length > 0 )
				data = FCK.XmlDeclaration + '\n' + data ;
		}

		return FCKConfig.ProtectedSource.Revert( data ) ;
	},

	UpdateLinkedField : function()
	{
		var value = FCK.GetXHTML( FCKConfig.FormatOutput ) ;

		if ( FCKConfig.HtmlEncodeOutput )
			value = FCKTools.HTMLEncode( value ) ;

		FCK.LinkedField.value = value ;
		FCK.Events.FireEvent( 'OnAfterLinkedFieldUpdate' ) ;
	},

	RegisteredDoubleClickHandlers : new Object(),

	OnDoubleClick : function( element )
	{
		var oCalls = FCK.RegisteredDoubleClickHandlers[ element.tagName.toUpperCase() ] ;

		if ( oCalls )
		{
			for ( var i = 0 ; i < oCalls.length ; i++ )
				oCalls[ i ]( element ) ;
		}

		// Generic handler for any element
		oCalls = FCK.RegisteredDoubleClickHandlers[ '*' ] ;

		if ( oCalls )
		{
			for ( var i = 0 ; i < oCalls.length ; i++ )
				oCalls[ i ]( element ) ;
		}

	},

	// Register objects that can handle double click operations.
	RegisterDoubleClickHandler : function( handlerFunction, tag )
	{
		var nodeName = tag || '*' ;
		nodeName = nodeName.toUpperCase() ;

		var aTargets ;

		if ( !( aTargets = FCK.RegisteredDoubleClickHandlers[ nodeName ] ) )
			FCK.RegisteredDoubleClickHandlers[ nodeName ] = [ handlerFunction ] ;
		else
		{
			// Check that the event handler isn't already registered with the same listener
			// It doesn't detect function pointers belonging to an object (at least in Gecko)
			if ( aTargets.IndexOf( handlerFunction ) == -1 )
				aTargets.push( handlerFunction ) ;
		}

	},

	OnAfterSetHTML : function()
	{
		FCKDocumentProcessor.Process( FCK.EditorDocument ) ;
		FCKUndo.SaveUndoStep() ;

		FCK.Events.FireEvent( 'OnSelectionChange' ) ;
		FCK.Events.FireEvent( 'OnAfterSetHTML' ) ;
	},

	// Saves URLs on links and images on special attributes, so they don't change when
	// moving around.
	ProtectUrls : function( html )
	{
		// <A> href
		html = html.replace( FCKRegexLib.ProtectUrlsA	, '$& _fcksavedurl=$1' ) ;

		// <IMG> src
		html = html.replace( FCKRegexLib.ProtectUrlsImg	, '$& _fcksavedurl=$1' ) ;

		// <AREA> href
		html = html.replace( FCKRegexLib.ProtectUrlsArea	, '$& _fcksavedurl=$1' ) ;

		return html ;
	},

	// Saves event attributes (like onclick) so they don't get executed while
	// editing.
	ProtectEvents : function( html )
	{
		return html.replace( FCKRegexLib.TagsWithEvent, _FCK_ProtectEvents_ReplaceTags ) ;
	},

	ProtectEventsRestore : function( html )
	{
		return html.replace( FCKRegexLib.ProtectedEvents, _FCK_ProtectEvents_RestoreEvents ) ;
	},

	ProtectTags : function( html )
	{
		var sTags = FCKConfig.ProtectedTags ;

		// IE doesn't support <abbr> and it breaks it. Let's protect it.
		if ( FCKBrowserInfo.IsIE )
			sTags += sTags.length > 0 ? '|ABBR|XML|EMBED|OBJECT' : 'ABBR|XML|EMBED|OBJECT' ;

		var oRegex ;
		if ( sTags.length > 0 )
		{
			oRegex = new RegExp( '<(' + sTags + ')(?!\w|:)', 'gi' ) ;
			html = html.replace( oRegex, '<FCK:$1' ) ;

			oRegex = new RegExp( '<\/(' + sTags + ')>', 'gi' ) ;
			html = html.replace( oRegex, '<\/FCK:$1>' ) ;
		}

		// Protect some empty elements. We must do it separately because the
		// original tag may not contain the closing slash, like <hr>:
		//		- <meta> tags get executed, so if you have a redirect meta, the
		//		  content will move to the target page.
		//		- <hr> may destroy the document structure if not well
		//		  positioned. The trick is protect it here and restore them in
		//		  the FCKDocumentProcessor.
		sTags = 'META' ;
		if ( FCKBrowserInfo.IsIE )
			sTags += '|HR' ;

		oRegex = new RegExp( '<((' + sTags + ')(?=\\s|>|/)[\\s\\S]*?)/?>', 'gi' ) ;
		html = html.replace( oRegex, '<FCK:$1 />' ) ;

		return html ;
	},

	SetData : function( data, resetIsDirty )
	{
		this.EditingArea.Mode = FCK.EditMode ;

		// If there was an onSelectionChange listener in IE we must remove it to avoid crashes #1498
		if ( FCKBrowserInfo.IsIE && FCK.EditorDocument )
		{
			FCK.EditorDocument.detachEvent("onselectionchange", Doc_OnSelectionChange ) ;
		}

		if ( FCK.EditMode == FCK_EDITMODE_WYSIWYG )
		{
			// Save the resetIsDirty for later use (async)
			this._ForceResetIsDirty = ( resetIsDirty === true ) ;

			// Protect parts of the code that must remain untouched (and invisible)
			// during editing.
			data = FCKConfig.ProtectedSource.Protect( data ) ;

			// Call the Data Processor to transform the data.
			data = FCK.DataProcessor.ConvertToHtml( data ) ;

			// Fix for invalid self-closing tags (see #152).
			data = data.replace( FCKRegexLib.InvalidSelfCloseTags, '$1></$2>' ) ;

			// Protect event attributes (they could get fired in the editing area).
			data = FCK.ProtectEvents( data ) ;

			// Protect some things from the browser itself.
			data = FCK.ProtectUrls( data ) ;
			data = FCK.ProtectTags( data ) ;

			// Insert the base tag (FCKConfig.BaseHref), if not exists in the source.
			// The base must be the first tag in the HEAD, to get relative
			// links on styles, for example.
			if ( FCK.TempBaseTag.length > 0 && !FCKRegexLib.HasBaseTag.test( data ) )
				data = data.replace( FCKRegexLib.HeadOpener, '$&' + FCK.TempBaseTag ) ;

			// Build the HTML for the additional things we need on <head>.
			var sHeadExtra = '' ;

			if ( !FCKConfig.FullPage )
				sHeadExtra += _FCK_GetEditorAreaStyleTags() ;

			if ( FCKBrowserInfo.IsIE )
				sHeadExtra += FCK._GetBehaviorsStyle() ;
			else if ( FCKConfig.ShowBorders )
				sHeadExtra += FCKTools.GetStyleHtml( FCK_ShowTableBordersCSS, true ) ;

			sHeadExtra += FCKTools.GetStyleHtml( FCK_InternalCSS, true ) ;

			// Attention: do not change it before testing it well (sample07)!
			// This is tricky... if the head ends with <meta ... content type>,
			// Firefox will break. But, it works if we place our extra stuff as
			// the last elements in the HEAD.
			data = data.replace( FCKRegexLib.HeadCloser, sHeadExtra + '$&' ) ;

			// Load the HTML in the editing area.
			this.EditingArea.OnLoad = _FCK_EditingArea_OnLoad ;
			this.EditingArea.Start( data ) ;
		}
		else
		{
			// Remove the references to the following elements, as the editing area
			// IFRAME will be removed.
			FCK.EditorWindow	= null ;
			FCK.EditorDocument	= null ;
			FCKDomTools.PaddingNode = null ;

			this.EditingArea.OnLoad = null ;
			this.EditingArea.Start( data ) ;

			// Enables the context menu in the textarea.
			this.EditingArea.Textarea._FCKShowContextMenu = true ;

			// Removes the enter key handler.
			FCK.EnterKeyHandler = null ;

			if ( resetIsDirty )
				this.ResetIsDirty() ;

			// Listen for keystroke events.
			FCK.KeystrokeHandler.AttachToElement( this.EditingArea.Textarea ) ;

			this.EditingArea.Textarea.focus() ;

			FCK.Events.FireEvent( 'OnAfterSetHTML' ) ;
		}

		if ( FCKBrowserInfo.IsGecko )
			window.onresize() ;
	},

	// This collection is used by the browser specific implementations to tell
	// which named commands must be handled separately.
	RedirectNamedCommands : new Object(),

	ExecuteNamedCommand : function( commandName, commandParameter, noRedirect, noSaveUndo )
	{
		if ( !noSaveUndo )
			FCKUndo.SaveUndoStep() ;

		if ( !noRedirect && FCK.RedirectNamedCommands[ commandName ] != null )
			FCK.ExecuteRedirectedNamedCommand( commandName, commandParameter ) ;
		else
		{
			FCK.Focus() ;
			FCK.EditorDocument.execCommand( commandName, false, commandParameter ) ;
			FCK.Events.FireEvent( 'OnSelectionChange' ) ;
		}

		if ( !noSaveUndo )
		FCKUndo.SaveUndoStep() ;
	},

	GetNamedCommandState : function( commandName )
	{
		try
		{

			// Bug #50 : Safari never returns positive state for the Paste command, override that.
			if ( FCKBrowserInfo.IsSafari && FCK.EditorWindow && commandName.IEquals( 'Paste' ) )
				return FCK_TRISTATE_OFF ;

			if ( !FCK.EditorDocument.queryCommandEnabled( commandName ) )
				return FCK_TRISTATE_DISABLED ;
			else
			{
				return FCK.EditorDocument.queryCommandState( commandName ) ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF ;
			}
		}
		catch ( e )
		{
			return FCK_TRISTATE_OFF ;
		}
	},

	GetNamedCommandValue : function( commandName )
	{
		var sValue = '' ;
		var eState = FCK.GetNamedCommandState( commandName ) ;

		if ( eState == FCK_TRISTATE_DISABLED )
			return null ;

		try
		{
			sValue = this.EditorDocument.queryCommandValue( commandName ) ;
		}
		catch(e) {}

		return sValue ? sValue : '' ;
	},

	Paste : function( _callListenersOnly )
	{
		// First call 'OnPaste' listeners.
		if ( FCK.Status != FCK_STATUS_COMPLETE || !FCK.Events.FireEvent( 'OnPaste' ) )
			return false ;

		// Then call the default implementation.
		return _callListenersOnly || FCK._ExecPaste() ;
	},

	PasteFromWord : function()
	{
		FCKDialog.OpenDialog( 'FCKDialog_Paste', FCKLang.PasteFromWord, 'dialog/fck_paste.html', 400, 330, 'Word' ) ;
	},

	Preview : function()
	{
		var sHTML ;

		if ( FCKConfig.FullPage )
		{
			if ( FCK.TempBaseTag.length > 0 )
				sHTML = FCK.TempBaseTag + FCK.GetXHTML() ;
			else
				sHTML = FCK.GetXHTML() ;
		}
		else
		{
			sHTML =
				FCKConfig.DocType +
				'<html dir="' + FCKConfig.ContentLangDirection + '">' +
				'<head>' +
				FCK.TempBaseTag +
				'<title>' + FCKLang.Preview + '</title>' +
				_FCK_GetEditorAreaStyleTags() +
				'</head><body' + FCKConfig.GetBodyAttributes() + '>' +
				FCK.GetXHTML() +
				'</body></html>' ;
		}

		var iWidth	= FCKConfig.ScreenWidth * 0.8 ;
		var iHeight	= FCKConfig.ScreenHeight * 0.7 ;
		var iLeft	= ( FCKConfig.ScreenWidth - iWidth ) / 2 ;

		var sOpenUrl = '' ;
		if ( FCK_IS_CUSTOM_DOMAIN && FCKBrowserInfo.IsIE)
		{
			window._FCKHtmlToLoad = sHTML ;
			sOpenUrl = 'javascript:void( (function(){' +
				'document.open() ;' +
				'document.domain="' + document.domain + '" ;' +
				'document.write( window.opener._FCKHtmlToLoad );' +
				'document.close() ;' +
				'window.opener._FCKHtmlToLoad = null ;' +
				'})() )' ;
		}

		var oWindow = window.open( sOpenUrl, null, 'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width=' + iWidth + ',height=' + iHeight + ',left=' + iLeft ) ;

		if ( !FCK_IS_CUSTOM_DOMAIN || !FCKBrowserInfo.IsIE)
		{
			oWindow.document.write( sHTML );
			oWindow.document.close();
		}

	},

	SwitchEditMode : function( noUndo )
	{
		var bIsWysiwyg = ( FCK.EditMode == FCK_EDITMODE_WYSIWYG ) ;

		// Save the current IsDirty state, so we may restore it after the switch.
		var bIsDirty = FCK.IsDirty() ;

		var sHtml ;

		// Update the HTML in the view output to show.
		if ( bIsWysiwyg )
		{
			FCKCommands.GetCommand( 'ShowBlocks' ).SaveState() ;
			if ( !noUndo && FCKBrowserInfo.IsIE )
				FCKUndo.SaveUndoStep() ;

			sHtml = FCK.GetXHTML( FCKConfig.FormatSource ) ;

			if ( sHtml == null )
				return false ;
		}
		else
			sHtml = this.EditingArea.Textarea.value ;

		FCK.EditMode = bIsWysiwyg ? FCK_EDITMODE_SOURCE : FCK_EDITMODE_WYSIWYG ;

		FCK.SetData( sHtml, !bIsDirty ) ;

		// Set the Focus.
		FCK.Focus() ;

		// Update the toolbar (Running it directly causes IE to fail).
		FCKTools.RunFunction( FCK.ToolbarSet.RefreshModeState, FCK.ToolbarSet ) ;

		return true ;
	},

	InsertElement : function( element )
	{
		// The parameter may be a string (element name), so transform it in an element.
		if ( typeof element == 'string' )
			element = this.EditorDocument.createElement( element ) ;

		var elementName = element.nodeName.toLowerCase() ;

		FCKSelection.Restore() ;

		// Create a range for the selection. V3 will have a new selection
		// object that may internally supply this feature.
		var range = new FCKDomRange( this.EditorWindow ) ;

		// Move to the selection and delete it.
		range.MoveToSelection() ;
		range.DeleteContents() ;

		if ( FCKListsLib.BlockElements[ elementName ] != null )
		{
			if ( range.StartBlock )
			{
				if ( range.CheckStartOfBlock() )
					range.MoveToPosition( range.StartBlock, 3 ) ;
				else if ( range.CheckEndOfBlock() )
					range.MoveToPosition( range.StartBlock, 4 ) ;
				else
					range.SplitBlock() ;
			}

			range.InsertNode( element ) ;

			var next = FCKDomTools.GetNextSourceElement( element, false, null, [ 'hr','br','param','img','area','input' ], true ) ;

			// Be sure that we have something after the new element, so we can move the cursor there.
			if ( !next && FCKConfig.EnterMode != 'br')
			{
				next = this.EditorDocument.body.appendChild( this.EditorDocument.createElement( FCKConfig.EnterMode ) ) ;

				if ( FCKBrowserInfo.IsGeckoLike )
					FCKTools.AppendBogusBr( next ) ;
			}

			if ( FCKListsLib.EmptyElements[ elementName ] == null )
				range.MoveToElementEditStart( element ) ;
			else if ( next )
				range.MoveToElementEditStart( next ) ;
			else
				range.MoveToPosition( element, 4 ) ;

			if ( FCKBrowserInfo.IsGecko )
			{
				if ( next )
					next.scrollIntoView( false ) ;
				element.scrollIntoView( false ) ;
			}
		}
		else
		{
			// Insert the node.
			range.InsertNode( element ) ;

			// Move the selection right after the new element.
			// DISCUSSION: Should we select the element instead?
			range.SetStart( element, 4 ) ;
			range.SetEnd( element, 4 ) ;
		}

		range.Select() ;
		range.Release() ;

		// REMOVE IT: The focus should not really be set here. It is up to the
		// calling code to reset the focus if needed.
		this.Focus() ;

		return element ;
	},

	_InsertBlockElement : function( blockElement )
	{
	},

	_IsFunctionKey : function( keyCode )
	{
		// keys that are captured but do not change editor contents
		if ( keyCode >= 16 && keyCode <= 20 )
			// shift, ctrl, alt, pause, capslock
			return true ;
		if ( keyCode == 27 || ( keyCode >= 33 && keyCode <= 40 ) )
			// esc, page up, page down, end, home, left, up, right, down
			return true ;
		if ( keyCode == 45 )
			// insert, no effect on FCKeditor, yet
			return true ;
		return false ;
	},

	_KeyDownListener : function( evt )
	{
		if (! evt)
			evt = FCK.EditorWindow.event ;
		if ( FCK.EditorWindow )
		{
			if ( !FCK._IsFunctionKey(evt.keyCode) // do not capture function key presses, like arrow keys or shift/alt/ctrl
					&& !(evt.ctrlKey || evt.metaKey) // do not capture Ctrl hotkeys, as they have their snapshot capture logic
					&& !(evt.keyCode == 46) ) // do not capture Del, it has its own capture logic in fckenterkey.js
				FCK._KeyDownUndo() ;
		}
		return true ;
	},

	_KeyDownUndo : function()
	{
		if ( !FCKUndo.Typing )
		{
			FCKUndo.SaveUndoStep() ;
			FCKUndo.Typing = true ;
			FCK.Events.FireEvent( "OnSelectionChange" ) ;
		}

		FCKUndo.TypesCount++ ;
		FCKUndo.Changed = 1 ;

		if ( FCKUndo.TypesCount > FCKUndo.MaxTypes )
		{
			FCKUndo.TypesCount = 0 ;
			FCKUndo.SaveUndoStep() ;
		}
	},

	_TabKeyHandler : function( evt )
	{
		if ( ! evt )
			evt = window.event ;

		var keystrokeValue = evt.keyCode ;

		// Pressing <Tab> in source mode should produce a tab space in the text area, not
		// changing the focus to something else.
		if ( keystrokeValue == 9 && FCK.EditMode != FCK_EDITMODE_WYSIWYG )
		{
			if ( FCKBrowserInfo.IsIE )
			{
				var range = document.selection.createRange() ;
				if ( range.parentElement() != FCK.EditingArea.Textarea )
					return true ;
				range.text = '\t' ;
				range.select() ;
			}
			else
			{
				var a = [] ;
				var el = FCK.EditingArea.Textarea ;
				var selStart = el.selectionStart ;
				var selEnd = el.selectionEnd ;
				a.push( el.value.substr(0, selStart ) ) ;
				a.push( '\t' ) ;
				a.push( el.value.substr( selEnd ) ) ;
				el.value = a.join( '' ) ;
				el.setSelectionRange( selStart + 1, selStart + 1 ) ;
			}

			if ( evt.preventDefault )
				return evt.preventDefault() ;

			return evt.returnValue = false ;
		}

		return true ;
	}
} ;

FCK.Events = new FCKEvents( FCK ) ;

// DEPRECATED in favor or "GetData".
FCK.GetHTML	= FCK.GetXHTML = FCK.GetData ;

// DEPRECATED in favor of "SetData".
FCK.SetHTML = FCK.SetData ;

// InsertElementAndGetIt and CreateElement are Deprecated : returns the same value as InsertElement.
FCK.InsertElementAndGetIt = FCK.CreateElement = FCK.InsertElement ;

// Replace all events attributes (like onclick).
function _FCK_ProtectEvents_ReplaceTags( tagMatch )
{
	return tagMatch.replace( FCKRegexLib.EventAttributes, _FCK_ProtectEvents_ReplaceEvents ) ;
}

// Replace an event attribute with its respective __fckprotectedatt attribute.
// The original event markup will be encoded and saved as the value of the new
// attribute.
function _FCK_ProtectEvents_ReplaceEvents( eventMatch, attName )
{
	return ' ' + attName + '_fckprotectedatt="' + encodeURIComponent( eventMatch ) + '"' ;
}

function _FCK_ProtectEvents_RestoreEvents( match, encodedOriginal )
{
	return decodeURIComponent( encodedOriginal ) ;
}

function _FCK_MouseEventsListener( evt )
{
	if ( ! evt )
		evt = window.event ;
	if ( evt.type == 'mousedown' )
		FCK.MouseDownFlag = true ;
	else if ( evt.type == 'mouseup' )
		FCK.MouseDownFlag = false ;
	else if ( evt.type == 'mousemove' )
		FCK.Events.FireEvent( 'OnMouseMove', evt ) ;
}

function _FCK_PaddingNodeListener()
{
	if ( FCKConfig.EnterMode.IEquals( 'br' ) )
		return ;
	FCKDomTools.EnforcePaddingNode( FCK.EditorDocument, FCKConfig.EnterMode ) ;

	if ( ! FCKBrowserInfo.IsIE && FCKDomTools.PaddingNode )
	{
		// Prevent the caret from going between the body and the padding node in Firefox.
		// i.e. <body>|<p></p></body>
		var sel = FCKSelection.GetSelection() ;
		if ( sel && sel.rangeCount == 1 )
		{
			var range = sel.getRangeAt( 0 ) ;
			if ( range.collapsed && range.startContainer == FCK.EditorDocument.body && range.startOffset == 0 )
			{
				range.selectNodeContents( FCKDomTools.PaddingNode ) ;
				range.collapse( true ) ;
				sel.removeAllRanges() ;
				sel.addRange( range ) ;
			}
		}
	}
	else if ( FCKDomTools.PaddingNode )
	{
		// Prevent the caret from going into an empty body but not into the padding node in IE.
		// i.e. <body><p></p>|</body>
		var parentElement = FCKSelection.GetParentElement() ;
		var paddingNode = FCKDomTools.PaddingNode ;
		if ( parentElement && parentElement.nodeName.IEquals( 'body' ) )
		{
			if ( FCK.EditorDocument.body.childNodes.length == 1
					&& FCK.EditorDocument.body.firstChild == paddingNode )
			{
				/*
				 * Bug #1764: Don't move the selection if the
				 * current selection isn't in the editor
				 * document.
				 */
				if ( FCKSelection._GetSelectionDocument( FCK.EditorDocument.selection ) != FCK.EditorDocument )
					return ;

				var range = FCK.EditorDocument.body.createTextRange() ;
				var clearContents = false ;
				if ( !paddingNode.childNodes.firstChild )
				{
					paddingNode.appendChild( FCKTools.GetElementDocument( paddingNode ).createTextNode( '\ufeff' ) ) ;
					clearContents = true ;
				}
				range.moveToElementText( paddingNode ) ;
				range.select() ;
				if ( clearContents )
					range.pasteHTML( '' ) ;
			}
		}
	}
}

function _FCK_EditingArea_OnLoad()
{
	// Get the editor's window and document (DOM)
	FCK.EditorWindow	= FCK.EditingArea.Window ;
	FCK.EditorDocument	= FCK.EditingArea.Document ;

	FCK.InitializeBehaviors() ;

	// Listen for mousedown and mouseup events for tracking drag and drops.
	FCK.MouseDownFlag = false ;
	FCKTools.AddEventListener( FCK.EditorDocument, 'mousemove', _FCK_MouseEventsListener ) ;
	FCKTools.AddEventListener( FCK.EditorDocument, 'mousedown', _FCK_MouseEventsListener ) ;
	FCKTools.AddEventListener( FCK.EditorDocument, 'mouseup', _FCK_MouseEventsListener ) ;

	// Most of the CTRL key combos do not work under Safari for onkeydown and onkeypress (See #1119)
	// But we can use the keyup event to override some of these...
	if ( FCKBrowserInfo.IsSafari )
	{
		var undoFunc = function( evt )
		{
			if ( ! ( evt.ctrlKey || evt.metaKey ) )
				return ;
			if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
				return ;
			switch ( evt.keyCode )
			{
				case 89:
					FCKUndo.Redo() ;
					break ;
				case 90:
					FCKUndo.Undo() ;
					break ;
			}
		}

		FCKTools.AddEventListener( FCK.EditorDocument, 'keyup', undoFunc ) ;
	}

	// Create the enter key handler
	FCK.EnterKeyHandler = new FCKEnterKey( FCK.EditorWindow, FCKConfig.EnterMode, FCKConfig.ShiftEnterMode, FCKConfig.TabSpaces ) ;

	// Listen for keystroke events.
	FCK.KeystrokeHandler.AttachToElement( FCK.EditorDocument ) ;

	if ( FCK._ForceResetIsDirty )
		FCK.ResetIsDirty() ;

	// This is a tricky thing for IE. In some cases, even if the cursor is
	// blinking in the editing, the keystroke handler doesn't catch keyboard
	// events. We must activate the editing area to make it work. (#142).
	if ( FCKBrowserInfo.IsIE && FCK.HasFocus )
		FCK.EditorDocument.body.setActive() ;

	FCK.OnAfterSetHTML() ;

	// Restore show blocks status.
	FCKCommands.GetCommand( 'ShowBlocks' ).RestoreState() ;

	// Check if it is not a startup call, otherwise complete the startup.
	if ( FCK.Status != FCK_STATUS_NOTLOADED )
		return ;

	FCK.SetStatus( FCK_STATUS_ACTIVE ) ;
}

function _FCK_GetEditorAreaStyleTags()
{
	return FCKTools.GetStyleHtml( FCKConfig.EditorAreaCSS ) +
		FCKTools.GetStyleHtml( FCKConfig.EditorAreaStyles ) ;
}

function _FCK_KeystrokeHandler_OnKeystroke( keystroke, keystrokeValue )
{
	if ( FCK.Status != FCK_STATUS_COMPLETE )
		return false ;

	if ( FCK.EditMode == FCK_EDITMODE_WYSIWYG )
	{
		switch ( keystrokeValue )
		{
			case 'Paste' :
				return !FCK.Paste() ;

			case 'Cut' :
				FCKUndo.SaveUndoStep() ;
				return false ;
		}
	}
	else
	{
		// In source mode, some actions must have their default behavior.
		if ( keystrokeValue.Equals( 'Paste', 'Undo', 'Redo', 'SelectAll', 'Cut' ) )
			return false ;
	}

	// The return value indicates if the default behavior of the keystroke must
	// be cancelled. Let's do that only if the Execute() call explicitly returns "false".
	var oCommand = FCK.Commands.GetCommand( keystrokeValue ) ;

	// If the command is disabled then ignore the keystroke
	if ( oCommand.GetState() == FCK_TRISTATE_DISABLED )
		return false ;

	return ( oCommand.Execute.apply( oCommand, FCKTools.ArgumentsToArray( arguments, 2 ) ) !== false ) ;
}

// Set the FCK.LinkedField reference to the field that will be used to post the
// editor data.
(function()
{
	// There is a bug on IE... getElementById returns any META tag that has the
	// name set to the ID you are looking for. So the best way in to get the array
	// by names and look for the correct one.
	// As ASP.Net generates a ID that is different from the Name, we must also
	// look for the field based on the ID (the first one is the ID).

	var oDocument = window.parent.document ;

	// Try to get the field using the ID.
	var eLinkedField = oDocument.getElementById( FCK.Name ) ;

	var i = 0;
	while ( eLinkedField || i == 0 )
	{
		if ( eLinkedField && eLinkedField.tagName.toLowerCase().Equals( 'input', 'textarea' ) )
		{
			FCK.LinkedField = eLinkedField ;
			break ;
		}

		eLinkedField = oDocument.getElementsByName( FCK.Name )[i++] ;
	}
})() ;

var FCKTempBin =
{
	Elements : new Array(),

	AddElement : function( element )
	{
		var iIndex = this.Elements.length ;
		this.Elements[ iIndex ] = element ;
		return iIndex ;
	},

	RemoveElement : function( index )
	{
		var e = this.Elements[ index ] ;
		this.Elements[ index ] = null ;
		return e ;
	},

	Reset : function()
	{
		var i = 0 ;
		while ( i < this.Elements.length )
			this.Elements[ i++ ] = null ;
		this.Elements.length = 0 ;
	}
} ;



// # Focus Manager: Manages the focus in the editor.
var FCKFocusManager = FCK.FocusManager =
{
	IsLocked : false,

	AddWindow : function( win, sendToEditingArea )
	{
		var oTarget ;

		if ( FCKBrowserInfo.IsIE )
			oTarget = win.nodeType == 1 ? win : win.frameElement ? win.frameElement : win.document ;
		else if ( FCKBrowserInfo.IsSafari )
			oTarget = win ;
		else
			oTarget = win.document ;

		FCKTools.AddEventListener( oTarget, 'blur', FCKFocusManager_Win_OnBlur ) ;
		FCKTools.AddEventListener( oTarget, 'focus', sendToEditingArea ? FCKFocusManager_Win_OnFocus_Area : FCKFocusManager_Win_OnFocus ) ;
	},

	RemoveWindow : function( win )
	{
		if ( FCKBrowserInfo.IsIE )
			oTarget = win.nodeType == 1 ? win : win.frameElement ? win.frameElement : win.document ;
		else
			oTarget = win.document ;

		FCKTools.RemoveEventListener( oTarget, 'blur', FCKFocusManager_Win_OnBlur ) ;
		FCKTools.RemoveEventListener( oTarget, 'focus', FCKFocusManager_Win_OnFocus_Area ) ;
		FCKTools.RemoveEventListener( oTarget, 'focus', FCKFocusManager_Win_OnFocus ) ;
	},

	Lock : function()
	{
		this.IsLocked = true ;
	},

	Unlock : function()
	{
		if ( this._HasPendingBlur )
			FCKFocusManager._Timer = window.setTimeout( FCKFocusManager_FireOnBlur, 100 ) ;

		this.IsLocked = false ;
	},

	_ResetTimer : function()
	{
		this._HasPendingBlur = false ;

		if ( this._Timer )
		{
			window.clearTimeout( this._Timer ) ;
			delete this._Timer ;
		}
	}
} ;

function FCKFocusManager_Win_OnBlur()
{
	if ( typeof(FCK) != 'undefined' && FCK.HasFocus )
	{
		FCKFocusManager._ResetTimer() ;
		FCKFocusManager._Timer = window.setTimeout( FCKFocusManager_FireOnBlur, 100 ) ;
	}
}

function FCKFocusManager_FireOnBlur()
{
	if ( FCKFocusManager.IsLocked )
		FCKFocusManager._HasPendingBlur = true ;
	else
	{
		FCK.HasFocus = false ;
		FCK.Events.FireEvent( "OnBlur" ) ;
	}
}

function FCKFocusManager_Win_OnFocus_Area()
{
	// Check if we are already focusing the editor (to avoid loops).
	if ( FCKFocusManager._IsFocusing )
		return ;

	FCKFocusManager._IsFocusing = true ;

	FCK.Focus() ;
	FCKFocusManager_Win_OnFocus() ;

	// The above FCK.Focus() call may trigger other focus related functions.
	// So, to avoid a loop, we delay the focusing mark removal, so it get
	// executed after all othre functions have been run.
	FCKTools.RunFunction( function()
		{
			delete FCKFocusManager._IsFocusing ;
		} ) ;
}

function FCKFocusManager_Win_OnFocus()
{
	FCKFocusManager._ResetTimer() ;

	if ( !FCK.HasFocus && !FCKFocusManager.IsLocked )
	{
		FCK.HasFocus = true ;
		FCK.Events.FireEvent( "OnFocus" ) ;
	}
}

/*
 * #1633 : Protect the editor iframe from external styles.
 * Notice that we can't use FCKTools.ResetStyles here since FCKTools isn't
 * loaded yet.
 */
(function()
{
	var el = window.frameElement ;
	var width = el.width ;
	var height = el.height ;
	if ( /^\d+$/.test( width ) ) width += 'px' ;
	if ( /^\d+$/.test( height ) ) height += 'px' ;
	var style = el.style ;
	style.border = style.padding = style.margin = 0 ;
	style.backgroundColor = 'transparent';
	style.backgroundImage = 'none';
	style.width = width ;
	style.height = height ;
})() ;

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Bangladesh Bangladesh
Software Engineer, Bangladesh.

Comments and Discussions