2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
\r
3 * Copyright (C) 2003-2009 Frederico Caldeira Knabben
\r
5 * == BEGIN LICENSE ==
\r
7 * Licensed under the terms of any of the following licenses at your
\r
10 * - GNU General Public License Version 2 or later (the "GPL")
\r
11 * http://www.gnu.org/licenses/gpl.html
\r
13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
\r
14 * http://www.gnu.org/licenses/lgpl.html
\r
16 * - Mozilla Public License Version 1.1 or later (the "MPL")
\r
17 * http://www.mozilla.org/MPL/MPL-1.1.html
\r
21 * FCKEditingArea Class: renders an editable area.
\r
26 * @param {String} targetElement The element that will hold the editing area. Any child element present in the target will be deleted.
\r
28 var FCKEditingArea = function( targetElement )
\r
30 this.TargetElement = targetElement ;
\r
31 this.Mode = FCK_EDITMODE_WYSIWYG ;
\r
33 if ( FCK.IECleanup )
\r
34 FCK.IECleanup.AddItem( this, FCKEditingArea_Cleanup ) ;
\r
39 * @param {String} html The complete HTML for the page, including DOCTYPE and the <html> tag.
\r
41 FCKEditingArea.prototype.Start = function( html, secondCall )
\r
43 var eTargetElement = this.TargetElement ;
\r
44 var oTargetDocument = FCKTools.GetElementDocument( eTargetElement ) ;
\r
46 // Remove all child nodes from the target.
\r
47 while( eTargetElement.firstChild )
\r
48 eTargetElement.removeChild( eTargetElement.firstChild ) ;
\r
50 if ( this.Mode == FCK_EDITMODE_WYSIWYG )
\r
52 // For FF, document.domain must be set only when different, otherwhise
\r
53 // we'll strangely have "Permission denied" issues.
\r
54 if ( FCK_IS_CUSTOM_DOMAIN )
\r
55 html = '<script>document.domain="' + FCK_RUNTIME_DOMAIN + '";</script>' + html ;
\r
57 // IE has a bug with the <base> tag... it must have a </base> closer,
\r
58 // otherwise the all successive tags will be set as children nodes of the <base>.
\r
59 if ( FCKBrowserInfo.IsIE )
\r
60 html = html.replace( /(<base[^>]*?)\s*\/?>(?!\s*<\/base>)/gi, '$1></base>' ) ;
\r
61 else if ( !secondCall )
\r
63 // Gecko moves some tags out of the body to the head, so we must use
\r
64 // innerHTML to set the body contents (SF BUG 1526154).
\r
66 // Extract the BODY contents from the html.
\r
67 var oMatchBefore = html.match( FCKRegexLib.BeforeBody ) ;
\r
68 var oMatchAfter = html.match( FCKRegexLib.AfterBody ) ;
\r
70 if ( oMatchBefore && oMatchAfter )
\r
72 var sBody = html.substr( oMatchBefore[1].length,
\r
73 html.length - oMatchBefore[1].length - oMatchAfter[1].length ) ; // This is the BODY tag contents.
\r
76 oMatchBefore[1] + // This is the HTML until the <body...> tag, inclusive.
\r
78 oMatchAfter[1] ; // This is the HTML from the </body> tag, inclusive.
\r
80 // If nothing in the body, place a BOGUS tag so the cursor will appear.
\r
81 if ( FCKBrowserInfo.IsGecko && ( sBody.length == 0 || FCKRegexLib.EmptyParagraph.test( sBody ) ) )
\r
82 sBody = '<br type="_moz">' ;
\r
84 this._BodyHTML = sBody ;
\r
88 this._BodyHTML = html ; // Invalid HTML input.
\r
91 // Create the editing area IFRAME.
\r
92 var oIFrame = this.IFrame = oTargetDocument.createElement( 'iframe' ) ;
\r
94 // IE: Avoid JavaScript errors thrown by the editing are source (like tags events).
\r
96 var sOverrideError = '<script type="text/javascript" _fcktemp="true">window.onerror=function(){return true;};</script>' ;
\r
98 oIFrame.frameBorder = 0 ;
\r
99 oIFrame.style.width = oIFrame.style.height = '100%' ;
\r
101 if ( FCK_IS_CUSTOM_DOMAIN && FCKBrowserInfo.IsIE )
\r
103 window._FCKHtmlToLoad = html.replace( /<head>/i, '<head>' + sOverrideError ) ;
\r
104 oIFrame.src = 'javascript:void( (function(){' +
\r
105 'document.open() ;' +
\r
106 'document.domain="' + document.domain + '" ;' +
\r
107 'document.write( window.parent._FCKHtmlToLoad );' +
\r
108 'document.close() ;' +
\r
109 'window.parent._FCKHtmlToLoad = null ;' +
\r
112 else if ( !FCKBrowserInfo.IsGecko )
\r
114 // Firefox will render the tables inside the body in Quirks mode if the
\r
115 // source of the iframe is set to javascript. see #515
\r
116 oIFrame.src = 'javascript:void(0)' ;
\r
119 // Append the new IFRAME to the target. For IE, it must be done after
\r
120 // setting the "src", to avoid the "secure/unsecure" message under HTTPS.
\r
121 eTargetElement.appendChild( oIFrame ) ;
\r
123 // Get the window and document objects used to interact with the newly created IFRAME.
\r
124 this.Window = oIFrame.contentWindow ;
\r
126 // IE: Avoid JavaScript errors thrown by the editing are source (like tags events).
\r
127 // TODO: This error handler is not being fired.
\r
128 // this.Window.onerror = function() { alert( 'Error!' ) ; return true ; }
\r
130 if ( !FCK_IS_CUSTOM_DOMAIN || !FCKBrowserInfo.IsIE )
\r
132 var oDoc = this.Window.document ;
\r
135 oDoc.write( html.replace( /<head>/i, '<head>' + sOverrideError ) ) ;
\r
139 if ( FCKBrowserInfo.IsAIR )
\r
140 FCKAdobeAIR.EditingArea_Start( oDoc, html ) ;
\r
142 // Firefox 1.0.x is buggy... ohh yes... so let's do it two times and it
\r
143 // will magically work.
\r
144 if ( FCKBrowserInfo.IsGecko10 && !secondCall )
\r
146 this.Start( html, true ) ;
\r
150 if ( oIFrame.readyState && oIFrame.readyState != 'completed' )
\r
152 var editArea = this ;
\r
154 // Using a IE alternative for DOMContentLoaded, similar to the
\r
155 // solution proposed at http://javascript.nwbox.com/IEContentLoaded/
\r
156 setTimeout( function()
\r
160 editArea.Window.document.documentElement.doScroll("left") ;
\r
164 setTimeout( arguments.callee, 0 ) ;
\r
167 editArea.Window._FCKEditingArea = editArea ;
\r
168 FCKEditingArea_CompleteStart.call( editArea.Window ) ;
\r
173 this.Window._FCKEditingArea = this ;
\r
175 // FF 1.0.x is buggy... we must wait a lot to enable editing because
\r
176 // sometimes the content simply disappears, for example when pasting
\r
177 // "bla1!<img src='some_url'>!bla2" in the source and then switching
\r
179 if ( FCKBrowserInfo.IsGecko10 )
\r
180 this.Window.setTimeout( FCKEditingArea_CompleteStart, 500 ) ;
\r
182 FCKEditingArea_CompleteStart.call( this.Window ) ;
\r
187 var eTextarea = this.Textarea = oTargetDocument.createElement( 'textarea' ) ;
\r
188 eTextarea.className = 'SourceField' ;
\r
189 eTextarea.dir = 'ltr' ;
\r
190 FCKDomTools.SetElementStyles( eTextarea,
\r
198 eTargetElement.appendChild( eTextarea ) ;
\r
200 eTextarea.value = html ;
\r
202 // Fire the "OnLoad" event.
\r
203 FCKTools.RunFunction( this.OnLoad ) ;
\r
207 // "this" here is FCKEditingArea.Window
\r
208 function FCKEditingArea_CompleteStart()
\r
210 // On Firefox, the DOM takes a little to become available. So we must wait for it in a loop.
\r
211 if ( !this.document.body )
\r
213 this.setTimeout( FCKEditingArea_CompleteStart, 50 ) ;
\r
217 var oEditorArea = this._FCKEditingArea ;
\r
219 // Save this reference to be re-used later.
\r
220 oEditorArea.Document = oEditorArea.Window.document ;
\r
222 oEditorArea.MakeEditable() ;
\r
224 // Fire the "OnLoad" event.
\r
225 FCKTools.RunFunction( oEditorArea.OnLoad ) ;
\r
228 FCKEditingArea.prototype.MakeEditable = function()
\r
230 var oDoc = this.Document ;
\r
232 if ( FCKBrowserInfo.IsIE )
\r
234 // Kludge for #141 and #523
\r
235 oDoc.body.disabled = true ;
\r
236 oDoc.body.contentEditable = true ;
\r
237 oDoc.body.removeAttribute( "disabled" ) ;
\r
239 /* The following commands don't throw errors, but have no effect.
\r
240 oDoc.execCommand( 'AutoDetect', false, false ) ;
\r
241 oDoc.execCommand( 'KeepSelection', false, true ) ;
\r
248 // Disable Firefox 2 Spell Checker.
\r
249 oDoc.body.spellcheck = ( this.FFSpellChecker !== false ) ;
\r
251 if ( this._BodyHTML )
\r
253 oDoc.body.innerHTML = this._BodyHTML ;
\r
254 oDoc.body.offsetLeft ; // Don't remove, this is a hack to fix Opera 9.50, see #2264.
\r
255 this._BodyHTML = null ;
\r
258 oDoc.designMode = 'on' ;
\r
260 // Tell Gecko (Firefox 1.5+) to enable or not live resizing of objects (by Alfonso Martinez)
\r
261 oDoc.execCommand( 'enableObjectResizing', false, !FCKConfig.DisableObjectResizing ) ;
\r
263 // Disable the standard table editing features of Firefox.
\r
264 oDoc.execCommand( 'enableInlineTableEditing', false, !FCKConfig.DisableFFTableHandles ) ;
\r
268 // In Firefox if the iframe is initially hidden it can't be set to designMode and it raises an exception
\r
269 // So we set up a DOM Mutation event Listener on the HTML, as it will raise several events when the document is visible again
\r
270 FCKTools.AddEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ;
\r
276 // This function processes the notifications of the DOM Mutation event on the document
\r
277 // We use it to know that the document will be ready to be editable again (or we hope so)
\r
278 function FCKEditingArea_Document_AttributeNodeModified( evt )
\r
280 var editingArea = evt.currentTarget.contentWindow._FCKEditingArea ;
\r
282 // We want to run our function after the events no longer fire, so we can know that it's a stable situation
\r
283 if ( editingArea._timer )
\r
284 window.clearTimeout( editingArea._timer ) ;
\r
286 editingArea._timer = FCKTools.SetTimeout( FCKEditingArea_MakeEditableByMutation, 1000, editingArea ) ;
\r
289 // This function ideally should be called after the document is visible, it does clean up of the
\r
290 // mutation tracking and tries again to make the area editable.
\r
291 function FCKEditingArea_MakeEditableByMutation()
\r
294 delete this._timer ;
\r
295 // Now we don't want to keep on getting this event
\r
296 FCKTools.RemoveEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ;
\r
297 // Let's try now to set the editing area editable
\r
298 // If it fails it will set up the Mutation Listener again automatically
\r
299 this.MakeEditable() ;
\r
302 FCKEditingArea.prototype.Focus = function()
\r
306 if ( this.Mode == FCK_EDITMODE_WYSIWYG )
\r
308 if ( FCKBrowserInfo.IsIE )
\r
311 this.Window.focus() ;
\r
315 var oDoc = FCKTools.GetElementDocument( this.Textarea ) ;
\r
316 if ( (!oDoc.hasFocus || oDoc.hasFocus() ) && oDoc.activeElement == this.Textarea )
\r
319 this.Textarea.focus() ;
\r
325 FCKEditingArea.prototype._FocusIE = function()
\r
327 // In IE it can happen that the document is in theory focused but the
\r
328 // active element is outside of it.
\r
329 this.Document.body.setActive() ;
\r
331 this.Window.focus() ;
\r
333 // Kludge for #141... yet more code to workaround IE bugs
\r
334 var range = this.Document.selection.createRange() ;
\r
336 var parentNode = range.parentElement() ;
\r
337 var parentTag = parentNode.nodeName.toLowerCase() ;
\r
339 // Only apply the fix when in a block, and the block is empty.
\r
340 if ( parentNode.childNodes.length > 0 ||
\r
341 !( FCKListsLib.BlockElements[parentTag] ||
\r
342 FCKListsLib.NonEmptyBlockElements[parentTag] ) )
\r
347 // Force the selection to happen, in this way we guarantee the focus will
\r
349 range = new FCKDomRange( this.Window ) ;
\r
350 range.MoveToElementEditStart( parentNode ) ;
\r
354 function FCKEditingArea_Cleanup()
\r
356 if ( this.Document )
\r
357 this.Document.body.innerHTML = "" ;
\r
358 this.TargetElement = null ;
\r
359 this.IFrame = null ;
\r
360 this.Document = null ;
\r
361 this.Textarea = null ;
\r
365 this.Window._FCKEditingArea = null ;
\r
366 this.Window = null ;
\r