1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 CKEDITOR.plugins.add( 'floatpanel', 7 { 8 requires : [ 'panel' ] 9 }); 10 11 (function() 12 { 13 var panels = {}; 14 var isShowing = false; 15 16 function getPanel( editor, doc, parentElement, definition, level ) 17 { 18 // Generates the panel key: docId-eleId-skinName-langDir[-uiColor][-CSSs][-level] 19 var key = 20 doc.getUniqueId() + 21 '-' + parentElement.getUniqueId() + 22 '-' + editor.skinName + 23 '-' + editor.lang.dir + 24 ( ( editor.uiColor && ( '-' + editor.uiColor ) ) || '' ) + 25 ( ( definition.css && ( '-' + definition.css ) ) || '' ) + 26 ( ( level && ( '-' + level ) ) || '' ); 27 28 var panel = panels[ key ]; 29 30 if ( !panel ) 31 { 32 panel = panels[ key ] = new CKEDITOR.ui.panel( doc, definition ); 33 panel.element = parentElement.append( CKEDITOR.dom.element.createFromHtml( panel.renderHtml( editor ), doc ) ); 34 35 panel.element.setStyles( 36 { 37 display : 'none', 38 position : 'absolute' 39 }); 40 } 41 42 return panel; 43 } 44 45 CKEDITOR.ui.floatPanel = CKEDITOR.tools.createClass( 46 { 47 $ : function( editor, parentElement, definition, level ) 48 { 49 definition.forceIFrame = true; 50 51 var doc = parentElement.getDocument(), 52 panel = getPanel( editor, doc, parentElement, definition, level || 0 ), 53 element = panel.element, 54 iframe = element.getFirst().getFirst(); 55 56 this.element = element; 57 58 this._ = 59 { 60 // The panel that will be floating. 61 panel : panel, 62 parentElement : parentElement, 63 definition : definition, 64 document : doc, 65 iframe : iframe, 66 children : [], 67 dir : editor.lang.dir 68 }; 69 }, 70 71 proto : 72 { 73 addBlock : function( name, block ) 74 { 75 return this._.panel.addBlock( name, block ); 76 }, 77 78 addListBlock : function( name, multiSelect ) 79 { 80 return this._.panel.addListBlock( name, multiSelect ); 81 }, 82 83 getBlock : function( name ) 84 { 85 return this._.panel.getBlock( name ); 86 }, 87 88 /* 89 corner (LTR): 90 1 = top-left 91 2 = top-right 92 3 = bottom-right 93 4 = bottom-left 94 95 corner (RTL): 96 1 = top-right 97 2 = top-left 98 3 = bottom-left 99 4 = bottom-right 100 */ 101 showBlock : function( name, offsetParent, corner, offsetX, offsetY ) 102 { 103 var panel = this._.panel, 104 block = panel.showBlock( name ); 105 106 this.allowBlur( false ); 107 isShowing = true; 108 109 var element = this.element, 110 iframe = this._.iframe, 111 definition = this._.definition, 112 position = offsetParent.getDocumentPosition( element.getDocument() ), 113 rtl = this._.dir == 'rtl'; 114 115 var left = position.x + ( offsetX || 0 ), 116 top = position.y + ( offsetY || 0 ); 117 118 if ( ( rtl && ( corner == 1 || corner == 4 ) ) || ( !rtl && ( corner == 2 || corner == 3 ) ) ) 119 left += offsetParent.$.offsetWidth - 1; 120 121 if ( corner == 3 || corner == 4 ) 122 top += offsetParent.$.offsetHeight - 1; 123 124 element.setStyles( 125 { 126 top : top + 'px', 127 left : '-3000px', 128 visibility : 'hidden', 129 opacity : '0', // FF3 is ignoring "visibility" 130 display : '' 131 }); 132 133 // Configure the IFrame blur event. Do that only once. 134 if ( !this._.blurSet ) 135 { 136 // Non IE prefer the event into a window object. 137 var focused = CKEDITOR.env.ie ? iframe : new CKEDITOR.dom.window( iframe.$.contentWindow ); 138 139 // With addEventListener compatible browsers, we must 140 // useCapture when registering the focus/blur events to 141 // guarantee they will be firing in all situations. (#3068, #3222 ) 142 CKEDITOR.event.useCapture = true; 143 144 focused.on( 'blur', function( ev ) 145 { 146 if ( CKEDITOR.env.ie && !this.allowBlur() ) 147 return; 148 149 // As we are using capture to register the listener, 150 // the blur event may get fired even when focusing 151 // inside the window itself, so we must ensure the 152 // target is out of it. 153 var target = ev.data.getTarget(), 154 targetWindow = target.getWindow && target.getWindow(); 155 156 if ( targetWindow && targetWindow.equals( focused ) ) 157 return; 158 159 if ( this.visible && !this._.activeChild && !isShowing ) 160 this.hide(); 161 }, 162 this ); 163 164 focused.on( 'focus', function() 165 { 166 this._.focused = true; 167 this.hideChild(); 168 this.allowBlur( true ); 169 }, 170 this ); 171 172 CKEDITOR.event.useCapture = false; 173 174 this._.blurSet = 1; 175 } 176 177 panel.onEscape = CKEDITOR.tools.bind( function() 178 { 179 this.onEscape && this.onEscape(); 180 }, 181 this ); 182 183 setTimeout( function() 184 { 185 if ( rtl ) 186 left -= element.$.offsetWidth; 187 188 element.setStyles( 189 { 190 left : left + 'px', 191 visibility : '', 192 opacity : '1' // FF3 is ignoring "visibility" 193 }); 194 195 if ( block.autoSize ) 196 { 197 function setHeight() 198 { 199 var target = element.getFirst(); 200 var height = block.element.$.scrollHeight; 201 202 // Account for extra height needed due to IE quirks box model bug: 203 // http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug 204 // (#3426) 205 if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && height > 0 ) 206 height += ( target.$.offsetHeight || 0 ) - ( target.$.clientHeight || 0 ); 207 208 target.setStyle( 'height', height + 'px' ); 209 210 // Fix IE < 8 visibility. 211 panel._.currentBlock.element.setStyle( 'display', 'none' ).removeStyle( 'display' ); 212 } 213 214 if ( !CKEDITOR.env.gecko || panel.isLoaded ) 215 { 216 // IE7 needs some time (setting the delay to 0ms won't work) to refresh 217 // the scrollHeight. (#3174) 218 if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 7 ) 219 setTimeout( setHeight, 50 ); 220 else 221 setHeight(); 222 } 223 else 224 panel.onLoad = setHeight; 225 } 226 else 227 element.getFirst().removeStyle( 'height' ); 228 229 // Set the IFrame focus, so the blur event gets fired. 230 setTimeout( function() 231 { 232 if ( definition.voiceLabel ) 233 { 234 if ( CKEDITOR.env.gecko ) 235 { 236 var container = iframe.getParent(); 237 container.setAttribute( 'role', 'region' ); 238 container.setAttribute( 'title', definition.voiceLabel ); 239 iframe.setAttribute( 'role', 'region' ); 240 iframe.setAttribute( 'title', ' ' ); 241 } 242 } 243 if ( CKEDITOR.env.ie && CKEDITOR.env.quirks ) 244 iframe.focus(); 245 else 246 iframe.$.contentWindow.focus(); 247 }, 0); 248 }, 0); 249 250 this.visible = 1; 251 252 if ( this.onShow ) 253 this.onShow.call( this ); 254 255 isShowing = false; 256 }, 257 258 hide : function() 259 { 260 if ( this.visible && ( !this.onHide || this.onHide.call( this ) !== true ) ) 261 { 262 this.hideChild(); 263 this.element.setStyle( 'display', 'none' ); 264 this.visible = 0; 265 } 266 }, 267 268 allowBlur : function( allow ) // Prevent editor from hiding the panel. #3222. 269 { 270 var panel = this._.panel; 271 if ( allow != undefined ) 272 panel.allowBlur = allow; 273 274 return panel.allowBlur; 275 }, 276 277 showAsChild : function( panel, blockName, offsetParent, corner, offsetX, offsetY ) 278 { 279 this.hideChild(); 280 281 panel.onHide = CKEDITOR.tools.bind( function() 282 { 283 // Use a timeout, so we give time for this menu to get 284 // potentially focused. 285 CKEDITOR.tools.setTimeout( function() 286 { 287 if ( !this._.focused ) 288 this.hide(); 289 }, 290 0, this ); 291 }, 292 this ); 293 294 this._.activeChild = panel; 295 this._.focused = false; 296 297 panel.showBlock( blockName, offsetParent, corner, offsetX, offsetY ); 298 299 /* #3767 IE: Second level menu may not have borders */ 300 if ( CKEDITOR.env.ie7Compat || ( CKEDITOR.env.ie8 && CKEDITOR.env.ie6Compat ) ) 301 { 302 setTimeout(function() 303 { 304 panel.element.getChild( 0 ).$.style.cssText += ''; 305 }, 100); 306 } 307 }, 308 309 hideChild : function() 310 { 311 var activeChild = this._.activeChild; 312 313 if ( activeChild ) 314 { 315 delete activeChild.onHide; 316 delete this._.activeChild; 317 activeChild.hide(); 318 } 319 } 320 } 321 }); 322 })(); 323