149
by Leo Iannacone
Added applets for gui editor |
1 |
/*
|
2 |
* FCKeditor - The text editor for Internet - http://www.fckeditor.net
|
|
3 |
* Copyright (C) 2003-2010 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 |
*/
|
|
28 |
var 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 |
*/
|
|
41 |
FCKEditingArea.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 |
while( eTargetElement.firstChild ) |
|
48 |
eTargetElement.removeChild( eTargetElement.firstChild ) ; |
|
49 |
||
50 |
if ( this.Mode == FCK_EDITMODE_WYSIWYG ) |
|
51 |
{
|
|
52 |
// For FF, document.domain must be set only when different, otherwhise
|
|
53 |
// we'll strangely have "Permission denied" issues.
|
|
54 |
if ( FCK_IS_CUSTOM_DOMAIN ) |
|
55 |
html = '<script>document.domain="' + FCK_RUNTIME_DOMAIN + '";</script>' + html ; |
|
56 |
||
57 |
// IE has a bug with the <base> tag... it must have a </base> closer,
|
|
58 |
// otherwise the all successive tags will be set as children nodes of the <base>.
|
|
59 |
if ( FCKBrowserInfo.IsIE ) |
|
60 |
html = html.replace( /(<base[^>]*?)\s*\/?>(?!\s*<\/base>)/gi, '$1></base>' ) ; |
|
61 |
else if ( !secondCall ) |
|
62 |
{
|
|
63 |
// Gecko moves some tags out of the body to the head, so we must use
|
|
64 |
// innerHTML to set the body contents (SF BUG 1526154).
|
|
65 |
||
66 |
// Extract the BODY contents from the html.
|
|
67 |
var oMatchBefore = html.match( FCKRegexLib.BeforeBody ) ; |
|
68 |
var oMatchAfter = html.match( FCKRegexLib.AfterBody ) ; |
|
69 |
||
70 |
if ( oMatchBefore && oMatchAfter ) |
|
71 |
{
|
|
72 |
var sBody = html.substr( oMatchBefore[1].length, |
|
73 |
html.length - oMatchBefore[1].length - oMatchAfter[1].length ) ; // This is the BODY tag contents. |
|
74 |
||
75 |
html = |
|
76 |
oMatchBefore[1] + // This is the HTML until the <body...> tag, inclusive. |
|
77 |
' ' + |
|
78 |
oMatchAfter[1] ; // This is the HTML from the </body> tag, inclusive. |
|
79 |
||
80 |
// If nothing in the body, place a BOGUS tag so the cursor will appear.
|
|
81 |
if ( FCKBrowserInfo.IsGecko && ( sBody.length == 0 || FCKRegexLib.EmptyParagraph.test( sBody ) ) ) |
|
82 |
sBody = '<br type="_moz">' ; |
|
83 |
||
84 |
this._BodyHTML = sBody ; |
|
85 |
||
86 |
}
|
|
87 |
else
|
|
88 |
this._BodyHTML = html ; // Invalid HTML input. |
|
89 |
}
|
|
90 |
||
91 |
// Create the editing area IFRAME.
|
|
92 |
var oIFrame = this.IFrame = oTargetDocument.createElement( 'iframe' ) ; |
|
93 |
||
94 |
// IE: Avoid JavaScript errors thrown by the editing are source (like tags events).
|
|
95 |
// See #1055.
|
|
96 |
var sOverrideError = '<script type="text/javascript" _fcktemp="true">window.onerror=function(){return true;};</script>' ; |
|
97 |
||
98 |
oIFrame.frameBorder = 0 ; |
|
99 |
oIFrame.style.width = oIFrame.style.height = '100%' ; |
|
100 |
||
101 |
if ( FCK_IS_CUSTOM_DOMAIN && FCKBrowserInfo.IsIE ) |
|
102 |
{
|
|
103 |
window._FCKHtmlToLoad = html.replace( /<head>/i, '<head>' + sOverrideError ) ; |
|
104 |
oIFrame.src = 'javascript:void( (function(){' + |
|
105 |
'document.open() ;' + |
|
106 |
'document.domain="' + document.domain + '" ;' + |
|
107 |
'document.write( window.parent._FCKHtmlToLoad );' + |
|
108 |
'document.close() ;' + |
|
109 |
'window.parent._FCKHtmlToLoad = null ;' + |
|
110 |
'})() )' ; |
|
111 |
}
|
|
112 |
else if ( !FCKBrowserInfo.IsGecko ) |
|
113 |
{
|
|
114 |
// Firefox will render the tables inside the body in Quirks mode if the
|
|
115 |
// source of the iframe is set to javascript. see #515
|
|
116 |
oIFrame.src = 'javascript:void(0)' ; |
|
117 |
}
|
|
118 |
||
119 |
// Append the new IFRAME to the target. For IE, it must be done after
|
|
120 |
// setting the "src", to avoid the "secure/unsecure" message under HTTPS.
|
|
121 |
eTargetElement.appendChild( oIFrame ) ; |
|
122 |
||
123 |
// Get the window and document objects used to interact with the newly created IFRAME.
|
|
124 |
this.Window = oIFrame.contentWindow ; |
|
125 |
||
126 |
// IE: Avoid JavaScript errors thrown by the editing are source (like tags events).
|
|
127 |
// TODO: This error handler is not being fired.
|
|
128 |
// this.Window.onerror = function() { alert( 'Error!' ) ; return true ; }
|
|
129 |
||
130 |
if ( !FCK_IS_CUSTOM_DOMAIN || !FCKBrowserInfo.IsIE ) |
|
131 |
{
|
|
132 |
var oDoc = this.Window.document ; |
|
133 |
||
134 |
oDoc.open() ; |
|
135 |
oDoc.write( html.replace( /<head>/i, '<head>' + sOverrideError ) ) ; |
|
136 |
oDoc.close() ; |
|
137 |
}
|
|
138 |
||
139 |
if ( FCKBrowserInfo.IsAIR ) |
|
140 |
FCKAdobeAIR.EditingArea_Start( oDoc, html ) ; |
|
141 |
||
142 |
// Firefox 1.0.x is buggy... ohh yes... so let's do it two times and it
|
|
143 |
// will magically work.
|
|
144 |
if ( FCKBrowserInfo.IsGecko10 && !secondCall ) |
|
145 |
{
|
|
146 |
this.Start( html, true ) ; |
|
147 |
return ; |
|
148 |
}
|
|
149 |
||
150 |
if ( oIFrame.readyState && oIFrame.readyState != 'completed' ) |
|
151 |
{
|
|
152 |
var editArea = this ; |
|
153 |
||
154 |
// Using a IE alternative for DOMContentLoaded, similar to the
|
|
155 |
// solution proposed at http://javascript.nwbox.com/IEContentLoaded/
|
|
156 |
setTimeout( function() |
|
157 |
{
|
|
158 |
try
|
|
159 |
{
|
|
160 |
editArea.Window.document.documentElement.doScroll("left") ; |
|
161 |
}
|
|
162 |
catch(e) |
|
163 |
{
|
|
164 |
setTimeout( arguments.callee, 0 ) ; |
|
165 |
return ; |
|
166 |
}
|
|
167 |
editArea.Window._FCKEditingArea = editArea ; |
|
168 |
FCKEditingArea_CompleteStart.call( editArea.Window ) ; |
|
169 |
}, 0 ) ; |
|
170 |
}
|
|
171 |
else
|
|
172 |
{
|
|
173 |
this.Window._FCKEditingArea = this ; |
|
174 |
||
175 |
// FF 1.0.x is buggy... we must wait a lot to enable editing because
|
|
176 |
// sometimes the content simply disappears, for example when pasting
|
|
177 |
// "bla1!<img src='some_url'>!bla2" in the source and then switching
|
|
178 |
// back to design.
|
|
179 |
if ( FCKBrowserInfo.IsGecko10 ) |
|
180 |
this.Window.setTimeout( FCKEditingArea_CompleteStart, 500 ) ; |
|
181 |
else
|
|
182 |
FCKEditingArea_CompleteStart.call( this.Window ) ; |
|
183 |
}
|
|
184 |
}
|
|
185 |
else
|
|
186 |
{
|
|
187 |
var eTextarea = this.Textarea = oTargetDocument.createElement( 'textarea' ) ; |
|
188 |
eTextarea.className = 'SourceField' ; |
|
189 |
eTextarea.dir = 'ltr' ; |
|
190 |
FCKDomTools.SetElementStyles( eTextarea, |
|
191 |
{
|
|
192 |
width : '100%', |
|
193 |
height : '100%', |
|
194 |
border : 'none', |
|
195 |
resize : 'none', |
|
196 |
outline : 'none' |
|
197 |
} ) ; |
|
198 |
eTargetElement.appendChild( eTextarea ) ; |
|
199 |
||
200 |
eTextarea.value = html ; |
|
201 |
||
202 |
// Fire the "OnLoad" event.
|
|
203 |
FCKTools.RunFunction( this.OnLoad ) ; |
|
204 |
}
|
|
205 |
}
|
|
206 |
||
207 |
// "this" here is FCKEditingArea.Window
|
|
208 |
function FCKEditingArea_CompleteStart() |
|
209 |
{
|
|
210 |
// On Firefox, the DOM takes a little to become available. So we must wait for it in a loop.
|
|
211 |
if ( !this.document.body ) |
|
212 |
{
|
|
213 |
this.setTimeout( FCKEditingArea_CompleteStart, 50 ) ; |
|
214 |
return ; |
|
215 |
}
|
|
216 |
||
217 |
var oEditorArea = this._FCKEditingArea ; |
|
218 |
||
219 |
// Save this reference to be re-used later.
|
|
220 |
oEditorArea.Document = oEditorArea.Window.document ; |
|
221 |
||
222 |
oEditorArea.MakeEditable() ; |
|
223 |
||
224 |
// Fire the "OnLoad" event.
|
|
225 |
FCKTools.RunFunction( oEditorArea.OnLoad ) ; |
|
226 |
}
|
|
227 |
||
228 |
FCKEditingArea.prototype.MakeEditable = function() |
|
229 |
{
|
|
230 |
var oDoc = this.Document ; |
|
231 |
||
232 |
if ( FCKBrowserInfo.IsIE ) |
|
233 |
{
|
|
234 |
// Kludge for #141 and #523
|
|
235 |
oDoc.body.disabled = true ; |
|
236 |
oDoc.body.contentEditable = true ; |
|
237 |
oDoc.body.removeAttribute( "disabled" ) ; |
|
238 |
||
239 |
/* The following commands don't throw errors, but have no effect.
|
|
240 |
oDoc.execCommand( 'AutoDetect', false, false ) ;
|
|
241 |
oDoc.execCommand( 'KeepSelection', false, true ) ;
|
|
242 |
*/
|
|
243 |
}
|
|
244 |
else
|
|
245 |
{
|
|
246 |
try
|
|
247 |
{
|
|
248 |
// Disable Firefox 2 Spell Checker.
|
|
249 |
oDoc.body.spellcheck = ( this.FFSpellChecker !== false ) ; |
|
250 |
||
251 |
if ( this._BodyHTML ) |
|
252 |
{
|
|
253 |
oDoc.body.innerHTML = this._BodyHTML ; |
|
254 |
oDoc.body.offsetLeft ; // Don't remove, this is a hack to fix Opera 9.50, see #2264. |
|
255 |
this._BodyHTML = null ; |
|
256 |
}
|
|
257 |
||
258 |
oDoc.designMode = 'on' ; |
|
259 |
||
260 |
// Tell Gecko (Firefox 1.5+) to enable or not live resizing of objects (by Alfonso Martinez)
|
|
261 |
oDoc.execCommand( 'enableObjectResizing', false, !FCKConfig.DisableObjectResizing ) ; |
|
262 |
||
263 |
// Disable the standard table editing features of Firefox.
|
|
264 |
oDoc.execCommand( 'enableInlineTableEditing', false, !FCKConfig.DisableFFTableHandles ) ; |
|
265 |
}
|
|
266 |
catch (e) |
|
267 |
{
|
|
268 |
// In Firefox if the iframe is initially hidden it can't be set to designMode and it raises an exception
|
|
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
|
|
270 |
FCKTools.AddEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ; |
|
271 |
}
|
|
272 |
||
273 |
}
|
|
274 |
}
|
|
275 |
||
276 |
// This function processes the notifications of the DOM Mutation event on the document
|
|
277 |
// We use it to know that the document will be ready to be editable again (or we hope so)
|
|
278 |
function FCKEditingArea_Document_AttributeNodeModified( evt ) |
|
279 |
{
|
|
280 |
var editingArea = evt.currentTarget.contentWindow._FCKEditingArea ; |
|
281 |
||
282 |
// We want to run our function after the events no longer fire, so we can know that it's a stable situation
|
|
283 |
if ( editingArea._timer ) |
|
284 |
window.clearTimeout( editingArea._timer ) ; |
|
285 |
||
286 |
editingArea._timer = FCKTools.SetTimeout( FCKEditingArea_MakeEditableByMutation, 1000, editingArea ) ; |
|
287 |
}
|
|
288 |
||
289 |
// This function ideally should be called after the document is visible, it does clean up of the
|
|
290 |
// mutation tracking and tries again to make the area editable.
|
|
291 |
function FCKEditingArea_MakeEditableByMutation() |
|
292 |
{
|
|
293 |
// Clean up
|
|
294 |
delete this._timer ; |
|
295 |
// Now we don't want to keep on getting this event
|
|
296 |
FCKTools.RemoveEventListener( this.Window.frameElement, 'DOMAttrModified', FCKEditingArea_Document_AttributeNodeModified ) ; |
|
297 |
// Let's try now to set the editing area editable
|
|
298 |
// If it fails it will set up the Mutation Listener again automatically
|
|
299 |
this.MakeEditable() ; |
|
300 |
}
|
|
301 |
||
302 |
FCKEditingArea.prototype.Focus = function() |
|
303 |
{
|
|
304 |
try
|
|
305 |
{
|
|
306 |
if ( this.Mode == FCK_EDITMODE_WYSIWYG ) |
|
307 |
{
|
|
308 |
if ( FCKBrowserInfo.IsIE ) |
|
309 |
this._FocusIE() ; |
|
310 |
else
|
|
311 |
this.Window.focus() ; |
|
312 |
}
|
|
313 |
else
|
|
314 |
{
|
|
315 |
var oDoc = FCKTools.GetElementDocument( this.Textarea ) ; |
|
316 |
if ( (!oDoc.hasFocus || oDoc.hasFocus() ) && oDoc.activeElement == this.Textarea ) |
|
317 |
return ; |
|
318 |
||
319 |
this.Textarea.focus() ; |
|
320 |
}
|
|
321 |
}
|
|
322 |
catch(e) {} |
|
323 |
}
|
|
324 |
||
325 |
FCKEditingArea.prototype._FocusIE = function() |
|
326 |
{
|
|
327 |
// In IE it can happen that the document is in theory focused but the
|
|
328 |
// active element is outside of it.
|
|
329 |
this.Document.body.setActive() ; |
|
330 |
||
331 |
this.Window.focus() ; |
|
332 |
||
333 |
// Kludge for #141... yet more code to workaround IE bugs
|
|
334 |
var range = this.Document.selection.createRange() ; |
|
335 |
||
336 |
var parentNode = range.parentElement() ; |
|
337 |
var parentTag = parentNode.nodeName.toLowerCase() ; |
|
338 |
||
339 |
// Only apply the fix when in a block, and the block is empty.
|
|
340 |
if ( parentNode.childNodes.length > 0 || |
|
341 |
!( FCKListsLib.BlockElements[parentTag] || |
|
342 |
FCKListsLib.NonEmptyBlockElements[parentTag] ) ) |
|
343 |
{
|
|
344 |
return ; |
|
345 |
}
|
|
346 |
||
347 |
// Force the selection to happen, in this way we guarantee the focus will
|
|
348 |
// be there.
|
|
349 |
range = new FCKDomRange( this.Window ) ; |
|
350 |
range.MoveToElementEditStart( parentNode ) ; |
|
351 |
range.Select() ; |
|
352 |
}
|
|
353 |
||
354 |
function FCKEditingArea_Cleanup() |
|
355 |
{
|
|
356 |
if ( this.Document ) |
|
357 |
{
|
|
358 |
// Avoid IE crash if an object is selected on unload #2201
|
|
359 |
this.Document.selection.empty() ; |
|
360 |
this.Document.body.innerHTML = "" ; |
|
361 |
}
|
|
362 |
this.TargetElement = null ; |
|
363 |
this.IFrame = null ; |
|
364 |
this.Document = null ; |
|
365 |
this.Textarea = null ; |
|
366 |
||
367 |
if ( this.Window ) |
|
368 |
{
|
|
369 |
this.Window._FCKEditingArea = null ; |
|
370 |
this.Window = null ; |
|
371 |
}
|
|
372 |
}
|