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 * Component that creates floating panels. It is used by many 22 * other components, like the toolbar items, context menu, etc... 23 */ 24 25var FCKPanel = function( parentWindow ) 26{ 27 this.IsRTL = ( FCKLang.Dir == 'rtl' ) ; 28 this.IsContextMenu = false ; 29 this._LockCounter = 0 ; 30 31 this._Window = parentWindow || window ; 32 33 var oDocument ; 34 35 if ( FCKBrowserInfo.IsIE ) 36 { 37 // Create the Popup that will hold the panel. 38 this._Popup = this._Window.createPopup() ; 39 oDocument = this.Document = this._Popup.document ; 40 41 FCK.IECleanup.AddItem( this, FCKPanel_Cleanup ) ; 42 } 43 else 44 { 45 var oIFrame = this._IFrame = this._Window.document.createElement('iframe') ; 46 oIFrame.src = 'javascript:void(0)' ; 47 oIFrame.allowTransparency = true ; 48 oIFrame.frameBorder = '0' ; 49 oIFrame.scrolling = 'no' ; 50 oIFrame.width = oIFrame.height = 0 ; 51 FCKDomTools.SetElementStyles( oIFrame, 52 { 53 position : 'absolute', 54 zIndex : FCKConfig.FloatingPanelsZIndex 55 } ) ; 56 57 if ( this._Window == window.parent && window.frameElement ) 58 { 59 var scrollPos = null ; 60 if ( FCKBrowserInfo.IsGecko && FCK && FCK.EditorDocument ) 61 scrollPos = [ FCK.EditorDocument.body.scrollLeft, FCK.EditorDocument.body.scrollTop ] ; 62 window.frameElement.parentNode.insertBefore( oIFrame, window.frameElement ) ; 63 if ( scrollPos ) 64 { 65 var restoreFunc = function() 66 { 67 FCK.EditorDocument.body.scrollLeft = scrollPos[0] ; 68 FCK.EditorDocument.body.scrollTop = scrollPos[1] ; 69 } 70 setTimeout( restoreFunc, 500 ) ; 71 } 72 } 73 else 74 this._Window.document.body.appendChild( oIFrame ) ; 75 76 var oIFrameWindow = oIFrame.contentWindow ; 77 78 oDocument = this.Document = oIFrameWindow.document ; 79 80 // Workaround for Safari 12256. Ticket #63 81 var sBase = '' ; 82 if ( FCKBrowserInfo.IsSafari ) 83 sBase = '<base href="' + window.document.location + '">' ; 84 85 // Initialize the IFRAME document body. 86 oDocument.open() ; 87 oDocument.write( '<html><head>' + sBase + '<\/head><body style="margin:0px;padding:0px;"><\/body><\/html>' ) ; 88 oDocument.close() ; 89 90 FCKTools.AddEventListenerEx( oIFrameWindow, 'focus', FCKPanel_Window_OnFocus, this ) ; 91 FCKTools.AddEventListenerEx( oIFrameWindow, 'blur', FCKPanel_Window_OnBlur, this ) ; 92 } 93 94 oDocument.dir = FCKLang.Dir ; 95 96 FCKTools.AddEventListener( oDocument, 'contextmenu', FCKTools.CancelEvent ) ; 97 98 99 // Create the main DIV that is used as the panel base. 100 this.MainNode = oDocument.body.appendChild( oDocument.createElement('DIV') ) ; 101 102 // The "float" property must be set so Firefox calculates the size correctly. 103 this.MainNode.style.cssFloat = this.IsRTL ? 'right' : 'left' ; 104} 105 106 107FCKPanel.prototype.AppendStyleSheet = function( styleSheet ) 108{ 109 FCKTools.AppendStyleSheet( this.Document, styleSheet ) ; 110} 111 112FCKPanel.prototype.Preload = function( x, y, relElement ) 113{ 114 // The offsetWidth and offsetHeight properties are not available if the 115 // element is not visible. So we must "show" the popup with no size to 116 // be able to use that values in the second call (IE only). 117 if ( this._Popup ) 118 this._Popup.show( x, y, 0, 0, relElement ) ; 119} 120 121FCKPanel.prototype.Show = function( x, y, relElement, width, height ) 122{ 123 var iMainWidth ; 124 var eMainNode = this.MainNode ; 125 126 if ( this._Popup ) 127 { 128 // The offsetWidth and offsetHeight properties are not available if the 129 // element is not visible. So we must "show" the popup with no size to 130 // be able to use that values in the second call. 131 this._Popup.show( x, y, 0, 0, relElement ) ; 132 133 // The following lines must be place after the above "show", otherwise it 134 // doesn't has the desired effect. 135 FCKDomTools.SetElementStyles( eMainNode, 136 { 137 width : width ? width + 'px' : '', 138 height : height ? height + 'px' : '' 139 } ) ; 140 141 iMainWidth = eMainNode.offsetWidth ; 142 143 if ( this.IsRTL ) 144 { 145 if ( this.IsContextMenu ) 146 x = x - iMainWidth + 1 ; 147 else if ( relElement ) 148 x = ( x * -1 ) + relElement.offsetWidth - iMainWidth ; 149 } 150 151 // Second call: Show the Popup at the specified location, with the correct size. 152 this._Popup.show( x, y, iMainWidth, eMainNode.offsetHeight, relElement ) ; 153 154 if ( this.OnHide ) 155 { 156 if ( this._Timer ) 157 CheckPopupOnHide.call( this, true ) ; 158 159 this._Timer = FCKTools.SetInterval( CheckPopupOnHide, 100, this ) ; 160 } 161 } 162 else 163 { 164 // Do not fire OnBlur while the panel is opened. 165 if ( typeof( FCK.ToolbarSet.CurrentInstance.FocusManager ) != 'undefined' ) 166 FCK.ToolbarSet.CurrentInstance.FocusManager.Lock() ; 167 168 if ( this.ParentPanel ) 169 this.ParentPanel.Lock() ; 170 171 FCKDomTools.SetElementStyles( eMainNode, 172 { 173 width : width ? width + 'px' : '', 174 height : height ? height + 'px' : '' 175 } ) ; 176 177 iMainWidth = eMainNode.offsetWidth ; 178 179 if ( !width ) this._IFrame.width = 1 ; 180 if ( !height ) this._IFrame.height = 1 ; 181 182 // This is weird... but with Firefox, we must get the offsetWidth before 183 // setting the _IFrame size (which returns "0"), and then after that, 184 // to return the correct width. Remove the first step and it will not 185 // work when the editor is in RTL. 186 // 187 // The "|| eMainNode.firstChild.offsetWidth" part has been added 188 // for Opera compatibility (see #570). 189 iMainWidth = eMainNode.offsetWidth || eMainNode.firstChild.offsetWidth ; 190 191 var oPos = FCKTools.GetElementPosition( 192 relElement.nodeType == 9 ? 193 ( FCKTools.IsStrictMode( relElement ) ? relElement.documentElement : relElement.body ) : 194 relElement, 195 this._Window ) ; 196 197 if ( this.IsRTL && !this.IsContextMenu ) 198 x = ( x * -1 ) ; 199 200 x += oPos.X ; 201 y += oPos.Y ; 202 203 if ( this.IsRTL ) 204 { 205 if ( this.IsContextMenu ) 206 x = x - iMainWidth + 1 ; 207 else if ( relElement ) 208 x = x + relElement.offsetWidth - iMainWidth ; 209 } 210 else 211 { 212 var oViewPaneSize = FCKTools.GetViewPaneSize( this._Window ) ; 213 var oScrollPosition = FCKTools.GetScrollPosition( this._Window ) ; 214 215 var iViewPaneHeight = oViewPaneSize.Height + oScrollPosition.Y ; 216 var iViewPaneWidth = oViewPaneSize.Width + oScrollPosition.X ; 217 218 if ( ( x + iMainWidth ) > iViewPaneWidth ) 219 x -= x + iMainWidth - iViewPaneWidth ; 220 221 if ( ( y + eMainNode.offsetHeight ) > iViewPaneHeight ) 222 y -= y + eMainNode.offsetHeight - iViewPaneHeight ; 223 } 224 225 if ( x < 0 ) 226 x = 0 ; 227 228 // Set the context menu DIV in the specified location. 229 FCKDomTools.SetElementStyles( this._IFrame, 230 { 231 left : x + 'px', 232 top : y + 'px' 233 } ) ; 234 235 var iWidth = iMainWidth ; 236 var iHeight = eMainNode.offsetHeight ; 237 238 this._IFrame.width = iWidth ; 239 this._IFrame.height = iHeight ; 240 241 // Move the focus to the IFRAME so we catch the "onblur". 242 this._IFrame.contentWindow.focus() ; 243 } 244 245 this._IsOpened = true ; 246 247 FCKTools.RunFunction( this.OnShow, this ) ; 248} 249 250FCKPanel.prototype.Hide = function( ignoreOnHide ) 251{ 252 if ( this._Popup ) 253 this._Popup.hide() ; 254 else 255 { 256 if ( !this._IsOpened ) 257 return ; 258 259 // Enable the editor to fire the "OnBlur". 260 if ( typeof( FCKFocusManager ) != 'undefined' ) 261 FCKFocusManager.Unlock() ; 262 263 // It is better to set the sizes to 0, otherwise Firefox would have 264 // rendering problems. 265 this._IFrame.width = this._IFrame.height = 0 ; 266 267 this._IsOpened = false ; 268 269 if ( this.ParentPanel ) 270 this.ParentPanel.Unlock() ; 271 272 if ( !ignoreOnHide ) 273 FCKTools.RunFunction( this.OnHide, this ) ; 274 } 275} 276 277FCKPanel.prototype.CheckIsOpened = function() 278{ 279 if ( this._Popup ) 280 return this._Popup.isOpen ; 281 else 282 return this._IsOpened ; 283} 284 285FCKPanel.prototype.CreateChildPanel = function() 286{ 287 var oWindow = this._Popup ? FCKTools.GetDocumentWindow( this.Document ) : this._Window ; 288 289 var oChildPanel = new FCKPanel( oWindow ) ; 290 oChildPanel.ParentPanel = this ; 291 292 return oChildPanel ; 293} 294 295FCKPanel.prototype.Lock = function() 296{ 297 this._LockCounter++ ; 298} 299 300FCKPanel.prototype.Unlock = function() 301{ 302 if ( --this._LockCounter == 0 && !this.HasFocus ) 303 this.Hide() ; 304} 305 306/* Events */ 307 308function FCKPanel_Window_OnFocus( e, panel ) 309{ 310 panel.HasFocus = true ; 311} 312 313function FCKPanel_Window_OnBlur( e, panel ) 314{ 315 panel.HasFocus = false ; 316 317 if ( panel._LockCounter == 0 ) 318 FCKTools.RunFunction( panel.Hide, panel ) ; 319} 320 321function CheckPopupOnHide( forceHide ) 322{ 323 if ( forceHide || !this._Popup.isOpen ) 324 { 325 window.clearInterval( this._Timer ) ; 326 this._Timer = null ; 327 328 FCKTools.RunFunction( this.OnHide, this ) ; 329 } 330} 331 332function FCKPanel_Cleanup() 333{ 334 this._Popup = null ; 335 this._Window = null ; 336 this.Document = null ; 337 this.MainNode = null ; 338} 339