2014-04-11 20:07:18 +00:00
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
|
|
|
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @fileOverview The "toolbar" plugin. Renders the default toolbar interface in
|
|
|
|
|
* the editor.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
( function() {
|
|
|
|
|
var toolbox = function() {
|
|
|
|
|
this.toolbars = [];
|
|
|
|
|
this.focusCommandExecuted = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
toolbox.prototype.focus = function() {
|
|
|
|
|
for ( var t = 0, toolbar; toolbar = this.toolbars[ t++ ]; ) {
|
|
|
|
|
for ( var i = 0, item; item = toolbar.items[ i++ ]; ) {
|
|
|
|
|
if ( item.focus ) {
|
|
|
|
|
item.focus();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var commands = {
|
|
|
|
|
toolbarFocus: {
|
|
|
|
|
modes: { wysiwyg: 1, source: 1 },
|
|
|
|
|
readOnly: 1,
|
|
|
|
|
|
|
|
|
|
exec: function( editor ) {
|
|
|
|
|
if ( editor.toolbox ) {
|
|
|
|
|
editor.toolbox.focusCommandExecuted = true;
|
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
// Make the first button focus accessible for IE. (https://dev.ckeditor.com/ticket/3417)
|
2014-04-11 20:07:18 +00:00
|
|
|
|
// Adobe AIR instead need while of delay.
|
2018-06-17 16:07:19 +00:00
|
|
|
|
if ( CKEDITOR.env.ie || CKEDITOR.env.air ) {
|
2014-04-11 20:07:18 +00:00
|
|
|
|
setTimeout( function() {
|
2018-06-17 16:07:19 +00:00
|
|
|
|
editor.toolbox.focus();
|
|
|
|
|
}, 100 );
|
|
|
|
|
} else {
|
2014-04-11 20:07:18 +00:00
|
|
|
|
editor.toolbox.focus();
|
2018-06-17 16:07:19 +00:00
|
|
|
|
}
|
2014-04-11 20:07:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CKEDITOR.plugins.add( 'toolbar', {
|
|
|
|
|
requires: 'button',
|
2018-06-17 16:07:19 +00:00
|
|
|
|
// jscs:disable maximumLineLength
|
|
|
|
|
lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
|
|
|
|
|
// jscs:enable maximumLineLength
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
|
|
|
|
init: function( editor ) {
|
|
|
|
|
var endFlag;
|
|
|
|
|
|
|
|
|
|
var itemKeystroke = function( item, keystroke ) {
|
|
|
|
|
var next, toolbar;
|
|
|
|
|
var rtl = editor.lang.dir == 'rtl',
|
|
|
|
|
toolbarGroupCycling = editor.config.toolbarGroupCycling,
|
|
|
|
|
// Picking right/left key codes.
|
|
|
|
|
rightKeyCode = rtl ? 37 : 39,
|
|
|
|
|
leftKeyCode = rtl ? 39 : 37;
|
|
|
|
|
|
|
|
|
|
toolbarGroupCycling = toolbarGroupCycling === undefined || toolbarGroupCycling;
|
|
|
|
|
|
|
|
|
|
switch ( keystroke ) {
|
|
|
|
|
case 9: // TAB
|
|
|
|
|
case CKEDITOR.SHIFT + 9: // SHIFT + TAB
|
|
|
|
|
// Cycle through the toolbars, starting from the one
|
|
|
|
|
// closest to the current item.
|
|
|
|
|
while ( !toolbar || !toolbar.items.length ) {
|
2018-06-17 16:07:19 +00:00
|
|
|
|
if ( keystroke == 9 ) {
|
|
|
|
|
toolbar = ( ( toolbar ? toolbar.next : item.toolbar.next ) || editor.toolbox.toolbars[ 0 ] );
|
|
|
|
|
} else {
|
|
|
|
|
toolbar = ( ( toolbar ? toolbar.previous : item.toolbar.previous ) || editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ] );
|
|
|
|
|
}
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
|
|
|
|
// Look for the first item that accepts focus.
|
|
|
|
|
if ( toolbar.items.length ) {
|
|
|
|
|
item = toolbar.items[ endFlag ? ( toolbar.items.length - 1 ) : 0 ];
|
|
|
|
|
while ( item && !item.focus ) {
|
|
|
|
|
item = endFlag ? item.previous : item.next;
|
|
|
|
|
|
|
|
|
|
if ( !item )
|
|
|
|
|
toolbar = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( item )
|
|
|
|
|
item.focus();
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case rightKeyCode:
|
|
|
|
|
next = item;
|
|
|
|
|
do {
|
|
|
|
|
// Look for the next item in the toolbar.
|
|
|
|
|
next = next.next;
|
|
|
|
|
|
|
|
|
|
// If it's the last item, cycle to the first one.
|
|
|
|
|
if ( !next && toolbarGroupCycling ) next = item.toolbar.items[ 0 ];
|
|
|
|
|
}
|
|
|
|
|
while ( next && !next.focus );
|
|
|
|
|
|
|
|
|
|
// If available, just focus it, otherwise focus the
|
|
|
|
|
// first one.
|
|
|
|
|
if ( next )
|
|
|
|
|
next.focus();
|
|
|
|
|
else
|
|
|
|
|
// Send a TAB.
|
|
|
|
|
itemKeystroke( item, 9 );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
case 40: // DOWN-ARROW
|
|
|
|
|
if ( item.button && item.button.hasArrow ) {
|
|
|
|
|
item.execute();
|
|
|
|
|
} else {
|
|
|
|
|
// Send left arrow key.
|
|
|
|
|
itemKeystroke( item, keystroke == 40 ? rightKeyCode : leftKeyCode );
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
case leftKeyCode:
|
|
|
|
|
case 38: // UP-ARROW
|
|
|
|
|
next = item;
|
|
|
|
|
do {
|
|
|
|
|
// Look for the previous item in the toolbar.
|
|
|
|
|
next = next.previous;
|
|
|
|
|
|
|
|
|
|
// If it's the first item, cycle to the last one.
|
|
|
|
|
if ( !next && toolbarGroupCycling ) next = item.toolbar.items[ item.toolbar.items.length - 1 ];
|
|
|
|
|
}
|
|
|
|
|
while ( next && !next.focus );
|
|
|
|
|
|
|
|
|
|
// If available, just focus it, otherwise focus the
|
|
|
|
|
// last one.
|
|
|
|
|
if ( next )
|
|
|
|
|
next.focus();
|
|
|
|
|
else {
|
|
|
|
|
endFlag = 1;
|
|
|
|
|
// Send a SHIFT + TAB.
|
|
|
|
|
itemKeystroke( item, CKEDITOR.SHIFT + 9 );
|
|
|
|
|
endFlag = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case 27: // ESC
|
|
|
|
|
editor.focus();
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case 13: // ENTER
|
|
|
|
|
case 32: // SPACE
|
|
|
|
|
item.execute();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
editor.on( 'uiSpace', function( event ) {
|
|
|
|
|
if ( event.data.space != editor.config.toolbarLocation )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Create toolbar only once.
|
|
|
|
|
event.removeListener();
|
|
|
|
|
|
|
|
|
|
editor.toolbox = new toolbox();
|
|
|
|
|
|
|
|
|
|
var labelId = CKEDITOR.tools.getNextId();
|
|
|
|
|
|
|
|
|
|
var output = [
|
|
|
|
|
'<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbar.toolbars, '</span>',
|
2018-06-17 16:07:19 +00:00
|
|
|
|
'<span id="' + editor.ui.spaceId( 'toolbox' ) + '" class="cke_toolbox" role="group" aria-labelledby="', labelId, '" onmousedown="return false;">'
|
|
|
|
|
];
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
|
|
|
|
var expanded = editor.config.toolbarStartupExpanded !== false,
|
|
|
|
|
groupStarted, pendingSeparator;
|
|
|
|
|
|
|
|
|
|
// If the toolbar collapser will be available, we'll have
|
|
|
|
|
// an additional container for all toolbars.
|
|
|
|
|
if ( editor.config.toolbarCanCollapse && editor.elementMode != CKEDITOR.ELEMENT_MODE_INLINE )
|
|
|
|
|
output.push( '<span class="cke_toolbox_main"' + ( expanded ? '>' : ' style="display:none">' ) );
|
|
|
|
|
|
|
|
|
|
var toolbars = editor.toolbox.toolbars,
|
2018-06-17 16:07:19 +00:00
|
|
|
|
toolbar = getToolbarConfig( editor ),
|
|
|
|
|
toolbarLength = toolbar.length;
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
for ( var r = 0; r < toolbarLength; r++ ) {
|
2014-04-11 20:07:18 +00:00
|
|
|
|
var toolbarId,
|
|
|
|
|
toolbarObj = 0,
|
|
|
|
|
toolbarName,
|
|
|
|
|
row = toolbar[ r ],
|
2018-06-17 16:07:19 +00:00
|
|
|
|
lastToolbarInRow = row !== '/' && ( toolbar[ r + 1 ] === '/' || r == toolbarLength - 1 ),
|
2014-04-11 20:07:18 +00:00
|
|
|
|
items;
|
|
|
|
|
|
|
|
|
|
// It's better to check if the row object is really
|
|
|
|
|
// available because it's a common mistake to leave
|
|
|
|
|
// an extra comma in the toolbar definition
|
|
|
|
|
// settings, which leads on the editor not loading
|
2018-06-17 16:07:19 +00:00
|
|
|
|
// at all in IE. (https://dev.ckeditor.com/ticket/3983)
|
2014-04-11 20:07:18 +00:00
|
|
|
|
if ( !row )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ( groupStarted ) {
|
|
|
|
|
output.push( '</span>' );
|
|
|
|
|
groupStarted = 0;
|
|
|
|
|
pendingSeparator = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( row === '/' ) {
|
|
|
|
|
output.push( '<span class="cke_toolbar_break"></span>' );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
items = row.items || row;
|
|
|
|
|
|
|
|
|
|
// Create all items defined for this toolbar.
|
|
|
|
|
for ( var i = 0; i < items.length; i++ ) {
|
|
|
|
|
var item = items[ i ],
|
|
|
|
|
canGroup;
|
|
|
|
|
|
|
|
|
|
if ( item ) {
|
|
|
|
|
if ( item.type == CKEDITOR.UI_SEPARATOR ) {
|
|
|
|
|
// Do not add the separator immediately. Just save
|
|
|
|
|
// it be included if we already have something in
|
|
|
|
|
// the toolbar and if a new item is to be added (later).
|
|
|
|
|
pendingSeparator = groupStarted && item;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canGroup = item.canGroup !== false;
|
|
|
|
|
|
|
|
|
|
// Initialize the toolbar first, if needed.
|
|
|
|
|
if ( !toolbarObj ) {
|
|
|
|
|
// Create the basic toolbar object.
|
|
|
|
|
toolbarId = CKEDITOR.tools.getNextId();
|
|
|
|
|
toolbarObj = { id: toolbarId, items: [] };
|
|
|
|
|
toolbarName = row.name && ( editor.lang.toolbar.toolbarGroups[ row.name ] || row.name );
|
|
|
|
|
|
|
|
|
|
// Output the toolbar opener.
|
2018-06-17 16:07:19 +00:00
|
|
|
|
output.push( '<span id="', toolbarId, '" class="cke_toolbar' + ( lastToolbarInRow ? ' cke_toolbar_last"' : '"' ),
|
|
|
|
|
( toolbarName ? ' aria-labelledby="' + toolbarId + '_label"' : '' ), ' role="toolbar">' );
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
|
|
|
|
// If a toolbar name is available, send the voice label.
|
|
|
|
|
toolbarName && output.push( '<span id="', toolbarId, '_label" class="cke_voice_label">', toolbarName, '</span>' );
|
|
|
|
|
|
|
|
|
|
output.push( '<span class="cke_toolbar_start"></span>' );
|
|
|
|
|
|
|
|
|
|
// Add the toolbar to the "editor.toolbox.toolbars"
|
|
|
|
|
// array.
|
|
|
|
|
var index = toolbars.push( toolbarObj ) - 1;
|
|
|
|
|
|
|
|
|
|
// Create the next/previous reference.
|
|
|
|
|
if ( index > 0 ) {
|
|
|
|
|
toolbarObj.previous = toolbars[ index - 1 ];
|
|
|
|
|
toolbarObj.previous.next = toolbarObj;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( canGroup ) {
|
|
|
|
|
if ( !groupStarted ) {
|
|
|
|
|
output.push( '<span class="cke_toolgroup" role="presentation">' );
|
|
|
|
|
groupStarted = 1;
|
|
|
|
|
}
|
|
|
|
|
} else if ( groupStarted ) {
|
|
|
|
|
output.push( '</span>' );
|
|
|
|
|
groupStarted = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
function addItem( item ) { // jshint ignore:line
|
2014-04-11 20:07:18 +00:00
|
|
|
|
var itemObj = item.render( editor, output );
|
|
|
|
|
index = toolbarObj.items.push( itemObj ) - 1;
|
|
|
|
|
|
|
|
|
|
if ( index > 0 ) {
|
|
|
|
|
itemObj.previous = toolbarObj.items[ index - 1 ];
|
|
|
|
|
itemObj.previous.next = itemObj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
itemObj.toolbar = toolbarObj;
|
|
|
|
|
itemObj.onkey = itemKeystroke;
|
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
// Fix for https://dev.ckeditor.com/ticket/3052:
|
2014-04-11 20:07:18 +00:00
|
|
|
|
// Prevent JAWS from focusing the toolbar after document load.
|
|
|
|
|
itemObj.onfocus = function() {
|
|
|
|
|
if ( !editor.toolbox.focusCommandExecuted )
|
|
|
|
|
editor.focus();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( pendingSeparator ) {
|
|
|
|
|
addItem( pendingSeparator );
|
|
|
|
|
pendingSeparator = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addItem( item );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( groupStarted ) {
|
|
|
|
|
output.push( '</span>' );
|
|
|
|
|
groupStarted = 0;
|
|
|
|
|
pendingSeparator = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( toolbarObj )
|
|
|
|
|
output.push( '<span class="cke_toolbar_end"></span></span>' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( editor.config.toolbarCanCollapse )
|
|
|
|
|
output.push( '</span>' );
|
|
|
|
|
|
|
|
|
|
// Not toolbar collapser for inline mode.
|
|
|
|
|
if ( editor.config.toolbarCanCollapse && editor.elementMode != CKEDITOR.ELEMENT_MODE_INLINE ) {
|
|
|
|
|
var collapserFn = CKEDITOR.tools.addFunction( function() {
|
|
|
|
|
editor.execCommand( 'toolbarCollapse' );
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
editor.on( 'destroy', function() {
|
|
|
|
|
CKEDITOR.tools.removeFunction( collapserFn );
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
editor.addCommand( 'toolbarCollapse', {
|
|
|
|
|
readOnly: 1,
|
|
|
|
|
exec: function( editor ) {
|
|
|
|
|
var collapser = editor.ui.space( 'toolbar_collapser' ),
|
|
|
|
|
toolbox = collapser.getPrevious(),
|
|
|
|
|
contents = editor.ui.space( 'contents' ),
|
|
|
|
|
toolboxContainer = toolbox.getParent(),
|
|
|
|
|
contentHeight = parseInt( contents.$.style.height, 10 ),
|
|
|
|
|
previousHeight = toolboxContainer.$.offsetHeight,
|
|
|
|
|
minClass = 'cke_toolbox_collapser_min',
|
|
|
|
|
collapsed = collapser.hasClass( minClass );
|
|
|
|
|
|
|
|
|
|
if ( !collapsed ) {
|
|
|
|
|
toolbox.hide();
|
|
|
|
|
collapser.addClass( minClass );
|
|
|
|
|
collapser.setAttribute( 'title', editor.lang.toolbar.toolbarExpand );
|
|
|
|
|
} else {
|
|
|
|
|
toolbox.show();
|
|
|
|
|
collapser.removeClass( minClass );
|
|
|
|
|
collapser.setAttribute( 'title', editor.lang.toolbar.toolbarCollapse );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update collapser symbol.
|
|
|
|
|
collapser.getFirst().setText( collapsed ? '\u25B2' : // BLACK UP-POINTING TRIANGLE
|
|
|
|
|
'\u25C0' ); // BLACK LEFT-POINTING TRIANGLE
|
|
|
|
|
|
|
|
|
|
var dy = toolboxContainer.$.offsetHeight - previousHeight;
|
|
|
|
|
contents.setStyle( 'height', ( contentHeight - dy ) + 'px' );
|
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
editor.fire( 'resize', {
|
|
|
|
|
outerHeight: editor.container.$.offsetHeight,
|
|
|
|
|
contentsHeight: contents.$.offsetHeight,
|
|
|
|
|
outerWidth: editor.container.$.offsetWidth
|
|
|
|
|
} );
|
2014-04-11 20:07:18 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
modes: { wysiwyg: 1, source: 1 }
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
editor.setKeystroke( CKEDITOR.ALT + ( CKEDITOR.env.ie || CKEDITOR.env.webkit ? 189 : 109 ) /*-*/, 'toolbarCollapse' );
|
|
|
|
|
|
2018-06-17 16:07:19 +00:00
|
|
|
|
output.push( '<a title="' + ( expanded ? editor.lang.toolbar.toolbarCollapse : editor.lang.toolbar.toolbarExpand ) +
|
|
|
|
|
'" id="' + editor.ui.spaceId( 'toolbar_collapser' ) +
|
|
|
|
|
'" tabIndex="-1" class="cke_toolbox_collapser' );
|
2014-04-11 20:07:18 +00:00
|
|
|
|
|
|
|
|
|
if ( !expanded )
|
|
|
|
|
output.push( ' cke_toolbox_collapser_min' );
|
|
|
|
|
|
|
|
|
|
output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">', '<span class="cke_arrow">▲</span>', // BLACK UP-POINTING TRIANGLE
|
|
|
|
|
'</a>' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.push( '</span>' );
|
|
|
|
|
event.data.html += output.join( '' );
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
editor.on( 'destroy', function() {
|
2018-06-17 16:07:19 +00:00
|
|
|
|
if ( this.toolbox ) {
|
2014-04-11 20:07:18 +00:00
|
|
|
|
var toolbars,
|
|
|
|
|
index = 0,
|
|
|
|
|
i, items, instance;
|
|
|
|
|
toolbars = this.toolbox.toolbars;
|
|
|
|
|
for ( ; index < toolbars.length; index++ ) {
|
|
|
|
|
items = toolbars[ index ].items;
|
|
|
|
|
for ( i = 0; i < items.length; i++ ) {
|
|
|
|
|
instance = items[ i ];
|
|
|
|
|
if ( instance.clickFn )
|
|
|
|
|
CKEDITOR.tools.removeFunction( instance.clickFn );
|
|
|
|
|
if ( instance.keyDownFn )
|
|
|
|
|
CKEDITOR.tools.removeFunction( instance.keyDownFn );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
// Manage editor focus when navigating the toolbar.
|
|
|
|
|
editor.on( 'uiReady', function() {
|
|
|
|
|
var toolbox = editor.ui.space( 'toolbox' );
|
|
|
|
|
toolbox && editor.focusManager.add( toolbox, 1 );
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
editor.addCommand( 'toolbarFocus', commands.toolbarFocus );
|
|
|
|
|
editor.setKeystroke( CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' );
|
|
|
|
|
|
|
|
|
|
editor.ui.add( '-', CKEDITOR.UI_SEPARATOR, {} );
|
|
|
|
|
editor.ui.addHandler( CKEDITOR.UI_SEPARATOR, {
|
|
|
|
|
create: function() {
|
|
|
|
|
return {
|
|
|
|
|
render: function( editor, output ) {
|
|
|
|
|
output.push( '<span class="cke_toolbar_separator" role="separator"></span>' );
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
function getToolbarConfig( editor ) {
|
|
|
|
|
var removeButtons = editor.config.removeButtons;
|
|
|
|
|
|
|
|
|
|
removeButtons = removeButtons && removeButtons.split( ',' );
|
|
|
|
|
|
|
|
|
|
function buildToolbarConfig() {
|
|
|
|
|
|
|
|
|
|
// Object containing all toolbar groups used by ui items.
|
|
|
|
|
var lookup = getItemDefinedGroups();
|
|
|
|
|
|
|
|
|
|
// Take the base for the new toolbar, which is basically a toolbar
|
|
|
|
|
// definition without items.
|
|
|
|
|
var toolbar = CKEDITOR.tools.clone( editor.config.toolbarGroups ) || getPrivateToolbarGroups( editor );
|
|
|
|
|
|
|
|
|
|
// Fill the toolbar groups with the available ui items.
|
|
|
|
|
for ( var i = 0; i < toolbar.length; i++ ) {
|
|
|
|
|
var toolbarGroup = toolbar[ i ];
|
|
|
|
|
|
|
|
|
|
// Skip toolbar break.
|
|
|
|
|
if ( toolbarGroup == '/' )
|
|
|
|
|
continue;
|
|
|
|
|
// Handle simply group name item.
|
|
|
|
|
else if ( typeof toolbarGroup == 'string' )
|
|
|
|
|
toolbarGroup = toolbar[ i ] = { name: toolbarGroup };
|
|
|
|
|
|
|
|
|
|
var items, subGroups = toolbarGroup.groups;
|
|
|
|
|
|
|
|
|
|
// Look for items that match sub groups.
|
|
|
|
|
if ( subGroups ) {
|
|
|
|
|
for ( var j = 0, sub; j < subGroups.length; j++ ) {
|
|
|
|
|
sub = subGroups[ j ];
|
|
|
|
|
|
|
|
|
|
// If any ui item is registered for this subgroup.
|
|
|
|
|
items = lookup[ sub ];
|
|
|
|
|
items && fillGroup( toolbarGroup, items );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the main group items as well.
|
|
|
|
|
items = lookup[ toolbarGroup.name ];
|
|
|
|
|
items && fillGroup( toolbarGroup, items );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toolbar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns an object containing all toolbar groups used by ui items.
|
|
|
|
|
function getItemDefinedGroups() {
|
|
|
|
|
var groups = {},
|
|
|
|
|
itemName, item, itemToolbar, group, order;
|
|
|
|
|
|
|
|
|
|
for ( itemName in editor.ui.items ) {
|
|
|
|
|
item = editor.ui.items[ itemName ];
|
|
|
|
|
itemToolbar = item.toolbar || 'others';
|
|
|
|
|
if ( itemToolbar ) {
|
|
|
|
|
// Break the toolbar property into its parts: "group_name[,order]".
|
|
|
|
|
itemToolbar = itemToolbar.split( ',' );
|
|
|
|
|
group = itemToolbar[ 0 ];
|
|
|
|
|
order = parseInt( itemToolbar[ 1 ] || -1, 10 );
|
|
|
|
|
|
|
|
|
|
// Initialize the group, if necessary.
|
|
|
|
|
groups[ group ] || ( groups[ group ] = [] );
|
|
|
|
|
|
|
|
|
|
// Push the data used to build the toolbar later.
|
|
|
|
|
groups[ group ].push( { name: itemName, order: order } );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Put the items in the right order.
|
|
|
|
|
for ( group in groups ) {
|
|
|
|
|
groups[ group ] = groups[ group ].sort( function( a, b ) {
|
|
|
|
|
return a.order == b.order ? 0 :
|
|
|
|
|
b.order < 0 ? -1 :
|
|
|
|
|
a.order < 0 ? 1 :
|
|
|
|
|
a.order < b.order ? -1 :
|
|
|
|
|
1;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return groups;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fillGroup( toolbarGroup, uiItems ) {
|
|
|
|
|
if ( uiItems.length ) {
|
|
|
|
|
if ( toolbarGroup.items )
|
|
|
|
|
toolbarGroup.items.push( editor.ui.create( '-' ) );
|
|
|
|
|
else
|
|
|
|
|
toolbarGroup.items = [];
|
|
|
|
|
|
|
|
|
|
var item, name;
|
|
|
|
|
while ( ( item = uiItems.shift() ) ) {
|
|
|
|
|
name = typeof item == 'string' ? item : item.name;
|
|
|
|
|
|
|
|
|
|
// Ignore items that are configured to be removed.
|
|
|
|
|
if ( !removeButtons || CKEDITOR.tools.indexOf( removeButtons, name ) == -1 ) {
|
|
|
|
|
item = editor.ui.create( name );
|
|
|
|
|
|
|
|
|
|
if ( !item )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ( !editor.addFeature( item ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
toolbarGroup.items.push( item );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function populateToolbarConfig( config ) {
|
|
|
|
|
var toolbar = [],
|
|
|
|
|
i, group, newGroup;
|
|
|
|
|
|
|
|
|
|
for ( i = 0; i < config.length; ++i ) {
|
|
|
|
|
group = config[ i ];
|
|
|
|
|
newGroup = {};
|
|
|
|
|
|
|
|
|
|
if ( group == '/' )
|
|
|
|
|
toolbar.push( group );
|
|
|
|
|
else if ( CKEDITOR.tools.isArray( group ) ) {
|
|
|
|
|
fillGroup( newGroup, CKEDITOR.tools.clone( group ) );
|
|
|
|
|
toolbar.push( newGroup );
|
|
|
|
|
}
|
|
|
|
|
else if ( group.items ) {
|
|
|
|
|
fillGroup( newGroup, CKEDITOR.tools.clone( group.items ) );
|
|
|
|
|
newGroup.name = group.name;
|
|
|
|
|
toolbar.push( newGroup );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toolbar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var toolbar = editor.config.toolbar;
|
|
|
|
|
|
|
|
|
|
// If it is a string, return the relative "toolbar_name" config.
|
|
|
|
|
if ( typeof toolbar == 'string' )
|
|
|
|
|
toolbar = editor.config[ 'toolbar_' + toolbar ];
|
|
|
|
|
|
|
|
|
|
return ( editor.toolbar = toolbar ? populateToolbarConfig( toolbar ) : buildToolbarConfig() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* Adds a toolbar group. See {@link CKEDITOR.config#toolbarGroups} for more details.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* **Note:** This method will not modify toolbar groups set explicitly by
|
|
|
|
|
* {@link CKEDITOR.config#toolbarGroups}. It will only extend the default setting.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* @param {String} name Toolbar group name.
|
|
|
|
|
* @param {Number/String} previous The name of the toolbar group after which this one
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* should be added or `0` if this group should be the first one.
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* @param {String} [subgroupOf] The name of the parent group.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* @member CKEDITOR.ui
|
|
|
|
|
*/
|
|
|
|
|
CKEDITOR.ui.prototype.addToolbarGroup = function( name, previous, subgroupOf ) {
|
|
|
|
|
// The toolbarGroups from the privates is the one we gonna use for automatic toolbar creation.
|
|
|
|
|
var toolbarGroups = getPrivateToolbarGroups( this.editor ),
|
|
|
|
|
atStart = previous === 0,
|
|
|
|
|
newGroup = { name: name };
|
|
|
|
|
|
|
|
|
|
if ( subgroupOf ) {
|
|
|
|
|
// Transform the subgroupOf name in the real subgroup object.
|
|
|
|
|
subgroupOf = CKEDITOR.tools.search( toolbarGroups, function( group ) {
|
|
|
|
|
return group.name == subgroupOf;
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
if ( subgroupOf ) {
|
|
|
|
|
!subgroupOf.groups && ( subgroupOf.groups = [] ) ;
|
|
|
|
|
|
|
|
|
|
if ( previous ) {
|
|
|
|
|
// Search the "previous" item and add the new one after it.
|
|
|
|
|
previous = CKEDITOR.tools.indexOf( subgroupOf.groups, previous );
|
|
|
|
|
if ( previous >= 0 ) {
|
|
|
|
|
subgroupOf.groups.splice( previous + 1, 0, name );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no previous found.
|
|
|
|
|
|
|
|
|
|
if ( atStart )
|
|
|
|
|
subgroupOf.groups.splice( 0, 0, name );
|
|
|
|
|
else
|
|
|
|
|
subgroupOf.groups.push( name );
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
// Ignore "previous" if subgroupOf has not been found.
|
|
|
|
|
previous = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( previous ) {
|
|
|
|
|
// Transform the "previous" name into its index.
|
|
|
|
|
previous = CKEDITOR.tools.indexOf( toolbarGroups, function( group ) {
|
|
|
|
|
return group.name == previous;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( atStart )
|
|
|
|
|
toolbarGroups.splice( 0, 0, name );
|
|
|
|
|
else if ( typeof previous == 'number' )
|
|
|
|
|
toolbarGroups.splice( previous + 1, 0, newGroup );
|
|
|
|
|
else
|
|
|
|
|
toolbarGroups.push( name );
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getPrivateToolbarGroups( editor ) {
|
|
|
|
|
return editor._.toolbarGroups || ( editor._.toolbarGroups = [
|
2018-06-17 16:07:19 +00:00
|
|
|
|
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
|
2014-04-11 20:07:18 +00:00
|
|
|
|
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
|
|
|
|
|
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
|
|
|
|
|
{ name: 'forms' },
|
|
|
|
|
'/',
|
|
|
|
|
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
|
|
|
|
|
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
|
|
|
|
|
{ name: 'links' },
|
|
|
|
|
{ name: 'insert' },
|
|
|
|
|
'/',
|
|
|
|
|
{ name: 'styles' },
|
|
|
|
|
{ name: 'colors' },
|
|
|
|
|
{ name: 'tools' },
|
|
|
|
|
{ name: 'others' },
|
|
|
|
|
{ name: 'about' }
|
|
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
} )();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Separator UI element.
|
|
|
|
|
*
|
|
|
|
|
* @readonly
|
|
|
|
|
* @property {String} [='separator']
|
|
|
|
|
* @member CKEDITOR
|
|
|
|
|
*/
|
|
|
|
|
CKEDITOR.UI_SEPARATOR = 'separator';
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* The part of the user interface where the toolbar will be rendered. For the default
|
|
|
|
|
* editor implementation, the recommended options are `'top'` and `'bottom'`.
|
|
|
|
|
*
|
|
|
|
|
* Please note that this option is only applicable to [classic](#!/guide/dev_framed)
|
|
|
|
|
* (`iframe`-based) editor. In case of [inline](#!/guide/dev_inline) editor the toolbar
|
|
|
|
|
* position is set dynamically depending on the position of the editable element on the screen.
|
|
|
|
|
*
|
|
|
|
|
* Read more in the [documentation](#!/guide/dev_toolbarlocation)
|
|
|
|
|
* and see the [SDK sample](https://sdk.ckeditor.com/samples/toolbarlocation.html).
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
|
|
|
|
* config.toolbarLocation = 'bottom';
|
|
|
|
|
*
|
|
|
|
|
* @cfg
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
CKEDITOR.config.toolbarLocation = 'top';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The toolbox (alias toolbar) definition. It is a toolbar name or an array of
|
|
|
|
|
* toolbars (strips), each one being also an array, containing a list of UI items.
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* If set to `null`, the toolbar will be generated automatically using all available buttons
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* and {@link #toolbarGroups} as a toolbar groups layout.
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* In CKEditor 4.5+ you can generate your toolbar customization code by using the [visual
|
|
|
|
|
* toolbar configurator](https://docs.ckeditor.com/ckeditor4/docs/#!/guide/dev_toolbar).
|
|
|
|
|
*
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* // Defines a toolbar with only one strip containing the "Source" button, a
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* // separator, and the "Bold" and "Italic" buttons.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* config.toolbar = [
|
|
|
|
|
* [ 'Source', '-', 'Bold', 'Italic' ]
|
|
|
|
|
* ];
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* // Similar to the example above, defines a "Basic" toolbar with only one strip containing three buttons.
|
|
|
|
|
* // Note that this setting is composed by "toolbar_" added to the toolbar name, which in this case is called "Basic".
|
|
|
|
|
* // This second part of the setting name can be anything. You must use this name in the CKEDITOR.config.toolbar setting
|
|
|
|
|
* // in order to instruct the editor which `toolbar_(name)` setting should be used.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* config.toolbar_Basic = [
|
|
|
|
|
* [ 'Source', '-', 'Bold', 'Italic' ]
|
|
|
|
|
* ];
|
|
|
|
|
* // Load toolbar_Name where Name = Basic.
|
|
|
|
|
* config.toolbar = 'Basic';
|
|
|
|
|
*
|
|
|
|
|
* @cfg {Array/String} [toolbar=null]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The toolbar groups definition.
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* If the toolbar layout is not explicitly defined by the {@link #toolbar} setting, then
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* this setting is used to group all defined buttons (see {@link CKEDITOR.ui#addButton}).
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* Buttons are associated with toolbar groups by the `toolbar` property in their definition objects.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* New groups may be dynamically added during the editor and plugin initialization by
|
|
|
|
|
* {@link CKEDITOR.ui#addToolbarGroup}. This is only possible if the default setting was used.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
|
|
|
|
* // Default setting.
|
|
|
|
|
* config.toolbarGroups = [
|
|
|
|
|
* { name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
|
|
|
|
|
* { name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
|
|
|
|
|
* { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
|
|
|
|
|
* { name: 'forms' },
|
|
|
|
|
* '/',
|
|
|
|
|
* { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
|
|
|
|
|
* { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
|
|
|
|
|
* { name: 'links' },
|
|
|
|
|
* { name: 'insert' },
|
|
|
|
|
* '/',
|
|
|
|
|
* { name: 'styles' },
|
|
|
|
|
* { name: 'colors' },
|
|
|
|
|
* { name: 'tools' },
|
|
|
|
|
* { name: 'others' },
|
|
|
|
|
* { name: 'about' }
|
|
|
|
|
* ];
|
|
|
|
|
*
|
|
|
|
|
* @cfg {Array} [toolbarGroups=see example]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* Whether the toolbar can be collapsed by the user. If disabled, the Collapse Toolbar
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* button will not be displayed.
|
|
|
|
|
*
|
|
|
|
|
* config.toolbarCanCollapse = true;
|
|
|
|
|
*
|
|
|
|
|
* @cfg {Boolean} [toolbarCanCollapse=false]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Whether the toolbar must start expanded when the editor is loaded.
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* Setting this option to `false` will affect the toolbar only when
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* {@link #toolbarCanCollapse} is set to `true`:
|
|
|
|
|
*
|
|
|
|
|
* config.toolbarCanCollapse = true;
|
|
|
|
|
* config.toolbarStartupExpanded = false;
|
|
|
|
|
*
|
|
|
|
|
* @cfg {Boolean} [toolbarStartupExpanded=true]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* When enabled, causes the *Arrow* keys navigation to cycle within the current
|
|
|
|
|
* toolbar group. Otherwise the *Arrow* keys will move through all items available in
|
|
|
|
|
* the toolbar. The *Tab* key will still be used to quickly jump among the
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* toolbar groups.
|
|
|
|
|
*
|
|
|
|
|
* config.toolbarGroupCycling = false;
|
|
|
|
|
*
|
|
|
|
|
* @since 3.6
|
|
|
|
|
* @cfg {Boolean} [toolbarGroupCycling=true]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* List of toolbar button names that must not be rendered. This will also work
|
|
|
|
|
* for non-button toolbar items, like the Font drop-down list.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
|
|
|
|
* config.removeButtons = 'Underline,JustifyCenter';
|
|
|
|
|
*
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* This configuration option should not be overused. The recommended way is to use the
|
|
|
|
|
* {@link CKEDITOR.config#removePlugins} setting to remove features from the editor
|
|
|
|
|
* or even better, [create a custom editor build](https://ckeditor.com/cke4/builder) with
|
|
|
|
|
* just the features that you will use.
|
|
|
|
|
* In some cases though, a single plugin may define a set of toolbar buttons and
|
|
|
|
|
* `removeButtons` may be useful when just a few of them are to be removed.
|
2014-04-11 20:07:18 +00:00
|
|
|
|
*
|
|
|
|
|
* @cfg {String} [removeButtons]
|
|
|
|
|
* @member CKEDITOR.config
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-17 16:07:19 +00:00
|
|
|
|
* The toolbar definition used by the editor. It is created from the
|
|
|
|
|
* {@link CKEDITOR.config#toolbar} option if it is set or automatically
|
2014-04-11 20:07:18 +00:00
|
|
|
|
* based on {@link CKEDITOR.config#toolbarGroups}.
|
|
|
|
|
*
|
|
|
|
|
* @readonly
|
|
|
|
|
* @property {Object} toolbar
|
|
|
|
|
* @member CKEDITOR.editor
|
|
|
|
|
*/
|