      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