]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/plugins/menu/plugin.js
3596e527a764fcc22fcaebf3f78e882750a80079
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
6 CKEDITOR
.plugins
.add( 'menu', {
7 requires: 'floatpanel',
9 beforeInit: function( editor
) {
10 var groups
= editor
.config
.menu_groups
.split( ',' ),
11 groupsOrder
= editor
._
.menuGroups
= {},
12 menuItems
= editor
._
.menuItems
= {};
14 for ( var i
= 0; i
< groups
.length
; i
++ )
15 groupsOrder
[ groups
[ i
] ] = i
+ 1;
18 * Registers an item group to the editor context menu in order to make it
19 * possible to associate it with menu items later.
21 * @param {String} name Specify a group name.
22 * @param {Number} [order=100] Define the display sequence of this group
23 * inside the menu. A smaller value gets displayed first.
24 * @member CKEDITOR.editor
26 editor
.addMenuGroup = function( name
, order
) {
27 groupsOrder
[ name
] = order
|| 100;
31 * Adds an item from the specified definition to the editor context menu.
34 * @param {String} name The menu item name.
35 * @param {Object} definition The menu item definition.
36 * @member CKEDITOR.editor
38 editor
.addMenuItem = function( name
, definition
) {
39 if ( groupsOrder
[ definition
.group
] )
40 menuItems
[ name
] = new CKEDITOR
.menuItem( this, name
, definition
);
44 * Adds one or more items from the specified definition object to the editor context menu.
47 * @param {Object} definitions Object where keys are used as itemName and corresponding values as definition for a {@link #addMenuItem} call.
48 * @member CKEDITOR.editor
50 editor
.addMenuItems = function( definitions
) {
51 for ( var itemName
in definitions
) {
52 this.addMenuItem( itemName
, definitions
[ itemName
] );
57 * Retrieves a particular menu item definition from the editor context menu.
60 * @param {String} name The name of the desired menu item.
62 * @member CKEDITOR.editor
64 editor
.getMenuItem = function( name
) {
65 return menuItems
[ name
];
69 * Removes a particular menu item added before from the editor context menu.
73 * @param {String} name The name of the desired menu item.
74 * @member CKEDITOR.editor
76 editor
.removeMenuItem = function( name
) {
77 delete menuItems
[ name
];
83 var menuItemSource
= '<span class="cke_menuitem">' +
85 ' class="cke_menubutton cke_menubutton__{name} cke_menubutton_{state} {cls}" href="{href}"' +
91 ' aria-label="{label}"' +
92 ' aria-describedby="{id}_description"' +
93 ' aria-haspopup="{hasPopup}"' +
94 ' aria-disabled="{disabled}"' +
98 // Some browsers don't cancel key events in the keydown but in the
100 // TODO: Check if really needed.
101 if ( CKEDITOR
.env
.gecko
&& CKEDITOR
.env
.mac
)
102 menuItemSource
+= ' onkeypress="return false;"';
104 // With Firefox, we need to force the button to redraw, otherwise it
105 // will remain in the focus state. Also we some extra help to prevent dragging (http://dev.ckeditor.com/ticket/10373).
106 if ( CKEDITOR
.env
.gecko
) {
107 menuItemSource
+= ( ' onblur="this.style.cssText = this.style.cssText;"' +
108 ' ondragstart="return false;"' );
111 // http://dev.ckeditor.com/ticket/188
112 menuItemSource
+= ' onmouseover="CKEDITOR.tools.callFunction({hoverFn},{index});"' +
113 ' onmouseout="CKEDITOR.tools.callFunction({moveOutFn},{index});" ' +
114 ( CKEDITOR
.env
.ie
? 'onclick="return false;" onmouseup' : 'onclick' ) +
115 '="CKEDITOR.tools.callFunction({clickFn},{index}); return false;"' +
120 '<span class="cke_menubutton_inner">' +
121 '<span class="cke_menubutton_icon">' +
122 '<span class="cke_button_icon cke_button__{iconName}_icon" style="{iconStyle}"></span>' +
124 '<span class="cke_menubutton_label">' +
130 '</a><span id="{id}_description" class="cke_voice_label" aria-hidden="false">{ariaShortcut}</span></span>';
132 var menuArrowSource
= '<span class="cke_menuarrow">' +
133 '<span>{label}</span>' +
136 var menuShortcutSource
= '<span class="cke_menubutton_label cke_menubutton_shortcut">' +
140 var menuItemTpl
= CKEDITOR
.addTemplate( 'menuItem', menuItemSource
),
141 menuArrowTpl
= CKEDITOR
.addTemplate( 'menuArrow', menuArrowSource
),
142 menuShortcutTpl
= CKEDITOR
.addTemplate( 'menuShortcut', menuShortcutSource
);
148 CKEDITOR
.menu
= CKEDITOR
.tools
.createClass( {
152 $: function( editor
, definition
) {
153 definition
= this._
.definition
= definition
|| {};
154 this.id
= CKEDITOR
.tools
.getNextId();
156 this.editor
= editor
;
158 this._
.listeners
= [];
160 this._
.level
= definition
.level
|| 1;
162 var panelDefinition
= CKEDITOR
.tools
.extend( {}, definition
.panel
, {
163 css: [ CKEDITOR
.skin
.getPath( 'editor' ) ],
164 level: this._
.level
- 1,
168 var attrs
= panelDefinition
.block
.attributes
= ( panelDefinition
.attributes
|| {} );
169 // Provide default role of 'menu'.
170 !attrs
.role
&& ( attrs
.role
= 'menu' );
171 this._
.panelDefinition
= panelDefinition
;
176 var selection
= this.editor
.getSelection(),
177 start
= selection
&& selection
.getStartElement(),
178 path
= this.editor
.elementPath(),
179 listeners
= this._
.listeners
;
182 // Call all listeners, filling the list of items to be displayed.
183 for ( var i
= 0; i
< listeners
.length
; i
++ ) {
184 var listenerItems
= listeners
[ i
]( start
, selection
, path
);
186 if ( listenerItems
) {
187 for ( var itemName
in listenerItems
) {
188 var item
= this.editor
.getMenuItem( itemName
);
190 if ( item
&& ( !item
.command
|| this.editor
.getCommand( item
.command
).state
) ) {
191 item
.state
= listenerItems
[ itemName
];
199 onClick: function( item
) {
204 else if ( item
.command
)
205 this.editor
.execCommand( item
.command
);
208 onEscape: function( keystroke
) {
209 var parent
= this.parent
;
210 // 1. If it's sub-menu, close it, with focus restored on this.
211 // 2. In case of a top-menu, close it, with focus returned to page.
213 parent
._
.panel
.hideChild( 1 );
214 else if ( keystroke
== 27 )
221 this.onHide
&& this.onHide();
224 showSubMenu: function( index
) {
225 var menu
= this._
.subMenu
,
226 item
= this.items
[ index
],
227 subItemDefs
= item
.getItems
&& item
.getItems();
229 // If this item has no subitems, we just hide the submenu, if
230 // available, and return back.
231 if ( !subItemDefs
) {
232 // Hide sub menu with focus returned.
233 this._
.panel
.hideChild( 1 );
237 // Create the submenu, if not available, or clean the existing
242 menu
= this._
.subMenu
= new CKEDITOR
.menu( this.editor
, CKEDITOR
.tools
.extend( {}, this._
.definition
, { level: this._
.level
+ 1 }, true ) );
244 menu
._
.onClick
= CKEDITOR
.tools
.bind( this._
.onClick
, this );
247 // Add all submenu items to the menu.
248 for ( var subItemName
in subItemDefs
) {
249 var subItem
= this.editor
.getMenuItem( subItemName
);
251 subItem
.state
= subItemDefs
[ subItemName
];
256 // Get the element representing the current item.
257 var element
= this._
.panel
.getBlock( this.id
).element
.getDocument().getById( this.id
+ String( index
) );
260 // This timeout is needed to give time for the sub-menu get
261 // focus when JAWS is running. (http://dev.ckeditor.com/ticket/9844)
262 setTimeout( function() {
263 menu
.show( element
, 2 );
274 add: function( item
) {
275 // Later we may sort the items, but Array#sort is not stable in
276 // some browsers, here we're forcing the original sequence with
277 // 'order' attribute if it hasn't been assigned. (http://dev.ckeditor.com/ticket/3868)
279 item
.order
= this.items
.length
;
281 this.items
.push( item
);
287 removeAll: function() {
292 * Shows the menu in given location.
294 * @param {CKEDITOR.dom.element} offsetParent
295 * @param {Number} [corner]
296 * @param {Number} [offsetX]
297 * @param {Number} [offsetY]
299 show: function( offsetParent
, corner
, offsetX
, offsetY
) {
301 if ( !this.parent
) {
303 // Don't menu with zero items.
304 if ( !this.items
.length
)
308 corner
= corner
|| ( this.editor
.lang
.dir
== 'rtl' ? 2 : 1 );
310 var items
= this.items
,
311 editor
= this.editor
,
312 panel
= this._
.panel
,
313 element
= this._
.element
;
315 // Create the floating panel for this menu.
317 panel
= this._
.panel
= new CKEDITOR
.ui
.floatPanel( this.editor
, CKEDITOR
.document
.getBody(), this._
.panelDefinition
, this._
.level
);
319 panel
.onEscape
= CKEDITOR
.tools
.bind( function( keystroke
) {
320 if ( this._
.onEscape( keystroke
) === false )
324 panel
.onShow = function() {
325 // Menu need CSS resets, compensate class name.
326 var holder
= panel
._
.panel
.getHolderElement();
327 holder
.getParent().addClass( 'cke' ).addClass( 'cke_reset_all' );
330 panel
.onHide
= CKEDITOR
.tools
.bind( function() {
331 this._
.onHide
&& this._
.onHide();
334 // Create an autosize block inside the panel.
335 var block
= panel
.addBlock( this.id
, this._
.panelDefinition
.block
);
336 block
.autoSize
= true;
338 var keys
= block
.keys
;
339 keys
[ 40 ] = 'next'; // ARROW-DOWN
340 keys
[ 9 ] = 'next'; // TAB
341 keys
[ 38 ] = 'prev'; // ARROW-UP
342 keys
[ CKEDITOR
.SHIFT
+ 9 ] = 'prev'; // SHIFT + TAB
343 keys
[ ( editor
.lang
.dir
== 'rtl' ? 37 : 39 ) ] = CKEDITOR
.env
.ie
? 'mouseup' : 'click'; // ARROW-RIGHT/ARROW-LEFT(rtl)
344 keys
[ 32 ] = CKEDITOR
.env
.ie
? 'mouseup' : 'click'; // SPACE
345 CKEDITOR
.env
.ie
&& ( keys
[ 13 ] = 'mouseup' ); // Manage ENTER, since onclick is blocked in IE (http://dev.ckeditor.com/ticket/8041).
347 element
= this._
.element
= block
.element
;
349 var elementDoc
= element
.getDocument();
350 elementDoc
.getBody().setStyle( 'overflow', 'hidden' );
351 elementDoc
.getElementsByTag( 'html' ).getItem( 0 ).setStyle( 'overflow', 'hidden' );
353 this._
.itemOverFn
= CKEDITOR
.tools
.addFunction( function( index
) {
354 clearTimeout( this._
.showSubTimeout
);
355 this._
.showSubTimeout
= CKEDITOR
.tools
.setTimeout( this._
.showSubMenu
, editor
.config
.menu_subMenuDelay
|| 400, this, [ index
] );
358 this._
.itemOutFn
= CKEDITOR
.tools
.addFunction( function() {
359 clearTimeout( this._
.showSubTimeout
);
362 this._
.itemClickFn
= CKEDITOR
.tools
.addFunction( function( index
) {
363 var item
= this.items
[ index
];
365 if ( item
.state
== CKEDITOR
.TRISTATE_DISABLED
) {
371 this._
.showSubMenu( index
);
373 this._
.onClick( item
);
377 // Put the items in the right order.
380 // Apply the editor mixed direction status to menu.
381 var path
= editor
.elementPath(),
382 mixedDirCls
= ( path
&& path
.direction() != editor
.lang
.dir
) ? ' cke_mixed_dir_content' : '';
384 // Build the HTML that composes the menu and its items.
385 var output
= [ '<div class="cke_menu' + mixedDirCls
+ '" role="presentation">' ];
387 var length
= items
.length
,
388 lastGroup
= length
&& items
[ 0 ].group
;
390 for ( var i
= 0; i
< length
; i
++ ) {
391 var item
= items
[ i
];
392 if ( lastGroup
!= item
.group
) {
393 output
.push( '<div class="cke_menuseparator" role="separator"></div>' );
394 lastGroup
= item
.group
;
397 item
.render( this, i
, output
);
400 output
.push( '</div>' );
402 // Inject the HTML inside the panel.
403 element
.setHtml( output
.join( '' ) );
405 CKEDITOR
.ui
.fire( 'ready', this );
409 this.parent
._
.panel
.showAsChild( panel
, this.id
, offsetParent
, corner
, offsetX
, offsetY
);
411 panel
.showBlock( this.id
, offsetParent
, corner
, offsetX
, offsetY
);
413 editor
.fire( 'menuShow', [ panel
] );
417 * Adds a callback executed on opening the menu. Items
418 * returned by that callback are added to the menu.
420 * @param {Function} listenerFn
421 * @param {CKEDITOR.dom.element} listenerFn.startElement The selection start anchor element.
422 * @param {CKEDITOR.dom.selection} listenerFn.selection The current selection.
423 * @param {CKEDITOR.dom.elementPath} listenerFn.path The current elements path.
424 * @param listenerFn.return Object (`commandName` => `state`) of items that should be added to the menu.
426 addListener: function( listenerFn
) {
427 this._
.listeners
.push( listenerFn
);
433 * @param {Boolean} [returnFocus]
435 hide: function( returnFocus
) {
436 this._
.onHide
&& this._
.onHide();
437 this._
.panel
&& this._
.panel
.hide( returnFocus
);
442 function sortItems( items
) {
443 items
.sort( function( itemA
, itemB
) {
444 if ( itemA
.group
< itemB
.group
)
446 else if ( itemA
.group
> itemB
.group
)
449 return itemA
.order
< itemB
.order
? -1 : itemA
.order
> itemB
.order
? 1 : 0;
457 CKEDITOR
.menuItem
= CKEDITOR
.tools
.createClass( {
458 $: function( editor
, name
, definition
) {
459 CKEDITOR
.tools
.extend( this, definition
,
463 className: 'cke_menubutton__' + name
466 // Transform the group name into its order number.
467 this.group
= editor
._
.menuGroups
[ this.group
];
469 this.editor
= editor
;
474 render: function( menu
, index
, output
) {
475 var id
= menu
.id
+ String( index
),
476 state
= ( typeof this.state
== 'undefined' ) ? CKEDITOR
.TRISTATE_OFF : this.state
,
478 editor
= this.editor
,
483 var stateName
= state
== CKEDITOR
.TRISTATE_ON
? 'on' : state
== CKEDITOR
.TRISTATE_DISABLED
? 'disabled' : 'off';
485 if ( this.role
in { menuitemcheckbox: 1, menuitemradio: 1 } )
486 ariaChecked
= ' aria-checked="' + ( state
== CKEDITOR
.TRISTATE_ON
? 'true' : 'false' ) + '"';
488 var hasSubMenu
= this.getItems
;
489 // ltr: BLACK LEFT-POINTING POINTER
490 // rtl: BLACK RIGHT-POINTING POINTER
491 var arrowLabel
= '&#' + ( this.editor
.lang
.dir
== 'rtl' ? '9668' : '9658' ) + ';';
493 var iconName
= this.name
;
494 if ( this.icon
&& !( /\./ ).test( this.icon
) )
495 iconName
= this.icon
;
497 if ( this.command
) {
498 command
= editor
.getCommand( this.command
);
499 keystroke
= editor
.getCommandKeystroke( command
);
502 shortcut
= CKEDITOR
.tools
.keystrokeToString( editor
.lang
.common
.keyboard
, keystroke
);
511 cls: this.className
|| '',
513 hasPopup: hasSubMenu
? 'true' : 'false',
514 disabled: state
== CKEDITOR
.TRISTATE_DISABLED
,
515 title: this.label
+ ( shortcut
? ' (' + shortcut
.display
+ ')' : '' ),
516 ariaShortcut: shortcut
? editor
.lang
.common
.keyboardShortcut
+ ' ' + shortcut
.aria : '',
517 href: 'javascript:void(\'' + ( this.label
|| '' ).replace( "'" + '' ) + '\')', // jshint ignore:line
518 hoverFn: menu
._
.itemOverFn
,
519 moveOutFn: menu
._
.itemOutFn
,
520 clickFn: menu
._
.itemClickFn
,
522 iconStyle: CKEDITOR
.skin
.getIconStyle( iconName
, ( this.editor
.lang
.dir
== 'rtl' ), iconName
== this.icon
? null : this.icon
, this.iconOffset
),
523 shortcutHtml: shortcut
? menuShortcutTpl
.output( { shortcut: shortcut
.display
} ) : '',
524 arrowHtml: hasSubMenu
? menuArrowTpl
.output( { label: arrowLabel
} ) : '',
525 role: this.role
? this.role : 'menuitem',
526 ariaChecked: ariaChecked
529 menuItemTpl
.output( params
, output
);
538 * The amount of time, in milliseconds, the editor waits before displaying submenu
539 * options when moving the mouse over options that contain submenus, like the
540 * "Cell Properties" entry for tables.
542 * // Remove the submenu delay.
543 * config.menu_subMenuDelay = 0;
545 * @cfg {Number} [menu_subMenuDelay=400]
546 * @member CKEDITOR.config
550 * Fired when a menu is shown.
553 * @member CKEDITOR.editor
554 * @param {CKEDITOR.editor} editor This editor instance.
555 * @param {CKEDITOR.ui.panel[]} data
559 * A comma separated list of items group names to be displayed in the context
560 * menu. The order of items will reflect the order specified in this list if
561 * no priority was defined in the groups.
563 * config.menu_groups = 'clipboard,table,anchor,link,image';
565 * @cfg {String} [menu_groups=see source]
566 * @member CKEDITOR.config
568 CKEDITOR
.config
.menu_groups
= 'clipboard,' +
570 'tablecell,tablecellproperties,tablerow,tablecolumn,table,' +
571 'anchor,link,image,flash,' +
572 'checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea,div';