]>
Commit | Line | Data |
---|---|---|
c63493c8 IB |
1 | /**\r |
2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r | |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license\r | |
4 | */\r | |
5 | \r | |
6 | CKEDITOR.plugins.add( 'floatpanel', {\r | |
7 | requires: 'panel'\r | |
8 | } );\r | |
9 | \r | |
10 | ( function() {\r | |
11 | var panels = {};\r | |
12 | \r | |
13 | function getPanel( editor, doc, parentElement, definition, level ) {\r | |
14 | // Generates the panel key: docId-eleId-skinName-langDir[-uiColor][-CSSs][-level]\r | |
15 | var key = CKEDITOR.tools.genKey( doc.getUniqueId(), parentElement.getUniqueId(), editor.lang.dir, editor.uiColor || '', definition.css || '', level || '' ),\r | |
16 | panel = panels[ key ];\r | |
17 | \r | |
18 | if ( !panel ) {\r | |
19 | panel = panels[ key ] = new CKEDITOR.ui.panel( doc, definition );\r | |
20 | panel.element = parentElement.append( CKEDITOR.dom.element.createFromHtml( panel.render( editor ), doc ) );\r | |
21 | \r | |
22 | panel.element.setStyles( {\r | |
23 | display: 'none',\r | |
24 | position: 'absolute'\r | |
25 | } );\r | |
26 | }\r | |
27 | \r | |
28 | return panel;\r | |
29 | }\r | |
30 | \r | |
31 | /**\r | |
32 | * Represents a floating panel UI element.\r | |
33 | *\r | |
34 | * It is reused by rich combos, color combos, menus, etc.\r | |
35 | * and it renders its content using {@link CKEDITOR.ui.panel}.\r | |
36 | *\r | |
37 | * @class\r | |
38 | * @todo\r | |
39 | */\r | |
40 | CKEDITOR.ui.floatPanel = CKEDITOR.tools.createClass( {\r | |
41 | /**\r | |
42 | * Creates a floatPanel class instance.\r | |
43 | *\r | |
44 | * @constructor\r | |
45 | * @param {CKEDITOR.editor} editor\r | |
46 | * @param {CKEDITOR.dom.element} parentElement\r | |
47 | * @param {Object} definition Definition of the panel that will be floating.\r | |
48 | * @param {Number} level\r | |
49 | */\r | |
50 | $: function( editor, parentElement, definition, level ) {\r | |
51 | definition.forceIFrame = 1;\r | |
52 | \r | |
53 | // In case of editor with floating toolbar append panels that should float\r | |
54 | // to the main UI element.\r | |
55 | if ( definition.toolbarRelated && editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE )\r | |
56 | parentElement = CKEDITOR.document.getById( 'cke_' + editor.name );\r | |
57 | \r | |
58 | var doc = parentElement.getDocument(),\r | |
59 | panel = getPanel( editor, doc, parentElement, definition, level || 0 ),\r | |
60 | element = panel.element,\r | |
61 | iframe = element.getFirst(),\r | |
62 | that = this;\r | |
63 | \r | |
64 | // Disable native browser menu. (#4825)\r | |
65 | element.disableContextMenu();\r | |
66 | \r | |
67 | this.element = element;\r | |
68 | \r | |
69 | this._ = {\r | |
70 | editor: editor,\r | |
71 | // The panel that will be floating.\r | |
72 | panel: panel,\r | |
73 | parentElement: parentElement,\r | |
74 | definition: definition,\r | |
75 | document: doc,\r | |
76 | iframe: iframe,\r | |
77 | children: [],\r | |
78 | dir: editor.lang.dir,\r | |
79 | showBlockParams: null\r | |
80 | };\r | |
81 | \r | |
82 | editor.on( 'mode', hide );\r | |
83 | editor.on( 'resize', hide );\r | |
84 | \r | |
85 | // When resize of the window is triggered floatpanel should be repositioned according to new dimensions.\r | |
86 | // #11724. Fixes issue with undesired panel hiding on Android and iOS.\r | |
87 | doc.getWindow().on( 'resize', function() {\r | |
88 | this.reposition();\r | |
89 | }, this );\r | |
90 | \r | |
91 | // We need a wrapper because events implementation doesn't allow to attach\r | |
92 | // one listener more than once for the same event on the same object.\r | |
93 | // Remember that floatPanel#hide is shared between all instances.\r | |
94 | function hide() {\r | |
95 | that.hide();\r | |
96 | }\r | |
97 | },\r | |
98 | \r | |
99 | proto: {\r | |
100 | /**\r | |
101 | * @todo\r | |
102 | */\r | |
103 | addBlock: function( name, block ) {\r | |
104 | return this._.panel.addBlock( name, block );\r | |
105 | },\r | |
106 | \r | |
107 | /**\r | |
108 | * @todo\r | |
109 | */\r | |
110 | addListBlock: function( name, multiSelect ) {\r | |
111 | return this._.panel.addListBlock( name, multiSelect );\r | |
112 | },\r | |
113 | \r | |
114 | /**\r | |
115 | * @todo\r | |
116 | */\r | |
117 | getBlock: function( name ) {\r | |
118 | return this._.panel.getBlock( name );\r | |
119 | },\r | |
120 | \r | |
121 | /**\r | |
122 | * Shows the panel block.\r | |
123 | *\r | |
124 | * @param {String} name\r | |
125 | * @param {CKEDITOR.dom.element} offsetParent Positioned parent.\r | |
126 | * @param {Number} corner\r | |
127 | *\r | |
128 | * * For LTR (left to right) oriented editor:\r | |
129 | * * `1` = top-left\r | |
130 | * * `2` = top-right\r | |
131 | * * `3` = bottom-right\r | |
132 | * * `4` = bottom-left\r | |
133 | * * For RTL (right to left):\r | |
134 | * * `1` = top-right\r | |
135 | * * `2` = top-left\r | |
136 | * * `3` = bottom-left\r | |
137 | * * `4` = bottom-right\r | |
138 | *\r | |
139 | * @param {Number} [offsetX=0]\r | |
140 | * @param {Number} [offsetY=0]\r | |
141 | * @param {Function} [callback] A callback function executed when block positioning is done.\r | |
142 | * @todo what do exactly these params mean (especially corner)?\r | |
143 | */\r | |
144 | showBlock: function( name, offsetParent, corner, offsetX, offsetY, callback ) {\r | |
145 | var panel = this._.panel,\r | |
146 | block = panel.showBlock( name );\r | |
147 | \r | |
148 | this._.showBlockParams = [].slice.call( arguments );\r | |
149 | this.allowBlur( false );\r | |
150 | \r | |
151 | // Record from where the focus is when open panel.\r | |
152 | var editable = this._.editor.editable();\r | |
153 | this._.returnFocus = editable.hasFocus ? editable : new CKEDITOR.dom.element( CKEDITOR.document.$.activeElement );\r | |
154 | this._.hideTimeout = 0;\r | |
155 | \r | |
156 | var element = this.element,\r | |
157 | iframe = this._.iframe,\r | |
158 | // Edge prefers iframe's window to the iframe, just like the rest of the browsers (#13143).\r | |
159 | focused = CKEDITOR.env.ie && !CKEDITOR.env.edge ? iframe : new CKEDITOR.dom.window( iframe.$.contentWindow ),\r | |
160 | doc = element.getDocument(),\r | |
161 | positionedAncestor = this._.parentElement.getPositionedAncestor(),\r | |
162 | position = offsetParent.getDocumentPosition( doc ),\r | |
163 | positionedAncestorPosition = positionedAncestor ? positionedAncestor.getDocumentPosition( doc ) : { x: 0, y: 0 },\r | |
164 | rtl = this._.dir == 'rtl',\r | |
165 | left = position.x + ( offsetX || 0 ) - positionedAncestorPosition.x,\r | |
166 | top = position.y + ( offsetY || 0 ) - positionedAncestorPosition.y;\r | |
167 | \r | |
168 | // Floating panels are off by (-1px, 0px) in RTL mode. (#3438)\r | |
169 | if ( rtl && ( corner == 1 || corner == 4 ) )\r | |
170 | left += offsetParent.$.offsetWidth;\r | |
171 | else if ( !rtl && ( corner == 2 || corner == 3 ) )\r | |
172 | left += offsetParent.$.offsetWidth - 1;\r | |
173 | \r | |
174 | if ( corner == 3 || corner == 4 )\r | |
175 | top += offsetParent.$.offsetHeight - 1;\r | |
176 | \r | |
177 | // Memorize offsetParent by it's ID.\r | |
178 | this._.panel._.offsetParentId = offsetParent.getId();\r | |
179 | \r | |
180 | element.setStyles( {\r | |
181 | top: top + 'px',\r | |
182 | left: 0,\r | |
183 | display: ''\r | |
184 | } );\r | |
185 | \r | |
186 | // Don't use display or visibility style because we need to\r | |
187 | // calculate the rendering layout later and focus the element.\r | |
188 | element.setOpacity( 0 );\r | |
189 | \r | |
190 | // To allow the context menu to decrease back their width\r | |
191 | element.getFirst().removeStyle( 'width' );\r | |
192 | \r | |
193 | // Report to focus manager.\r | |
194 | this._.editor.focusManager.add( focused );\r | |
195 | \r | |
196 | // Configure the IFrame blur event. Do that only once.\r | |
197 | if ( !this._.blurSet ) {\r | |
198 | \r | |
199 | // With addEventListener compatible browsers, we must\r | |
200 | // useCapture when registering the focus/blur events to\r | |
201 | // guarantee they will be firing in all situations. (#3068, #3222 )\r | |
202 | CKEDITOR.event.useCapture = true;\r | |
203 | \r | |
204 | focused.on( 'blur', function( ev ) {\r | |
205 | // As we are using capture to register the listener,\r | |
206 | // the blur event may get fired even when focusing\r | |
207 | // inside the window itself, so we must ensure the\r | |
208 | // target is out of it.\r | |
209 | if ( !this.allowBlur() || ev.data.getPhase() != CKEDITOR.EVENT_PHASE_AT_TARGET )\r | |
210 | return;\r | |
211 | \r | |
212 | if ( this.visible && !this._.activeChild ) {\r | |
213 | // [iOS] Allow hide to be prevented if touch is bound\r | |
214 | // to any parent of the iframe blur happens before touch (#10714).\r | |
215 | if ( CKEDITOR.env.iOS ) {\r | |
216 | if ( !this._.hideTimeout )\r | |
217 | this._.hideTimeout = CKEDITOR.tools.setTimeout( doHide, 0, this );\r | |
218 | } else {\r | |
219 | doHide.call( this );\r | |
220 | }\r | |
221 | }\r | |
222 | \r | |
223 | function doHide() {\r | |
224 | // Panel close is caused by user's navigating away the focus, e.g. click outside the panel.\r | |
225 | // DO NOT restore focus in this case.\r | |
226 | delete this._.returnFocus;\r | |
227 | this.hide();\r | |
228 | }\r | |
229 | }, this );\r | |
230 | \r | |
231 | focused.on( 'focus', function() {\r | |
232 | this._.focused = true;\r | |
233 | this.hideChild();\r | |
234 | this.allowBlur( true );\r | |
235 | }, this );\r | |
236 | \r | |
237 | // [iOS] if touch is bound to any parent of the iframe blur\r | |
238 | // happens twice before touchstart and before touchend (#10714).\r | |
239 | if ( CKEDITOR.env.iOS ) {\r | |
240 | // Prevent false hiding on blur.\r | |
241 | // We don't need to return focus here because touchend will fire anyway.\r | |
242 | // If user scrolls and pointer gets out of the panel area touchend will also fire.\r | |
243 | focused.on( 'touchstart', function() {\r | |
244 | clearTimeout( this._.hideTimeout );\r | |
245 | }, this );\r | |
246 | \r | |
247 | // Set focus back to handle blur and hide panel when needed.\r | |
248 | focused.on( 'touchend', function() {\r | |
249 | this._.hideTimeout = 0;\r | |
250 | this.focus();\r | |
251 | }, this );\r | |
252 | }\r | |
253 | \r | |
254 | CKEDITOR.event.useCapture = false;\r | |
255 | \r | |
256 | this._.blurSet = 1;\r | |
257 | }\r | |
258 | \r | |
259 | panel.onEscape = CKEDITOR.tools.bind( function( keystroke ) {\r | |
260 | if ( this.onEscape && this.onEscape( keystroke ) === false )\r | |
261 | return false;\r | |
262 | }, this );\r | |
263 | \r | |
264 | CKEDITOR.tools.setTimeout( function() {\r | |
265 | var panelLoad = CKEDITOR.tools.bind( function() {\r | |
266 | var target = element;\r | |
267 | \r | |
268 | // Reset panel width as the new content can be narrower\r | |
269 | // than the old one. (#9355)\r | |
270 | target.removeStyle( 'width' );\r | |
271 | \r | |
272 | if ( block.autoSize ) {\r | |
273 | var panelDoc = block.element.getDocument(),\r | |
274 | width = ( ( CKEDITOR.env.webkit || CKEDITOR.env.edge ) ? block.element : panelDoc.getBody() ).$.scrollWidth;\r | |
275 | \r | |
276 | // Account for extra height needed due to IE quirks box model bug:\r | |
277 | // http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug\r | |
278 | // (#3426)\r | |
279 | if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && width > 0 )\r | |
280 | width += ( target.$.offsetWidth || 0 ) - ( target.$.clientWidth || 0 ) + 3;\r | |
281 | \r | |
282 | // Add some extra pixels to improve the appearance.\r | |
283 | width += 10;\r | |
284 | \r | |
285 | target.setStyle( 'width', width + 'px' );\r | |
286 | \r | |
287 | var height = block.element.$.scrollHeight;\r | |
288 | \r | |
289 | // Account for extra height needed due to IE quirks box model bug:\r | |
290 | // http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug\r | |
291 | // (#3426)\r | |
292 | if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && height > 0 )\r | |
293 | height += ( target.$.offsetHeight || 0 ) - ( target.$.clientHeight || 0 ) + 3;\r | |
294 | \r | |
295 | target.setStyle( 'height', height + 'px' );\r | |
296 | \r | |
297 | // Fix IE < 8 visibility.\r | |
298 | panel._.currentBlock.element.setStyle( 'display', 'none' ).removeStyle( 'display' );\r | |
299 | } else {\r | |
300 | target.removeStyle( 'height' );\r | |
301 | }\r | |
302 | \r | |
303 | // Flip panel layout horizontally in RTL with known width.\r | |
304 | if ( rtl )\r | |
305 | left -= element.$.offsetWidth;\r | |
306 | \r | |
307 | // Pop the style now for measurement.\r | |
308 | element.setStyle( 'left', left + 'px' );\r | |
309 | \r | |
310 | /* panel layout smartly fit the viewport size. */\r | |
311 | var panelElement = panel.element,\r | |
312 | panelWindow = panelElement.getWindow(),\r | |
313 | rect = element.$.getBoundingClientRect(),\r | |
314 | viewportSize = panelWindow.getViewPaneSize();\r | |
315 | \r | |
316 | // Compensation for browsers that dont support "width" and "height".\r | |
317 | var rectWidth = rect.width || rect.right - rect.left,\r | |
318 | rectHeight = rect.height || rect.bottom - rect.top;\r | |
319 | \r | |
320 | // Check if default horizontal layout is impossible.\r | |
321 | var spaceAfter = rtl ? rect.right : viewportSize.width - rect.left,\r | |
322 | spaceBefore = rtl ? viewportSize.width - rect.right : rect.left;\r | |
323 | \r | |
324 | if ( rtl ) {\r | |
325 | if ( spaceAfter < rectWidth ) {\r | |
326 | // Flip to show on right.\r | |
327 | if ( spaceBefore > rectWidth )\r | |
328 | left += rectWidth;\r | |
329 | // Align to window left.\r | |
330 | else if ( viewportSize.width > rectWidth )\r | |
331 | left = left - rect.left;\r | |
332 | // Align to window right, never cutting the panel at right.\r | |
333 | else\r | |
334 | left = left - rect.right + viewportSize.width;\r | |
335 | }\r | |
336 | } else if ( spaceAfter < rectWidth ) {\r | |
337 | // Flip to show on left.\r | |
338 | if ( spaceBefore > rectWidth )\r | |
339 | left -= rectWidth;\r | |
340 | // Align to window right.\r | |
341 | else if ( viewportSize.width > rectWidth )\r | |
342 | left = left - rect.right + viewportSize.width;\r | |
343 | // Align to window left, never cutting the panel at left.\r | |
344 | else\r | |
345 | left = left - rect.left;\r | |
346 | }\r | |
347 | \r | |
348 | \r | |
349 | // Check if the default vertical layout is possible.\r | |
350 | var spaceBelow = viewportSize.height - rect.top,\r | |
351 | spaceAbove = rect.top;\r | |
352 | \r | |
353 | if ( spaceBelow < rectHeight ) {\r | |
354 | // Flip to show above.\r | |
355 | if ( spaceAbove > rectHeight )\r | |
356 | top -= rectHeight;\r | |
357 | // Align to window bottom.\r | |
358 | else if ( viewportSize.height > rectHeight )\r | |
359 | top = top - rect.bottom + viewportSize.height;\r | |
360 | // Align to top, never cutting the panel at top.\r | |
361 | else\r | |
362 | top = top - rect.top;\r | |
363 | }\r | |
364 | \r | |
365 | // If IE is in RTL, we have troubles with absolute\r | |
366 | // position and horizontal scrolls. Here we have a\r | |
367 | // series of hacks to workaround it. (#6146)\r | |
368 | if ( CKEDITOR.env.ie ) {\r | |
369 | var offsetParent = new CKEDITOR.dom.element( element.$.offsetParent ),\r | |
370 | scrollParent = offsetParent;\r | |
371 | \r | |
372 | // Quirks returns <body>, but standards returns <html>.\r | |
373 | if ( scrollParent.getName() == 'html' )\r | |
374 | scrollParent = scrollParent.getDocument().getBody();\r | |
375 | \r | |
376 | if ( scrollParent.getComputedStyle( 'direction' ) == 'rtl' ) {\r | |
377 | // For IE8, there is not much logic on this, but it works.\r | |
378 | if ( CKEDITOR.env.ie8Compat )\r | |
379 | left -= element.getDocument().getDocumentElement().$.scrollLeft * 2;\r | |
380 | else\r | |
381 | left -= ( offsetParent.$.scrollWidth - offsetParent.$.clientWidth );\r | |
382 | }\r | |
383 | }\r | |
384 | \r | |
385 | // Trigger the onHide event of the previously active panel to prevent\r | |
386 | // incorrect styles from being applied (#6170)\r | |
387 | var innerElement = element.getFirst(),\r | |
388 | activePanel;\r | |
389 | if ( ( activePanel = innerElement.getCustomData( 'activePanel' ) ) )\r | |
390 | activePanel.onHide && activePanel.onHide.call( this, 1 );\r | |
391 | innerElement.setCustomData( 'activePanel', this );\r | |
392 | \r | |
393 | element.setStyles( {\r | |
394 | top: top + 'px',\r | |
395 | left: left + 'px'\r | |
396 | } );\r | |
397 | element.setOpacity( 1 );\r | |
398 | \r | |
399 | callback && callback();\r | |
400 | }, this );\r | |
401 | \r | |
402 | panel.isLoaded ? panelLoad() : panel.onLoad = panelLoad;\r | |
403 | \r | |
404 | CKEDITOR.tools.setTimeout( function() {\r | |
405 | var scrollTop = CKEDITOR.env.webkit && CKEDITOR.document.getWindow().getScrollPosition().y;\r | |
406 | \r | |
407 | // Focus the panel frame first, so blur gets fired.\r | |
408 | this.focus();\r | |
409 | \r | |
410 | // Focus the block now.\r | |
411 | block.element.focus();\r | |
412 | \r | |
413 | // #10623, #10951 - restore the viewport's scroll position after focusing list element.\r | |
414 | if ( CKEDITOR.env.webkit )\r | |
415 | CKEDITOR.document.getBody().$.scrollTop = scrollTop;\r | |
416 | \r | |
417 | // We need this get fired manually because of unfired focus() function.\r | |
418 | this.allowBlur( true );\r | |
419 | this._.editor.fire( 'panelShow', this );\r | |
420 | }, 0, this );\r | |
421 | }, CKEDITOR.env.air ? 200 : 0, this );\r | |
422 | this.visible = 1;\r | |
423 | \r | |
424 | if ( this.onShow )\r | |
425 | this.onShow.call( this );\r | |
426 | },\r | |
427 | \r | |
428 | /**\r | |
429 | * Repositions the panel with the same parameters that were used in the last {@link #showBlock} call.\r | |
430 | *\r | |
431 | * @since 4.5.4\r | |
432 | */\r | |
433 | reposition: function() {\r | |
434 | var blockParams = this._.showBlockParams;\r | |
435 | \r | |
436 | if ( this.visible && this._.showBlockParams ) {\r | |
437 | this.hide();\r | |
438 | this.showBlock.apply( this, blockParams );\r | |
439 | }\r | |
440 | },\r | |
441 | \r | |
442 | /**\r | |
443 | * Restores the last focused element or simply focuses the panel window.\r | |
444 | */\r | |
445 | focus: function() {\r | |
446 | // Webkit requires to blur any previous focused page element, in\r | |
447 | // order to properly fire the "focus" event.\r | |
448 | if ( CKEDITOR.env.webkit ) {\r | |
449 | var active = CKEDITOR.document.getActive();\r | |
450 | active && !active.equals( this._.iframe ) && active.$.blur();\r | |
451 | }\r | |
452 | \r | |
453 | // Restore last focused element or simply focus panel window.\r | |
454 | var focus = this._.lastFocused || this._.iframe.getFrameDocument().getWindow();\r | |
455 | focus.focus();\r | |
456 | },\r | |
457 | \r | |
458 | /**\r | |
459 | * @todo\r | |
460 | */\r | |
461 | blur: function() {\r | |
462 | var doc = this._.iframe.getFrameDocument(),\r | |
463 | active = doc.getActive();\r | |
464 | \r | |
465 | active && active.is( 'a' ) && ( this._.lastFocused = active );\r | |
466 | },\r | |
467 | \r | |
468 | /**\r | |
469 | * Hides the panel.\r | |
470 | *\r | |
471 | * @todo\r | |
472 | */\r | |
473 | hide: function( returnFocus ) {\r | |
474 | if ( this.visible && ( !this.onHide || this.onHide.call( this ) !== true ) ) {\r | |
475 | this.hideChild();\r | |
476 | // Blur previously focused element. (#6671)\r | |
477 | CKEDITOR.env.gecko && this._.iframe.getFrameDocument().$.activeElement.blur();\r | |
478 | this.element.setStyle( 'display', 'none' );\r | |
479 | this.visible = 0;\r | |
480 | this.element.getFirst().removeCustomData( 'activePanel' );\r | |
481 | \r | |
482 | // Return focus properly. (#6247)\r | |
483 | var focusReturn = returnFocus && this._.returnFocus;\r | |
484 | if ( focusReturn ) {\r | |
485 | // Webkit requires focus moved out panel iframe first.\r | |
486 | if ( CKEDITOR.env.webkit && focusReturn.type )\r | |
487 | focusReturn.getWindow().$.focus();\r | |
488 | \r | |
489 | focusReturn.focus();\r | |
490 | }\r | |
491 | \r | |
492 | delete this._.lastFocused;\r | |
493 | this._.showBlockParams = null;\r | |
494 | \r | |
495 | this._.editor.fire( 'panelHide', this );\r | |
496 | }\r | |
497 | },\r | |
498 | \r | |
499 | /**\r | |
500 | * @todo\r | |
501 | */\r | |
502 | allowBlur: function( allow ) {\r | |
503 | // Prevent editor from hiding the panel. (#3222)\r | |
504 | var panel = this._.panel;\r | |
505 | if ( allow !== undefined )\r | |
506 | panel.allowBlur = allow;\r | |
507 | \r | |
508 | return panel.allowBlur;\r | |
509 | },\r | |
510 | \r | |
511 | /**\r | |
512 | * Shows the specified panel as a child of one block of this one.\r | |
513 | *\r | |
514 | * @param {CKEDITOR.ui.floatPanel} panel\r | |
515 | * @param {String} blockName\r | |
516 | * @param {CKEDITOR.dom.element} offsetParent Positioned parent.\r | |
517 | * @param {Number} corner\r | |
518 | *\r | |
519 | * * For LTR (left to right) oriented editor:\r | |
520 | * * `1` = top-left\r | |
521 | * * `2` = top-right\r | |
522 | * * `3` = bottom-right\r | |
523 | * * `4` = bottom-left\r | |
524 | * * For RTL (right to left):\r | |
525 | * * `1` = top-right\r | |
526 | * * `2` = top-left\r | |
527 | * * `3` = bottom-left\r | |
528 | * * `4` = bottom-right\r | |
529 | *\r | |
530 | * @param {Number} [offsetX=0]\r | |
531 | * @param {Number} [offsetY=0]\r | |
532 | * @todo\r | |
533 | */\r | |
534 | showAsChild: function( panel, blockName, offsetParent, corner, offsetX, offsetY ) {\r | |
535 | // Skip reshowing of child which is already visible.\r | |
536 | if ( this._.activeChild == panel && panel._.panel._.offsetParentId == offsetParent.getId() )\r | |
537 | return;\r | |
538 | \r | |
539 | this.hideChild();\r | |
540 | \r | |
541 | panel.onHide = CKEDITOR.tools.bind( function() {\r | |
542 | // Use a timeout, so we give time for this menu to get\r | |
543 | // potentially focused.\r | |
544 | CKEDITOR.tools.setTimeout( function() {\r | |
545 | if ( !this._.focused )\r | |
546 | this.hide();\r | |
547 | }, 0, this );\r | |
548 | }, this );\r | |
549 | \r | |
550 | this._.activeChild = panel;\r | |
551 | this._.focused = false;\r | |
552 | \r | |
553 | panel.showBlock( blockName, offsetParent, corner, offsetX, offsetY );\r | |
554 | this.blur();\r | |
555 | \r | |
556 | /* #3767 IE: Second level menu may not have borders */\r | |
557 | if ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) {\r | |
558 | setTimeout( function() {\r | |
559 | panel.element.getChild( 0 ).$.style.cssText += '';\r | |
560 | }, 100 );\r | |
561 | }\r | |
562 | },\r | |
563 | \r | |
564 | /**\r | |
565 | * @todo\r | |
566 | */\r | |
567 | hideChild: function( restoreFocus ) {\r | |
568 | var activeChild = this._.activeChild;\r | |
569 | \r | |
570 | if ( activeChild ) {\r | |
571 | delete activeChild.onHide;\r | |
572 | delete this._.activeChild;\r | |
573 | activeChild.hide();\r | |
574 | \r | |
575 | // At this point focus should be moved back to parent panel.\r | |
576 | restoreFocus && this.focus();\r | |
577 | }\r | |
578 | }\r | |
579 | }\r | |
580 | } );\r | |
581 | \r | |
582 | CKEDITOR.on( 'instanceDestroyed', function() {\r | |
583 | var isLastInstance = CKEDITOR.tools.isEmpty( CKEDITOR.instances );\r | |
584 | \r | |
585 | for ( var i in panels ) {\r | |
586 | var panel = panels[ i ];\r | |
587 | // Safe to destroy it since there're no more instances.(#4241)\r | |
588 | if ( isLastInstance )\r | |
589 | panel.destroy();\r | |
590 | // Panel might be used by other instances, just hide them.(#4552)\r | |
591 | else\r | |
592 | panel.element.hide();\r | |
593 | }\r | |
594 | // Remove the registration.\r | |
595 | isLastInstance && ( panels = {} );\r | |
596 | \r | |
597 | } );\r | |
598 | } )();\r |