]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blame - sources/plugins/panel/plugin.js
Update to 4.7.3
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / panel / plugin.js
CommitLineData
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( function() {\r
7 CKEDITOR.plugins.add( 'panel', {\r
8 beforeInit: function( editor ) {\r
9 editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler );\r
10 }\r
11 } );\r
12\r
13 /**\r
14 * Panel UI element.\r
15 *\r
16 * @readonly\r
17 * @property {String} [='panel']\r
18 * @member CKEDITOR\r
19 */\r
20 CKEDITOR.UI_PANEL = 'panel';\r
21\r
22 /**\r
23 * @class\r
24 * @constructor Creates a panel class instance.\r
25 * @param {CKEDITOR.dom.document} document\r
26 * @param {Object} definition\r
27 */\r
28 CKEDITOR.ui.panel = function( document, definition ) {\r
29 // Copy all definition properties to this object.\r
30 if ( definition )\r
31 CKEDITOR.tools.extend( this, definition );\r
32\r
33 // Set defaults.\r
34 CKEDITOR.tools.extend( this, {\r
35 className: '',\r
36 css: []\r
37 } );\r
38\r
39 this.id = CKEDITOR.tools.getNextId();\r
40 this.document = document;\r
41 this.isFramed = this.forceIFrame || this.css.length;\r
42\r
43 this._ = {\r
44 blocks: {}\r
45 };\r
46 };\r
47\r
48 /**\r
49 * Represents panel handler object.\r
50 *\r
51 * @class\r
52 * @singleton\r
53 * @extends CKEDITOR.ui.handlerDefinition\r
54 */\r
55 CKEDITOR.ui.panel.handler = {\r
56 /**\r
57 * Transforms a panel definition in a {@link CKEDITOR.ui.panel} instance.\r
58 *\r
59 * @param {Object} definition\r
60 * @returns {CKEDITOR.ui.panel}\r
61 */\r
62 create: function( definition ) {\r
63 return new CKEDITOR.ui.panel( definition );\r
64 }\r
65 };\r
66\r
67 var panelTpl = CKEDITOR.addTemplate( 'panel', '<div lang="{langCode}" id="{id}" dir={dir}' +\r
68 ' class="cke cke_reset_all {editorId} cke_panel cke_panel {cls} cke_{dir}"' +\r
69 ' style="z-index:{z-index}" role="presentation">' +\r
70 '{frame}' +\r
71 '</div>' );\r
72\r
73 var frameTpl = CKEDITOR.addTemplate( 'panel-frame', '<iframe id="{id}" class="cke_panel_frame" role="presentation" frameborder="0" src="{src}"></iframe>' );\r
74\r
75 var frameDocTpl = CKEDITOR.addTemplate( 'panel-frame-inner', '<!DOCTYPE html>' +\r
76 '<html class="cke_panel_container {env}" dir="{dir}" lang="{langCode}">' +\r
77 '<head>{css}</head>' +\r
78 '<body class="cke_{dir}"' +\r
79 ' style="margin:0;padding:0" onload="{onload}"></body>' +\r
80 '<\/html>' );\r
81\r
82 /** @class CKEDITOR.ui.panel */\r
83 CKEDITOR.ui.panel.prototype = {\r
84 /**\r
85 * Renders the combo.\r
86 *\r
87 * @param {CKEDITOR.editor} editor The editor instance which this button is\r
88 * to be used by.\r
89 * @param {Array} [output] The output array to which append the HTML relative\r
90 * to this button.\r
91 */\r
92 render: function( editor, output ) {\r
93 this.getHolderElement = function() {\r
94 var holder = this._.holder;\r
95\r
96 if ( !holder ) {\r
97 if ( this.isFramed ) {\r
98 var iframe = this.document.getById( this.id + '_frame' ),\r
99 parentDiv = iframe.getParent(),\r
100 doc = iframe.getFrameDocument();\r
101\r
1794320d 102 // Make it scrollable on iOS. (http://dev.ckeditor.com/ticket/8308)\r
c63493c8
IB
103 CKEDITOR.env.iOS && parentDiv.setStyles( {\r
104 'overflow': 'scroll',\r
105 '-webkit-overflow-scrolling': 'touch'\r
106 } );\r
107\r
108 var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function() {\r
109 this.isLoaded = true;\r
110 if ( this.onLoad )\r
111 this.onLoad();\r
112 }, this ) );\r
113\r
114 doc.write( frameDocTpl.output( CKEDITOR.tools.extend( {\r
115 css: CKEDITOR.tools.buildStyleHtml( this.css ),\r
116 onload: 'window.parent.CKEDITOR.tools.callFunction(' + onLoad + ');'\r
117 }, data ) ) );\r
118\r
119 var win = doc.getWindow();\r
120\r
121 // Register the CKEDITOR global.\r
122 win.$.CKEDITOR = CKEDITOR;\r
123\r
1794320d 124 // Arrow keys for scrolling is only preventable with 'keypress' event in Opera (http://dev.ckeditor.com/ticket/4534).\r
c63493c8
IB
125 doc.on( 'keydown', function( evt ) {\r
126 var keystroke = evt.data.getKeystroke(),\r
127 dir = this.document.getById( this.id ).getAttribute( 'dir' );\r
128\r
129 // Delegate key processing to block.\r
130 if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false ) {\r
131 evt.data.preventDefault();\r
132 return;\r
133 }\r
134\r
135 // ESC/ARROW-LEFT(ltr) OR ARROW-RIGHT(rtl)\r
136 if ( keystroke == 27 || keystroke == ( dir == 'rtl' ? 39 : 37 ) ) {\r
137 if ( this.onEscape && this.onEscape( keystroke ) === false )\r
138 evt.data.preventDefault();\r
139 }\r
140 }, this );\r
141\r
142 holder = doc.getBody();\r
143 holder.unselectable();\r
144 CKEDITOR.env.air && CKEDITOR.tools.callFunction( onLoad );\r
145 } else {\r
146 holder = this.document.getById( this.id );\r
147 }\r
148\r
149 this._.holder = holder;\r
150 }\r
151\r
152 return holder;\r
153 };\r
154\r
155 var data = {\r
156 editorId: editor.id,\r
157 id: this.id,\r
158 langCode: editor.langCode,\r
159 dir: editor.lang.dir,\r
160 cls: this.className,\r
161 frame: '',\r
162 env: CKEDITOR.env.cssClass,\r
163 'z-index': editor.config.baseFloatZIndex + 1\r
164 };\r
165\r
166 if ( this.isFramed ) {\r
167 // With IE, the custom domain has to be taken care at first,\r
168 // for other browers, the 'src' attribute should be left empty to\r
169 // trigger iframe's 'load' event.\r
170 var src =\r
171 CKEDITOR.env.air ? 'javascript:void(0)' : // jshint ignore:line\r
172 CKEDITOR.env.ie ? 'javascript:void(function(){' + encodeURIComponent( // jshint ignore:line\r
173 'document.open();' +\r
174 // In IE, the document domain must be set any time we call document.open().\r
175 '(' + CKEDITOR.tools.fixDomain + ')();' +\r
176 'document.close();'\r
177 ) + '}())' :\r
178 '';\r
179\r
180 data.frame = frameTpl.output( {\r
181 id: this.id + '_frame',\r
182 src: src\r
183 } );\r
184 }\r
185\r
186 var html = panelTpl.output( data );\r
187\r
188 if ( output )\r
189 output.push( html );\r
190\r
191 return html;\r
192 },\r
193\r
194 /**\r
195 * @todo\r
196 */\r
197 addBlock: function( name, block ) {\r
198 block = this._.blocks[ name ] = block instanceof CKEDITOR.ui.panel.block ? block : new CKEDITOR.ui.panel.block( this.getHolderElement(), block );\r
199\r
200 if ( !this._.currentBlock )\r
201 this.showBlock( name );\r
202\r
203 return block;\r
204 },\r
205\r
206 /**\r
207 * @todo\r
208 */\r
209 getBlock: function( name ) {\r
210 return this._.blocks[ name ];\r
211 },\r
212\r
213 /**\r
214 * @todo\r
215 */\r
216 showBlock: function( name ) {\r
217 var blocks = this._.blocks,\r
218 block = blocks[ name ],\r
219 current = this._.currentBlock;\r
220\r
221 // ARIA role works better in IE on the body element, while on the iframe\r
1794320d 222 // for FF. (http://dev.ckeditor.com/ticket/8864)\r
c63493c8
IB
223 var holder = !this.forceIFrame || CKEDITOR.env.ie ? this._.holder : this.document.getById( this.id + '_frame' );\r
224\r
225 if ( current )\r
226 current.hide();\r
227\r
228 this._.currentBlock = block;\r
229\r
230 CKEDITOR.fire( 'ariaWidget', holder );\r
231\r
232 // Reset the focus index, so it will always go into the first one.\r
233 block._.focusIndex = -1;\r
234\r
235 this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block );\r
236\r
237 block.show();\r
238\r
239 return block;\r
240 },\r
241\r
242 /**\r
243 * @todo\r
244 */\r
245 destroy: function() {\r
246 this.element && this.element.remove();\r
247 }\r
248 };\r
249\r
250 /**\r
251 * @class\r
252 *\r
253 * @todo class and all methods\r
254 */\r
255 CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass( {\r
256 /**\r
257 * Creates a block class instances.\r
258 *\r
259 * @constructor\r
260 * @todo\r
261 */\r
262 $: function( blockHolder, blockDefinition ) {\r
263 this.element = blockHolder.append( blockHolder.getDocument().createElement( 'div', {\r
264 attributes: {\r
265 'tabindex': -1,\r
266 'class': 'cke_panel_block'\r
267 },\r
268 styles: {\r
269 display: 'none'\r
270 }\r
271 } ) );\r
272\r
273 // Copy all definition properties to this object.\r
274 if ( blockDefinition )\r
275 CKEDITOR.tools.extend( this, blockDefinition );\r
276\r
277 // Set the a11y attributes of this element ...\r
278 this.element.setAttributes( {\r
279 'role': this.attributes.role || 'presentation',\r
280 'aria-label': this.attributes[ 'aria-label' ],\r
281 'title': this.attributes.title || this.attributes[ 'aria-label' ]\r
282 } );\r
283\r
284 this.keys = {};\r
285\r
286 this._.focusIndex = -1;\r
287\r
288 // Disable context menu for panels.\r
289 this.element.disableContextMenu();\r
290 },\r
291\r
292 _: {\r
293\r
294 /**\r
295 * Mark the item specified by the index as current activated.\r
296 */\r
297 markItem: function( index ) {\r
298 if ( index == -1 )\r
299 return;\r
300 var links = this.element.getElementsByTag( 'a' );\r
301 var item = links.getItem( this._.focusIndex = index );\r
302\r
1794320d 303 // Safari need focus on the iframe window first(http://dev.ckeditor.com/ticket/3389), but we need\r
c63493c8
IB
304 // lock the blur to avoid hiding the panel.\r
305 if ( CKEDITOR.env.webkit )\r
306 item.getDocument().getWindow().focus();\r
307 item.focus();\r
308\r
309 this.onMark && this.onMark( item );\r
1794320d
IB
310 },\r
311\r
312 /**\r
313 * Marks the first visible item or the one whose `aria-selected` attribute is set to `true`.\r
314 * The latter has priority over the former.\r
315 *\r
316 * @private\r
317 * @param beforeMark function to be executed just before marking.\r
318 * Used in cases when any preparatory cleanup (like unmarking all items) would simultaneously\r
319 * destroy the information that is needed to determine the focused item.\r
320 */\r
321 markFirstDisplayed: function( beforeMark ) {\r
322 var notDisplayed = function( element ) {\r
323 return element.type == CKEDITOR.NODE_ELEMENT && element.getStyle( 'display' ) == 'none';\r
324 },\r
325 links = this._.getItems(),\r
326 item, focused;\r
327\r
328 for ( var i = links.count() - 1; i >= 0; i-- ) {\r
329 item = links.getItem( i );\r
330\r
331 if ( !item.getAscendant( notDisplayed ) ) {\r
332 focused = item;\r
333 this._.focusIndex = i;\r
334 }\r
335\r
336 if ( item.getAttribute( 'aria-selected' ) == 'true' ) {\r
337 focused = item;\r
338 this._.focusIndex = i;\r
339 break;\r
340 }\r
341 }\r
342\r
343 if ( !focused ) {\r
344 return;\r
345 }\r
346\r
347 if ( beforeMark ) {\r
348 beforeMark();\r
349 }\r
350\r
351 if ( CKEDITOR.env.webkit )\r
352 focused.getDocument().getWindow().focus();\r
353 focused.focus();\r
354\r
355 this.onMark && this.onMark( focused );\r
356 },\r
357\r
358 /**\r
359 * Returns a `CKEDITOR.dom.nodeList` of block items.\r
360 *\r
361 * @returns {*|CKEDITOR.dom.nodeList}\r
362 */\r
363 getItems: function() {\r
364 return this.element.getElementsByTag( 'a' );\r
c63493c8
IB
365 }\r
366 },\r
367\r
368 proto: {\r
369 show: function() {\r
370 this.element.setStyle( 'display', '' );\r
371 },\r
372\r
373 hide: function() {\r
374 if ( !this.onHide || this.onHide.call( this ) !== true )\r
375 this.element.setStyle( 'display', 'none' );\r
376 },\r
377\r
378 onKeyDown: function( keystroke, noCycle ) {\r
379 var keyAction = this.keys[ keystroke ];\r
380 switch ( keyAction ) {\r
381 // Move forward.\r
382 case 'next':\r
383 var index = this._.focusIndex,\r
384 links = this.element.getElementsByTag( 'a' ),\r
385 link;\r
386\r
387 while ( ( link = links.getItem( ++index ) ) ) {\r
388 // Move the focus only if the element is marked with\r
389 // the _cke_focus and it it's visible (check if it has\r
390 // width).\r
391 if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) {\r
392 this._.focusIndex = index;\r
393 link.focus();\r
394 break;\r
395 }\r
396 }\r
397\r
1794320d 398 // If no link was found, cycle and restart from the top. (http://dev.ckeditor.com/ticket/11125)\r
c63493c8
IB
399 if ( !link && !noCycle ) {\r
400 this._.focusIndex = -1;\r
401 return this.onKeyDown( keystroke, 1 );\r
402 }\r
403\r
404 return false;\r
405\r
406 // Move backward.\r
407 case 'prev':\r
408 index = this._.focusIndex;\r
409 links = this.element.getElementsByTag( 'a' );\r
410\r
411 while ( index > 0 && ( link = links.getItem( --index ) ) ) {\r
412 // Move the focus only if the element is marked with\r
413 // the _cke_focus and it it's visible (check if it has\r
414 // width).\r
415 if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) {\r
416 this._.focusIndex = index;\r
417 link.focus();\r
418 break;\r
419 }\r
420\r
421 // Make sure link is null when the loop ends and nothing was\r
1794320d 422 // found (http://dev.ckeditor.com/ticket/11125).\r
c63493c8
IB
423 link = null;\r
424 }\r
425\r
1794320d 426 // If no link was found, cycle and restart from the bottom. (http://dev.ckeditor.com/ticket/11125)\r
c63493c8
IB
427 if ( !link && !noCycle ) {\r
428 this._.focusIndex = links.count();\r
429 return this.onKeyDown( keystroke, 1 );\r
430 }\r
431\r
432 return false;\r
433\r
434 case 'click':\r
435 case 'mouseup':\r
436 index = this._.focusIndex;\r
437 link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );\r
438\r
439 if ( link )\r
440 link.$[ keyAction ] ? link.$[ keyAction ]() : link.$[ 'on' + keyAction ]();\r
441\r
442 return false;\r
443 }\r
444\r
445 return true;\r
446 }\r
447 }\r
448 } );\r
449\r
450} )();\r
451\r
452/**\r
453 * Fired when a panel is added to the document.\r
454 *\r
455 * @event ariaWidget\r
456 * @member CKEDITOR\r
457 * @param {Object} data The element wrapping the panel.\r
458 */\r