PageRenderTime 22ms CodeModel.GetById 1ms app.highlight 15ms RepoModel.GetById 2ms app.codeStats 0ms

/source/Plug-in/fck/editor/_source/classes/fckeditingarea.js

http://prosporous.googlecode.com/
JavaScript | 324 lines | 190 code | 54 blank | 80 comment | 35 complexity | 662474092a841b94da3070b5b86b5851 MD5 | raw file
  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 * FCKEditingArea Class: renders an editable area.
 22 */
 23
 24/**
 25 * @constructor
 26 * @param {String} targetElement The element that will hold the editing area. Any child element present in the target will be deleted.
 27 */
 28var FCKEditingArea = function( targetElement )
 29{
 30	this.TargetElement = targetElement ;
 31	this.Mode = FCK_EDITMODE_WYSIWYG ;
 32
 33	if ( FCK.IECleanup )
 34		FCK.IECleanup.AddItem( this, FCKEditingArea_Cleanup ) ;
 35}
 36
 37
 38/**
 39 * @param {String} html The complete HTML for the page, including DOCTYPE and the <html> tag.
 40 */
 41FCKEditingArea.prototype.Start = function( html, secondCall )
 42{
 43	var eTargetElement	= this.TargetElement ;
 44	var oTargetDocument	= FCKTools.GetElementDocument( eTargetElement ) ;
 45
 46	// Remove all child nodes from the target.
 47	var oChild ;
 48	while( ( oChild = eTargetElement.firstChild ) )		// Only one "=".
 49	{
 50		// Set innerHTML = '' to avoid memory leak.
 51		if ( oChild.contentWindow )
 52			oChild.contentWindow.document.body.innerHTML = '' ;
 53
 54		eTargetElement.removeChild( oChild ) ;
 55	}
 56
 57	if ( this.Mode == FCK_EDITMODE_WYSIWYG )
 58	{
 59		// Create the editing area IFRAME.
 60		var oIFrame = this.IFrame = oTargetDocument.createElement( 'iframe' ) ;
 61		
 62		// Firefox will render the tables inside the body in Quirks mode if the 
 63		// source of the iframe is set to javascript. see #515
 64		if ( !FCKBrowserInfo.IsGecko )
 65			oIFrame.src = 'javascript:void(0)' ;
 66		
 67		oIFrame.frameBorder = 0 ;
 68		oIFrame.width = oIFrame.height = '100%' ;
 69
 70		// Append the new IFRAME to the target.
 71		eTargetElement.appendChild( oIFrame ) ;
 72
 73		// IE has a bug with the <base> tag... it must have a </base> closer,
 74		// otherwise the all successive tags will be set as children nodes of the <base>.
 75		if ( FCKBrowserInfo.IsIE )
 76			html = html.replace( /(<base[^>]*?)\s*\/?>(?!\s*<\/base>)/gi, '$1></base>' ) ;
 77		else if ( !secondCall )
 78		{
 79			// Gecko moves some tags out of the body to the head, so we must use
 80			// innerHTML to set the body contents (SF BUG 1526154).
 81
 82			// Extract the BODY contents from the html.
 83			var oMatchBefore = html.match( FCKRegexLib.BeforeBody ) ;
 84			var oMatchAfter = html.match( FCKRegexLib.AfterBody ) ;
 85
 86			if ( oMatchBefore && oMatchAfter )
 87			{
 88				var sBody = html.substr( oMatchBefore[1].length,
 89					       html.length - oMatchBefore[1].length - oMatchAfter[1].length ) ;	// This is the BODY tag contents.
 90
 91				html =
 92					oMatchBefore[1] +			// This is the HTML until the <body...> tag, inclusive.
 93					'&nbsp;' +
 94					oMatchAfter[1] ;			// This is the HTML from the </body> tag, inclusive.
 95
 96				// If nothing in the body, place a BOGUS tag so the cursor will appear.
 97				if ( FCKBrowserInfo.IsGecko && ( sBody.length == 0 || FCKRegexLib.EmptyParagraph.test( sBody ) ) )
 98					sBody = '<br type="_moz">' ;
 99
100				this._BodyHTML = sBody ;
101
102			}
103			else
104				this._BodyHTML = html ;			// Invalid HTML input.
105		}
106
107		// Get the window and document objects used to interact with the newly created IFRAME.
108		this.Window = oIFrame.contentWindow ;
109
110		// IE: Avoid JavaScript errors thrown by the editing are source (like tags events).
111		// TODO: This error handler is not being fired.
112		// this.Window.onerror = function() { alert( 'Error!' ) ; return true ; }
113
114		var oDoc = this.Document = this.Window.document ;
115
116		oDoc.open() ;
117		oDoc.write( html ) ;
118		oDoc.close() ;
119
120		// Firefox 1.0.x is buggy... ohh yes... so let's do it two times and it
121		// will magically work.
122		if ( FCKBrowserInfo.IsGecko10 && !secondCall )
123		{
124			this.Start( html, true ) ;
125			return ;
126		}
127
128		this.Window._FCKEditingArea = this ;
129
130		// FF 1.0.x is buggy... we must wait a lot to enable editing because
131		// sometimes the content simply disappears, for example when pasting
132		// "bla1!<img src='some_url'>!bla2" in the source and then switching
133		// back to design.
134		if ( FCKBrowserInfo.IsGecko10 )
135			this.Window.setTimeout( FCKEditingArea_CompleteStart, 500 ) ;
136		else
137			FCKEditingArea_CompleteStart.call( this.Window ) ;
138	}
139	else
140	{
141		var eTextarea = this.Textarea = oTargetDocument.createElement( 'textarea' ) ;
142		eTextarea.className = 'SourceField' ;
143		eTextarea.dir = 'ltr' ;
144		FCKDomTools.SetElementStyles( eTextarea, 
145			{ 
146				width	: '100%', 
147				height	: '100%', 
148				border	: 'none', 
149				resize	: 'none',
150				outline	: 'none'
151			} ) ;
152		eTargetElement.appendChild( eTextarea ) ;
153
154		eTextarea.value = html  ;
155
156		// Fire the "OnLoad" event.
157		FCKTools.RunFunction( this.OnLoad ) ;
158	}
159}
160
161// "this" here is FCKEditingArea.Window
162function FCKEditingArea_CompleteStart()
163{
164	// On Firefox, the DOM takes a little to become available. So we must wait for it in a loop.
165	if ( !this.document.body )
166	{
167		this.setTimeout( FCKEditingArea_CompleteStart, 50 ) ;
168		return ;
169	}
170
171	var oEditorArea = this._FCKEditingArea ;
172	
173	oEditorArea.MakeEditable() ;
174
175	// Fire the "OnLoad" event.
176	FCKTools.RunFunction( oEditorArea.OnLoad ) ;
177}
178
179FCKEditingArea.prototype.MakeEditable = function()
180{
181	var oDoc = this.Document ;
182
183	if ( FCKBrowserInfo.IsIE )
184	{
185		// Kludge for #141 and #523
186		oDoc.body.disabled = true ;
187		oDoc.body.contentEditable = true ;
188		oDoc.body.removeAttribute( "disabled" ) ;
189
190		/* The following commands don't throw errors, but have no effect.
191		oDoc.execCommand( 'AutoDetect', false, false ) ;
192		oDoc.execCommand( 'KeepSelection', false, true ) ;
193		*/
194	}
195	else
196	{
197		try
198		{
199			// Disable Firefox 2 Spell Checker.
200			oDoc.body.spellcheck = ( this.FFSpellChecker !== false ) ;
201
202			if ( this._BodyHTML )
203			{
204				oDoc.body.innerHTML = this._BodyHTML ;
205				this._BodyHTML = null ;
206			}
207
208			oDoc.designMode = 'on' ;
209
210			// Tell Gecko (Firefox 1.5+) to enable or not live resizing of objects (by Alfonso Martinez)
211			oDoc.execCommand( 'enableObjectResizing', false, !FCKConfig.DisableObjectResizing ) ;
212
213			// Disable the standard table editing features of Firefox.
214			oDoc.execCommand( 'enableInlineTableEditing', false, !FCKConfig.DisableFFTableHandles ) ;
215		}
216		catch (e) 
217		{
218			// In Firefox if the iframe is initially hidden it can't be set to designMode and it raises an exception
219			// So we set up a DOM Mutation event Listener on the HTML, as it will raise several events when the document is  visible again
220			FCKTools.AddEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ;
221		}
222
223	}
224}
225
226// This function processes the notifications of the DOM Mutation event on the document
227// We use it to know that the document will be ready to be editable again (or we hope so)
228function FCKEditingArea_Document_AttributeNodeModified( evt )
229{
230	var editingArea = evt.currentTarget.contentWindow._FCKEditingArea ;
231	
232	// We want to run our function after the events no longer fire, so we can know that it's a stable situation
233	if ( editingArea._timer )
234		window.clearTimeout( editingArea._timer ) ;
235
236	editingArea._timer = FCKTools.SetTimeout( FCKEditingArea_MakeEditableByMutation, 1000, editingArea ) ;	
237}
238
239// This function ideally should be called after the document is visible, it does clean up of the
240// mutation tracking and tries again to make the area editable.
241function FCKEditingArea_MakeEditableByMutation()
242{
243	// Clean up
244	delete this._timer ;
245	// Now we don't want to keep on getting this event
246	FCKTools.RemoveEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ;
247	// Let's try now to set the editing area editable
248	// If it fails it will set up the Mutation Listener again automatically
249	this.MakeEditable() ;
250}
251
252FCKEditingArea.prototype.Focus = function()
253{
254	try
255	{
256		if ( this.Mode == FCK_EDITMODE_WYSIWYG )
257		{
258			// The following check is important to avoid IE entering in a focus loop. Ref:
259			// http://sourceforge.net/tracker/index.php?func=detail&aid=1567060&group_id=75348&atid=543653
260			if ( FCKBrowserInfo.IsIE && this.Document.hasFocus() )
261				this._EnsureFocusIE() ;
262
263			this.Window.focus() ;
264
265			// In IE it can happen that the document is in theory focused but the active element is outside it
266			if ( FCKBrowserInfo.IsIE )
267				this._EnsureFocusIE() ;
268		}
269		else
270		{
271			var oDoc = FCKTools.GetElementDocument( this.Textarea ) ;
272			if ( (!oDoc.hasFocus || oDoc.hasFocus() ) && oDoc.activeElement == this.Textarea )
273				return ;
274
275			this.Textarea.focus() ;
276		}
277	}
278	catch(e) {}
279}
280
281FCKEditingArea.prototype._EnsureFocusIE = function()
282{
283	// In IE it can happen that the document is in theory focused but the active element is outside it
284	this.Document.body.setActive() ;
285
286	// Kludge for #141... yet more code to workaround IE bugs
287	var range = this.Document.selection.createRange() ;
288
289	var parentNode = range.parentElement() ;
290	var parentTag = parentNode.nodeName.toLowerCase() ;
291
292	// Only apply the fix when in a block, and the block is empty.
293	if ( parentNode.childNodes.length > 0 ||
294		 !( FCKListsLib.BlockElements[parentTag] || 
295		    FCKListsLib.NonEmptyBlockElements[parentTag] ) )
296	{
297		return ;
298	}
299
300	range.moveEnd( "character", 1 ) ;
301	range.select() ;
302
303	if ( range.boundingWidth > 0 )
304	{
305		range.moveEnd( "character", -1 ) ;
306		range.select() ;
307	}
308}
309
310function FCKEditingArea_Cleanup()
311{
312	if ( this.Document )
313		this.Document.body.innerHTML = "" ;
314	this.TargetElement = null ;
315	this.IFrame = null ;
316	this.Document = null ;
317	this.Textarea = null ;
318
319	if ( this.Window )
320	{
321		this.Window._FCKEditingArea = null ;
322		this.Window = null ;
323	}
324}