Ticket #1436: fckeditingarea.js

File fckeditingarea.js, 11.0 KB (added by Jeff Johnson, 16 years ago)

Change FCKEditingArea.prototype.Focus behavior for Safari

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