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