1367 lines
35 KiB
JavaScript
Executable File
1367 lines
35 KiB
JavaScript
Executable File
/* global ToolbarConfigurator, alert */
|
|
|
|
'use strict';
|
|
|
|
( function() {
|
|
var AbstractToolbarModifier = ToolbarConfigurator.AbstractToolbarModifier;
|
|
|
|
/**
|
|
* @class ToolbarConfigurator.ToolbarModifier
|
|
* @param {String} editorId An id of modified editor
|
|
* @param {Object} cfg
|
|
* @extends AbstractToolbarModifier
|
|
* @constructor
|
|
*/
|
|
function ToolbarModifier( editorId, cfg ) {
|
|
AbstractToolbarModifier.call( this, editorId, cfg );
|
|
|
|
this.removedButtons = null;
|
|
this.originalConfig = null;
|
|
this.actualConfig = null;
|
|
this.emptyVisible = false;
|
|
|
|
// edit, paste, config
|
|
this.state = 'edit';
|
|
|
|
this.toolbarButtons = [
|
|
{
|
|
text: {
|
|
active: 'Hide empty toolbar groups',
|
|
inactive: 'Show empty toolbar groups'
|
|
},
|
|
group: 'edit',
|
|
position: 'left',
|
|
cssClass: 'button-a-soft',
|
|
clickCallback: function( button, buttonDefinition ) {
|
|
var className = 'button-a-background';
|
|
|
|
button[ button.hasClass( className ) ? 'removeClass' : 'addClass' ]( className );
|
|
|
|
this._toggleVisibilityEmptyElements();
|
|
|
|
if ( this.emptyVisible ) {
|
|
button.setText( buttonDefinition.text.active );
|
|
} else {
|
|
button.setText( buttonDefinition.text.inactive );
|
|
}
|
|
}
|
|
},
|
|
{
|
|
text: 'Add row separator',
|
|
group: 'edit',
|
|
position: 'left',
|
|
cssClass: 'button-a-soft',
|
|
clickCallback: function() {
|
|
this._addSeparator();
|
|
}
|
|
},
|
|
/*{
|
|
text: 'Paste config',
|
|
group: 'edit',
|
|
position: 'left',
|
|
clickCallback: function() {
|
|
this.state = 'paste';
|
|
|
|
this.modifyContainer.addClass( 'hidden' );
|
|
this.configContainer.removeClass( 'hidden' );
|
|
this.configContainer.setHtml( '<textarea></textarea>' );
|
|
this.showToolbarBtnsByGroupName( 'config' );
|
|
}
|
|
},*/
|
|
{
|
|
text: 'Select config',
|
|
group: 'config',
|
|
position: 'left',
|
|
cssClass: 'button-a-soft',
|
|
clickCallback: function() {
|
|
this.configContainer.findOne( 'textarea' ).$.select();
|
|
}
|
|
},
|
|
{
|
|
text: 'Back to configurator',
|
|
group: 'config',
|
|
position: 'right',
|
|
cssClass: 'button-a-background',
|
|
clickCallback: function() {
|
|
if ( this.state === 'paste' ) {
|
|
var cfg = this.configContainer.findOne( 'textarea' ).getValue();
|
|
cfg = ToolbarModifier.evaluateToolbarGroupsConfig( cfg );
|
|
|
|
if ( cfg ) {
|
|
this.setConfig( cfg );
|
|
} else {
|
|
alert( 'Your pasted config is wrong.' );
|
|
}
|
|
}
|
|
|
|
this.state = 'edit';
|
|
this._showConfigurationTool();
|
|
this.showToolbarBtnsByGroupName( this.state );
|
|
}
|
|
},
|
|
{
|
|
text: 'Get toolbar <span class="highlight">config</span>',
|
|
group: 'edit',
|
|
position: 'right',
|
|
cssClass: 'button-a-background icon-pos-left icon-download',
|
|
clickCallback: function() {
|
|
this.state = 'config';
|
|
this._showConfig();
|
|
this.showToolbarBtnsByGroupName( this.state );
|
|
}
|
|
}
|
|
];
|
|
|
|
this.cachedActiveElement = null;
|
|
}
|
|
|
|
// Expose the class.
|
|
ToolbarConfigurator.ToolbarModifier = ToolbarModifier;
|
|
|
|
ToolbarModifier.prototype = Object.create( ToolbarConfigurator.AbstractToolbarModifier.prototype );
|
|
|
|
/**
|
|
* @returns {Object}
|
|
*/
|
|
ToolbarModifier.prototype.getActualConfig = function() {
|
|
var copy = AbstractToolbarModifier.prototype.getActualConfig.call( this );
|
|
|
|
if ( copy.toolbarGroups ) {
|
|
|
|
var max = copy.toolbarGroups.length;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentGroup = copy.toolbarGroups[ i ];
|
|
|
|
copy.toolbarGroups[ i ] = ToolbarModifier.parseGroupToConfigValue( currentGroup );
|
|
}
|
|
|
|
}
|
|
|
|
return copy;
|
|
};
|
|
|
|
/**
|
|
* @param {Function} callback
|
|
* @param {String} [config]
|
|
* @param {Boolean} [forceKeepRemoveButtons=false]
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._onInit = function( callback, config, forceKeepRemoveButtons ) {
|
|
forceKeepRemoveButtons = ( forceKeepRemoveButtons === true );
|
|
AbstractToolbarModifier.prototype._onInit.call( this, undefined, config );
|
|
|
|
this.removedButtons = [];
|
|
|
|
if ( forceKeepRemoveButtons ) {
|
|
if ( this.actualConfig.removeButtons ) {
|
|
this.removedButtons = this.actualConfig.removeButtons.split( ',' );
|
|
} else {
|
|
this.removedButtons = [];
|
|
}
|
|
} else {
|
|
if ( !( 'removeButtons' in this.originalConfig ) ) {
|
|
this.originalConfig.removeButtons = '';
|
|
this.removedButtons = [];
|
|
} else {
|
|
this.removedButtons = this.originalConfig.removeButtons ? this.originalConfig.removeButtons.split( ',' ) : [];
|
|
}
|
|
}
|
|
|
|
if ( !this.actualConfig.toolbarGroups )
|
|
this.actualConfig.toolbarGroups = this.fullToolbarEditor.getFullToolbarGroupsConfig();
|
|
|
|
this._fixGroups( this.actualConfig );
|
|
this._calculateTotalBtns();
|
|
|
|
this._createModifier();
|
|
this._refreshMoveBtnsAvalibility();
|
|
this._refreshBtnTabIndexes();
|
|
|
|
if ( typeof callback === 'function' )
|
|
callback( this.mainContainer );
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._showConfigurationTool = function() {
|
|
this.configContainer.addClass( 'hidden' );
|
|
this.modifyContainer.removeClass( 'hidden' );
|
|
};
|
|
|
|
/**
|
|
* Show configuration file in tool
|
|
*
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._showConfig = function() {
|
|
var that = this,
|
|
actualConfig = this.getActualConfig(),
|
|
cfg = {};
|
|
if ( actualConfig.toolbarGroups ) {
|
|
cfg.toolbarGroups = actualConfig.toolbarGroups;
|
|
|
|
var groups = prepareGroups( actualConfig.toolbarGroups, this.cfg.trimEmptyGroups );
|
|
|
|
cfg.toolbarGroups = '\n\t\t' + groups.join( ',\n\t\t' );
|
|
}
|
|
|
|
function prepareGroups( toolbarGroups, trimEmptyGroups ) {
|
|
var groups = [],
|
|
max = toolbarGroups.length;
|
|
|
|
for ( var i = 0; i < max; i++ ) {
|
|
var group = toolbarGroups[ i ];
|
|
|
|
if ( group === '/' ) {
|
|
groups.push( '\'/\'' );
|
|
continue;
|
|
}
|
|
|
|
if ( trimEmptyGroups ) {
|
|
var max2 = group.groups.length;
|
|
while ( max2-- ) {
|
|
var subgroup = group.groups[ max2 ];
|
|
|
|
if ( ToolbarModifier.getTotalSubGroupButtonsNumber( subgroup, that.fullToolbarEditor ) === 0 ) {
|
|
group.groups.splice( max2, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !( trimEmptyGroups && group.groups.length === 0 ) ) {
|
|
groups.push( AbstractToolbarModifier.stringifyJSONintoOneLine( group, {
|
|
addSpaces: true,
|
|
noQuotesOnKey: true,
|
|
singleQuotes: true
|
|
} ) );
|
|
}
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
if ( actualConfig.removeButtons ) {
|
|
cfg.removeButtons = actualConfig.removeButtons;
|
|
}
|
|
|
|
var content = [
|
|
'<textarea class="configCode" readonly>',
|
|
'CKEDITOR.editorConfig = function( config ) {\n',
|
|
( cfg.toolbarGroups ? '\tconfig.toolbarGroups = [' + cfg.toolbarGroups + '\n\t];' : '' ),
|
|
( cfg.removeButtons ? '\n\n' : '' ),
|
|
( cfg.removeButtons ? '\tconfig.removeButtons = \'' + cfg.removeButtons + '\';' : '' ),
|
|
'\n};',
|
|
'</textarea>'
|
|
].join( '' );
|
|
|
|
|
|
|
|
this.modifyContainer.addClass( 'hidden' );
|
|
this.configContainer.removeClass( 'hidden' );
|
|
|
|
this.configContainer.setHtml( content );
|
|
};
|
|
|
|
/**
|
|
* Toggle empty groups and subgroups visibility.
|
|
*
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._toggleVisibilityEmptyElements = function() {
|
|
if ( this.modifyContainer.hasClass( 'empty-visible' ) ) {
|
|
this.modifyContainer.removeClass( 'empty-visible' );
|
|
this.emptyVisible = false;
|
|
} else {
|
|
this.modifyContainer.addClass( 'empty-visible' );
|
|
this.emptyVisible = true;
|
|
}
|
|
|
|
this._refreshMoveBtnsAvalibility();
|
|
};
|
|
|
|
/**
|
|
* Creates HTML main container of modifier.
|
|
*
|
|
* @returns {CKEDITOR.dom.element}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._createModifier = function() {
|
|
var that = this;
|
|
|
|
AbstractToolbarModifier.prototype._createModifier.call( this );
|
|
|
|
this.modifyContainer.setHtml( this._toolbarConfigToListString() );
|
|
|
|
var groupLi = this.modifyContainer.find( 'li[data-type="group"]' );
|
|
|
|
this.modifyContainer.on( 'mouseleave', function() {
|
|
this._dehighlightActiveToolGroup();
|
|
}, this );
|
|
|
|
var max = groupLi.count();
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
groupLi.getItem( i ).on( 'mouseenter', onGroupHover );
|
|
}
|
|
|
|
function onGroupHover() {
|
|
that._highlightGroup( this.data( 'name' ) );
|
|
}
|
|
|
|
CKEDITOR.document.on( 'keypress', function( e ) {
|
|
var nativeEvent = e.data.$,
|
|
keyCode = nativeEvent.keyCode,
|
|
spaceOrEnter = ( keyCode === 32 || keyCode === 13 ),
|
|
active = new CKEDITOR.dom.element( CKEDITOR.document.$.activeElement );
|
|
|
|
var mainContainer = active.getAscendant( function( node ) {
|
|
return node.$ === that.mainContainer.$;
|
|
} );
|
|
|
|
if ( !mainContainer || !spaceOrEnter ) {
|
|
return;
|
|
}
|
|
|
|
if ( active.data( 'type' ) === 'button' ) {
|
|
active.findOne( 'input' ).$.click();
|
|
}
|
|
} );
|
|
|
|
this.modifyContainer.on( 'click', function( e ) {
|
|
var origEvent = e.data.$,
|
|
target = new CKEDITOR.dom.element( ( origEvent.target || origEvent.srcElement ) ),
|
|
relativeGroupOrSeparatorLi = ToolbarModifier.getGroupOrSeparatorLiAncestor( target );
|
|
|
|
if ( !relativeGroupOrSeparatorLi ) {
|
|
return;
|
|
}
|
|
|
|
that.cachedActiveElement = document.activeElement;
|
|
|
|
// checkbox clicked
|
|
if ( target.$ instanceof HTMLInputElement )
|
|
that._handleCheckboxClicked( target );
|
|
|
|
// link clicked
|
|
else if ( target.$ instanceof HTMLButtonElement ) {
|
|
if ( origEvent.preventDefault )
|
|
origEvent.preventDefault();
|
|
else
|
|
origEvent.returnValue = false;
|
|
|
|
var result = that._handleAnchorClicked( target.$ );
|
|
|
|
if ( result && result.action == 'remove' )
|
|
return;
|
|
|
|
}
|
|
|
|
var elementType = relativeGroupOrSeparatorLi.data( 'type' ),
|
|
elementName = relativeGroupOrSeparatorLi.data( 'name' );
|
|
|
|
that._setActiveElement( elementType, elementName );
|
|
|
|
if ( that.cachedActiveElement )
|
|
that.cachedActiveElement.focus();
|
|
} );
|
|
|
|
if ( !this.toolbarContainer ) {
|
|
this._createToolbar();
|
|
this.toolbarContainer.insertBefore( this.mainContainer.getChildren().getItem( 0 ) );
|
|
}
|
|
|
|
this.showToolbarBtnsByGroupName( 'edit' );
|
|
|
|
if ( !this.configContainer ) {
|
|
this.configContainer = new CKEDITOR.dom.element( 'div' );
|
|
this.configContainer.addClass( 'configContainer' );
|
|
this.configContainer.addClass( 'hidden' );
|
|
|
|
this.mainContainer.append( this.configContainer );
|
|
}
|
|
|
|
return this.mainContainer;
|
|
};
|
|
|
|
/**
|
|
* Show toolbar buttons related to group name provided in argument
|
|
* and hide other buttons
|
|
* Please note: this method works on toolbar in tool, which is located
|
|
* on top of the tool
|
|
*
|
|
* @param {String} groupName
|
|
*/
|
|
ToolbarModifier.prototype.showToolbarBtnsByGroupName = function( groupName ) {
|
|
if ( !this.toolbarContainer ) {
|
|
return;
|
|
}
|
|
|
|
var allButtons = this.toolbarContainer.find( 'button' );
|
|
|
|
var max = allButtons.count();
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentBtn = allButtons.getItem( i );
|
|
|
|
if ( currentBtn.data( 'group' ) == groupName )
|
|
currentBtn.removeClass( 'hidden' );
|
|
else
|
|
currentBtn.addClass( 'hidden' );
|
|
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parse group "model" to configuration value
|
|
*
|
|
* @param {Object} group
|
|
* @returns {Object}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.parseGroupToConfigValue = function( group ) {
|
|
if ( group.type == 'separator' ) {
|
|
return '/';
|
|
}
|
|
|
|
var groups = group.groups,
|
|
max = groups.length;
|
|
|
|
delete group.totalBtns;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
groups[ i ] = groups[ i ].name;
|
|
}
|
|
|
|
return group;
|
|
};
|
|
|
|
/**
|
|
* Find closest Li ancestor in DOM tree which is group or separator element
|
|
*
|
|
* @param {CKEDITOR.dom.element} element
|
|
* @returns {CKEDITOR.dom.element}
|
|
*/
|
|
ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) {
|
|
if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' )
|
|
return element;
|
|
else {
|
|
return ToolbarModifier.getFirstAncestor( element, function( ancestor ) {
|
|
var type = ancestor.data( 'type' );
|
|
|
|
return ( type == 'group' || type == 'separator' );
|
|
} );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set active element in tool by provided type and name.
|
|
*
|
|
* @param {String} type
|
|
* @param {String} name
|
|
*/
|
|
ToolbarModifier.prototype._setActiveElement = function( type, name ) {
|
|
// clear current active element
|
|
if ( this.currentActive )
|
|
this.currentActive.elem.removeClass( 'active' );
|
|
|
|
if ( type === null ) {
|
|
this._dehighlightActiveToolGroup();
|
|
this.currentActive = null;
|
|
return;
|
|
}
|
|
|
|
var liElem = this.mainContainer.findOne( 'ul[data-type=table-body] li[data-type="' + type + '"][data-name="' + name + '"]' );
|
|
|
|
liElem.addClass( 'active' );
|
|
|
|
// setup model
|
|
this.currentActive = {
|
|
type: type,
|
|
name: name,
|
|
elem: liElem
|
|
};
|
|
|
|
// highlight group in toolbar
|
|
if ( type == 'group' )
|
|
this._highlightGroup( name );
|
|
|
|
if ( type == 'separator' )
|
|
this._dehighlightActiveToolGroup();
|
|
};
|
|
|
|
/**
|
|
* @returns {CKEDITOR.dom.element|null}
|
|
*/
|
|
ToolbarModifier.prototype.getActiveToolGroup = function() {
|
|
if ( this.editorInstance.container )
|
|
return this.editorInstance.container.findOne( '.cke_toolgroup.active, .cke_toolbar.active' );
|
|
else
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._dehighlightActiveToolGroup = function() {
|
|
var currentActive = this.getActiveToolGroup();
|
|
|
|
if ( currentActive )
|
|
currentActive.removeClass( 'active' );
|
|
|
|
// @see ToolbarModifier.prototype._highlightGroup.
|
|
if ( this.editorInstance.container ) {
|
|
this.editorInstance.container.removeClass( 'some-toolbar-active' );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Highlight group by its name, and dehighlight current group.
|
|
*
|
|
* @param {String} name
|
|
*/
|
|
ToolbarModifier.prototype._highlightGroup = function( name ) {
|
|
if ( !this.editorInstance.container )
|
|
return;
|
|
|
|
var foundBtnName = this.getFirstEnabledButtonInGroup( name ),
|
|
foundBtn = this.editorInstance.container.findOne( '.cke_button__' + foundBtnName + ', .cke_combo__' + foundBtnName );
|
|
|
|
this._dehighlightActiveToolGroup();
|
|
|
|
// Helpful to dim other toolbar groups if one is highlighted.
|
|
if ( this.editorInstance.container ) {
|
|
this.editorInstance.container.addClass( 'some-toolbar-active' );
|
|
}
|
|
|
|
if ( foundBtn ) {
|
|
var btnToolbar = ToolbarModifier.getFirstAncestor( foundBtn, function( ancestor ) {
|
|
return ancestor.hasClass( 'cke_toolbar' );
|
|
} );
|
|
|
|
if ( btnToolbar )
|
|
btnToolbar.addClass( 'active' );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {String} groupName
|
|
* @return {String|null}
|
|
*/
|
|
ToolbarModifier.prototype.getFirstEnabledButtonInGroup = function( groupName ) {
|
|
var groups = this.actualConfig.toolbarGroups,
|
|
groupIndex = this.getGroupIndex( groupName ),
|
|
group = groups[ groupIndex ];
|
|
|
|
if ( groupIndex === -1 ) {
|
|
return null;
|
|
}
|
|
|
|
var max = group.groups ? group.groups.length : 0;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currSubgroupName = group.groups[ i ].name,
|
|
firstEnabled = this.getFirstEnabledButtonInSubgroup( currSubgroupName );
|
|
|
|
if ( firstEnabled )
|
|
return firstEnabled;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {String} subgroupName
|
|
* @returns {String|null}
|
|
*/
|
|
ToolbarModifier.prototype.getFirstEnabledButtonInSubgroup = function( subgroupName ) {
|
|
var subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ subgroupName ];
|
|
|
|
var max = subgroupBtns ? subgroupBtns.length : 0;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currBtnName = subgroupBtns[ i ].name;
|
|
if ( !this.isButtonRemoved( currBtnName ) )
|
|
return currBtnName;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Sets up parameters and call adequate action.
|
|
*
|
|
* @param {CKEDITOR.dom.element} checkbox
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._handleCheckboxClicked = function( checkbox ) {
|
|
var closestLi = checkbox.getAscendant( 'li' ),
|
|
elementName = closestLi.data( 'name' ),
|
|
aboutToAddToRemoved = !checkbox.$.checked;
|
|
|
|
if ( aboutToAddToRemoved )
|
|
this._addButtonToRemoved( elementName );
|
|
else
|
|
this._removeButtonFromRemoved( elementName );
|
|
};
|
|
|
|
/**
|
|
* Sets up parameters and call adequate action.
|
|
*
|
|
* @param {HTMLAnchorElement} anchor
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._handleAnchorClicked = function( anchor ) {
|
|
var anchorDOM = new CKEDITOR.dom.element( anchor ),
|
|
relativeLi = anchorDOM.getAscendant( 'li' ),
|
|
relativeUl = relativeLi.getAscendant( 'ul' ),
|
|
elementType = relativeLi.data( 'type' ),
|
|
elementName = relativeLi.data( 'name' ),
|
|
direction = anchorDOM.data( 'direction' ),
|
|
nearestLi = ( direction === 'up' ? relativeLi.getPrevious() : relativeLi.getNext() ),
|
|
groupName,
|
|
subgroupName,
|
|
newIndex;
|
|
|
|
// nothing to do
|
|
if ( anchorDOM.hasClass( 'disabled' ) )
|
|
return null;
|
|
|
|
// remove separator and nothing else
|
|
if ( anchorDOM.hasClass( 'remove' ) ) {
|
|
relativeLi.remove();
|
|
this._removeSeparator( relativeLi.data( 'name' ) );
|
|
this._setActiveElement( null );
|
|
return { action: 'remove' };
|
|
}
|
|
|
|
if ( !anchorDOM.hasClass( 'move' ) || !nearestLi )
|
|
return { action: null };
|
|
|
|
// move group or separator
|
|
if ( elementType === 'group' || elementType === 'separator' ) {
|
|
groupName = elementName;
|
|
newIndex = this._moveGroup( direction, groupName );
|
|
}
|
|
|
|
// move subgroup
|
|
if ( elementType === 'subgroup' ) {
|
|
subgroupName = elementName;
|
|
groupName = relativeLi.getAscendant( 'li' ).data( 'name' );
|
|
newIndex = this._moveSubgroup( direction, groupName, subgroupName );
|
|
}
|
|
|
|
// Visual effect
|
|
if ( direction === 'up' )
|
|
relativeLi.insertBefore( relativeUl.getChild( newIndex ) );
|
|
|
|
if ( direction === 'down' )
|
|
relativeLi.insertAfter( relativeUl.getChild( newIndex ) );
|
|
|
|
// Should know whether there is next li element after modifications.
|
|
var nextLi = relativeLi;
|
|
|
|
// We are looking for next li element in list (to check whether current one is the last one)
|
|
var found;
|
|
while ( nextLi = ( direction === 'up' ? nextLi.getPrevious() : nextLi.getNext() ) ) {
|
|
if ( !this.emptyVisible && nextLi.hasClass( 'empty' ) ) {
|
|
continue;
|
|
}
|
|
|
|
found = nextLi;
|
|
break;
|
|
}
|
|
|
|
// If not found, it means that we reached end.
|
|
if ( !found ) {
|
|
var selector = ( '[data-direction="' + ( direction === 'up' ? 'down' : 'up' ) + '"]' );
|
|
|
|
// Shifting direction.
|
|
this.cachedActiveElement = anchorDOM.getParent().findOne( selector );
|
|
}
|
|
|
|
this._refreshMoveBtnsAvalibility();
|
|
this._refreshBtnTabIndexes();
|
|
|
|
return {
|
|
action: 'move'
|
|
};
|
|
};
|
|
|
|
/**
|
|
* First element can not be moved up, and last element can not be moved down,
|
|
* so they are disabled.
|
|
*/
|
|
ToolbarModifier.prototype._refreshMoveBtnsAvalibility = function() {
|
|
var that = this,
|
|
disabledBtns = this.mainContainer.find( 'ul[data-type=table-body] li > p > span > button.move.disabled' );
|
|
|
|
// enabling all disabled buttons
|
|
var max = disabledBtns.count();
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentBtn = disabledBtns.getItem( i );
|
|
currentBtn.removeClass( 'disabled' );
|
|
}
|
|
|
|
|
|
function disableElementsInLists( ulList ) {
|
|
var max = ulList.count();
|
|
for ( i = 0; i < max; i += 1 ) {
|
|
that._disableElementsInList( ulList.getItem( i ) );
|
|
}
|
|
}
|
|
|
|
// Disable buttons in toolbars.
|
|
disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body]' ) );
|
|
|
|
// Disable buttons in toolbar groups.
|
|
disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body] > li > ul' ) );
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._refreshBtnTabIndexes = function() {
|
|
var tabindexed = this.mainContainer.find( '[data-tab="true"]' );
|
|
|
|
var max = tabindexed.count();
|
|
for ( var i = 0; i < max; i++ ) {
|
|
var item = tabindexed.getItem( i ),
|
|
disabled = item.hasClass( 'disabled' );
|
|
|
|
item.setAttribute( 'tabindex', disabled ? -1 : i );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Disable buttons to move elements up and down which should be disabled.
|
|
*
|
|
* @param {CKEDITOR.dom.element} ul
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._disableElementsInList = function( ul ) {
|
|
var liList = ul.getChildren();
|
|
|
|
if ( !liList.count() )
|
|
return;
|
|
|
|
var firstDisabled, lastDisabled;
|
|
if ( this.emptyVisible ) {
|
|
firstDisabled = ul.getFirst();
|
|
lastDisabled = ul.getLast();
|
|
} else {
|
|
firstDisabled = ul.getFirst( isNotEmptyChecker );
|
|
lastDisabled = ul.getLast( isNotEmptyChecker );
|
|
}
|
|
|
|
function isNotEmptyChecker( element ) {
|
|
return !element.hasClass( 'empty' );
|
|
}
|
|
|
|
if ( firstDisabled )
|
|
var firstDisabledBtn = firstDisabled.findOne( 'p button[data-direction="up"]' );
|
|
|
|
if ( lastDisabled )
|
|
var lastDisabledBtn = lastDisabled.findOne( 'p button[data-direction="down"]' );
|
|
|
|
if ( firstDisabledBtn ) {
|
|
firstDisabledBtn.addClass( 'disabled' );
|
|
firstDisabledBtn.setAttribute( 'tabindex', '-1' );
|
|
}
|
|
|
|
if ( lastDisabledBtn ) {
|
|
lastDisabledBtn.addClass( 'disabled' );
|
|
lastDisabledBtn.setAttribute( 'tabindex', '-1' );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets group index in actual config toolbarGroups
|
|
*
|
|
* @param {String} name
|
|
* @returns {Number}
|
|
*/
|
|
ToolbarModifier.prototype.getGroupIndex = function( name ) {
|
|
var groups = this.actualConfig.toolbarGroups;
|
|
|
|
var max = groups.length;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
if ( groups[ i ].name === name )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Handle adding separator.
|
|
*
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._addSeparator = function() {
|
|
var separatorIndex = this._determineSeparatorToAddIndex(),
|
|
separator = ToolbarModifier.createSeparatorLiteral(),
|
|
domSeparator = CKEDITOR.dom.element.createFromHtml( ToolbarModifier.getToolbarSeparatorString( separator ) );
|
|
|
|
this.actualConfig.toolbarGroups.splice( separatorIndex, 0, separator );
|
|
|
|
domSeparator.insertBefore( this.modifyContainer.findOne( 'ul[data-type=table-body]' ).getChild( separatorIndex ) );
|
|
|
|
this._setActiveElement( 'separator', separator.name );
|
|
this._refreshMoveBtnsAvalibility();
|
|
this._refreshBtnTabIndexes();
|
|
this._refreshEditor();
|
|
};
|
|
|
|
/**
|
|
* Handle removing separator.
|
|
*
|
|
* @param {String} name
|
|
*/
|
|
ToolbarModifier.prototype._removeSeparator = function( name ) {
|
|
var separatorIndex = CKEDITOR.tools.indexOf( this.actualConfig.toolbarGroups, function( group ) {
|
|
return group.type == 'separator' && group.name == name;
|
|
} );
|
|
|
|
this.actualConfig.toolbarGroups.splice( separatorIndex, 1 );
|
|
|
|
this._refreshMoveBtnsAvalibility();
|
|
this._refreshBtnTabIndexes();
|
|
this._refreshEditor();
|
|
};
|
|
|
|
/**
|
|
* Determine index where separator should be added, based on currently selected element.
|
|
*
|
|
* @returns {Number}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._determineSeparatorToAddIndex = function() {
|
|
if ( !this.currentActive )
|
|
return 0;
|
|
|
|
var groupLi;
|
|
if ( this.currentActive.elem.data( 'type' ) == 'group' || this.currentActive.elem.data( 'type' ) == 'separator' )
|
|
groupLi = this.currentActive.elem;
|
|
else
|
|
groupLi = this.currentActive.elem.getAscendant( 'li' );
|
|
|
|
return groupLi.getIndex();
|
|
};
|
|
|
|
/**
|
|
* @param {Array} elementsArray
|
|
* @param {Number} elementIndex
|
|
* @param {String} direction
|
|
* @returns {Number}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._moveElement = function( elementsArray, elementIndex, direction ) {
|
|
var nextIndex;
|
|
|
|
if ( this.emptyVisible )
|
|
nextIndex = ( direction == 'down' ? elementIndex + 1 : elementIndex - 1 );
|
|
else {
|
|
// When empty elements are not visible, there is need to skip them.
|
|
nextIndex = ToolbarModifier.getFirstElementIndexWith( elementsArray, elementIndex, direction, isEmptyOrSeparatorChecker );
|
|
}
|
|
|
|
function isEmptyOrSeparatorChecker( element ) {
|
|
return element.totalBtns || element.type == 'separator';
|
|
}
|
|
|
|
var offset = nextIndex - elementIndex;
|
|
|
|
return ToolbarModifier.moveTo( offset, elementsArray, elementIndex );
|
|
};
|
|
|
|
/**
|
|
* Moves group located in config level up or down and refresh editor.
|
|
*
|
|
* @param {String} direction
|
|
* @param {String} groupName
|
|
* @returns {Number}
|
|
*/
|
|
ToolbarModifier.prototype._moveGroup = function( direction, groupName ) {
|
|
var groupIndex = this.getGroupIndex( groupName ),
|
|
groups = this.actualConfig.toolbarGroups,
|
|
newIndex = this._moveElement( groups, groupIndex, direction );
|
|
|
|
this._refreshMoveBtnsAvalibility();
|
|
this._refreshBtnTabIndexes();
|
|
this._refreshEditor();
|
|
|
|
return newIndex;
|
|
};
|
|
|
|
/**
|
|
* Moves subgroup located in config level up or down and refresh editor.
|
|
*
|
|
* @param {String} direction
|
|
* @param {String} groupName
|
|
* @param {String} subgroupName
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._moveSubgroup = function( direction, groupName, subgroupName ) {
|
|
var groupIndex = this.getGroupIndex( groupName ),
|
|
groups = this.actualConfig.toolbarGroups,
|
|
group = groups[ groupIndex ],
|
|
subgroupIndex = CKEDITOR.tools.indexOf( group.groups, function( subgroup ) {
|
|
return subgroup.name == subgroupName;
|
|
} ),
|
|
newIndex = this._moveElement( group.groups, subgroupIndex, direction );
|
|
|
|
this._refreshEditor();
|
|
|
|
return newIndex;
|
|
};
|
|
|
|
/**
|
|
* Set `totalBtns` property in `actualConfig.toolbarGroups` elements.
|
|
*
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._calculateTotalBtns = function() {
|
|
var groups = this.actualConfig.toolbarGroups;
|
|
|
|
var i = groups.length;
|
|
// from the end
|
|
while ( i-- ) {
|
|
var currentGroup = groups[ i ],
|
|
totalBtns = ToolbarModifier.getTotalGroupButtonsNumber( currentGroup, this.fullToolbarEditor );
|
|
|
|
if ( currentGroup.type == 'separator' ) {
|
|
// nothing to do with separator
|
|
continue;
|
|
}
|
|
|
|
currentGroup.totalBtns = totalBtns;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add button to removeButtons field in config and refresh editor.
|
|
*
|
|
* @param {String} buttonName
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._addButtonToRemoved = function( buttonName ) {
|
|
if ( CKEDITOR.tools.indexOf( this.removedButtons, buttonName ) != -1 )
|
|
throw 'Button already added to removed';
|
|
|
|
this.removedButtons.push( buttonName );
|
|
this.actualConfig.removeButtons = this.removedButtons.join( ',' );
|
|
this._refreshEditor();
|
|
};
|
|
|
|
/**
|
|
* Remove button from removeButtons field in config and refresh editor.
|
|
*
|
|
* @param {String} buttonName
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._removeButtonFromRemoved = function( buttonName ) {
|
|
var foundAtIndex = CKEDITOR.tools.indexOf( this.removedButtons, buttonName );
|
|
|
|
if ( foundAtIndex === -1 )
|
|
throw 'Trying to remove button from removed, but not found';
|
|
|
|
this.removedButtons.splice( foundAtIndex, 1 );
|
|
this.actualConfig.removeButtons = this.removedButtons.join( ',' );
|
|
this._refreshEditor();
|
|
};
|
|
|
|
/**
|
|
* Parse group "model" to configuration value
|
|
*
|
|
* @param {Object} group
|
|
* @returns {Object}
|
|
* @static
|
|
*/
|
|
ToolbarModifier.parseGroupToConfigValue = function( group ) {
|
|
if ( group.type == 'separator' ) {
|
|
return '/';
|
|
}
|
|
|
|
var groups = group.groups,
|
|
max = groups.length;
|
|
|
|
delete group.totalBtns;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
groups[ i ] = groups[ i ].name;
|
|
}
|
|
|
|
return group;
|
|
};
|
|
|
|
/**
|
|
* Find closest Li ancestor in DOM tree which is group or separator element
|
|
*
|
|
* @param {CKEDITOR.dom.element} element
|
|
* @returns {CKEDITOR.dom.element}
|
|
* @static
|
|
*/
|
|
ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) {
|
|
if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' )
|
|
return element;
|
|
else {
|
|
return ToolbarModifier.getFirstAncestor( element, function( ancestor ) {
|
|
var type = ancestor.data( 'type' );
|
|
|
|
return ( type == 'group' || type == 'separator' );
|
|
} );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create separator literal with unique id.
|
|
*
|
|
* @public
|
|
* @static
|
|
* @return {Object}
|
|
*/
|
|
ToolbarModifier.createSeparatorLiteral = function() {
|
|
return {
|
|
type: 'separator',
|
|
name: ( 'separator' + CKEDITOR.tools.getNextNumber() )
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Creates HTML unordered list string based on toolbarGroups field in config.
|
|
*
|
|
* @returns {String}
|
|
* @static
|
|
*/
|
|
ToolbarModifier.prototype._toolbarConfigToListString = function() {
|
|
var groups = this.actualConfig.toolbarGroups || [],
|
|
listString = '<ul data-type="table-body">';
|
|
|
|
var max = groups.length;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentGroup = groups[ i ];
|
|
|
|
if ( currentGroup.type === 'separator' )
|
|
listString += ToolbarModifier.getToolbarSeparatorString( currentGroup );
|
|
else
|
|
listString += this._getToolbarGroupString( currentGroup );
|
|
}
|
|
|
|
listString += '</ul>';
|
|
|
|
var headerString = ToolbarModifier.getToolbarHeaderString();
|
|
|
|
return headerString + listString;
|
|
};
|
|
|
|
/**
|
|
* Created HTML group list element based on group field in config.
|
|
*
|
|
* @param {Object} group
|
|
* @returns {String}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._getToolbarGroupString = function( group ) {
|
|
var subgroups = group.groups,
|
|
groupString = '';
|
|
|
|
groupString += [
|
|
'<li ',
|
|
'data-type="group" ',
|
|
'data-name="', group.name, '" ',
|
|
( group.totalBtns ? '' : 'class="empty"' ),
|
|
'>'
|
|
].join( '' );
|
|
groupString += ToolbarModifier.getToolbarElementPreString( group ) + '<ul>';
|
|
|
|
var max = subgroups.length;
|
|
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentSubgroup = subgroups[ i ],
|
|
subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ currentSubgroup.name ];
|
|
|
|
groupString += this._getToolbarSubgroupString( currentSubgroup, subgroupBtns );
|
|
}
|
|
groupString += '</ul></li>';
|
|
|
|
return groupString;
|
|
};
|
|
|
|
/**
|
|
* @param {Object} separator
|
|
* @returns {String}
|
|
* @static
|
|
*/
|
|
ToolbarModifier.getToolbarSeparatorString = function( separator ) {
|
|
return [
|
|
'<li ',
|
|
'data-type="', separator.type , '" ',
|
|
'data-name="', separator.name , '"',
|
|
'>',
|
|
ToolbarModifier.getToolbarElementPreString( 'row separator' ),
|
|
'</li>'
|
|
].join( '' );
|
|
};
|
|
|
|
/**
|
|
* @returns {string}
|
|
*/
|
|
ToolbarModifier.getToolbarHeaderString = function() {
|
|
return '<ul data-type="table-header">' +
|
|
'<li data-type="header">' +
|
|
'<p>Toolbars</p>' +
|
|
'<ul>' +
|
|
'<li>' +
|
|
'<p>Toolbar groups</p>' +
|
|
'<p>Toolbar group items</p>' +
|
|
'</li>' +
|
|
'</ul>' +
|
|
'</li>' +
|
|
'</ul>';
|
|
};
|
|
|
|
/**
|
|
* Find and return first ancestor of element provided in first argument
|
|
* which match the criteria checked in function provided in second argument.
|
|
*
|
|
* @param {CKEDITOR.dom.element} element
|
|
* @param {Function} checker
|
|
* @returns {CKEDITOR.dom.element|null}
|
|
*/
|
|
ToolbarModifier.getFirstAncestor = function( element, checker ) {
|
|
var ancestors = element.getParents(),
|
|
i = ancestors.length;
|
|
|
|
while ( i-- ) {
|
|
if ( checker( ancestors[ i ] ) )
|
|
return ancestors[ i ];
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Looking through array elements start from index provided in second argument
|
|
* and go 'up' or 'down' in array
|
|
* last argument is condition checker which should return Boolean value
|
|
*
|
|
* User cases:
|
|
*
|
|
* ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'down', function( elem ) { return elem == 4; } ); // 4
|
|
* ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'up', function( elem ) { return elem == 4; } ); // 1
|
|
*
|
|
* @param {Array} array
|
|
* @param {Number} i
|
|
* @param {String} direction 'up' or 'down'
|
|
* @param {Function} conditionChecker
|
|
* @static
|
|
* @returns {Number} index of found element
|
|
*/
|
|
ToolbarModifier.getFirstElementIndexWith = function( array, i, direction, conditionChecker ) {
|
|
function whileChecker() {
|
|
var result;
|
|
if ( direction === 'up' )
|
|
result = i--;
|
|
else
|
|
result = ( ++i < array.length );
|
|
|
|
return result;
|
|
}
|
|
|
|
while ( whileChecker() ) {
|
|
if ( conditionChecker( array[ i ] ) )
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Moves array element at index level up or down.
|
|
*
|
|
* @static
|
|
* @param {String} direction
|
|
* @param {Array} array
|
|
* @param {Number} index
|
|
* @returns {Number}
|
|
*/
|
|
ToolbarModifier.moveTo = function( offset, array, index ) {
|
|
var element, newIndex;
|
|
|
|
if ( index !== -1 )
|
|
element = array.splice( index, 1 )[ 0 ];
|
|
|
|
newIndex = index + offset;
|
|
|
|
array.splice( newIndex, 0, element );
|
|
|
|
return newIndex;
|
|
};
|
|
|
|
/**
|
|
* @static
|
|
* @param {Object} subgroup
|
|
* @returns {Number}
|
|
*/
|
|
ToolbarModifier.getTotalSubGroupButtonsNumber = function( subgroup, fullToolbarEditor ) {
|
|
var subgroupName = ( typeof subgroup == 'string' ? subgroup : subgroup.name ),
|
|
subgroupBtns = fullToolbarEditor.buttonsByGroup[ subgroupName ];
|
|
|
|
return ( subgroupBtns ? subgroupBtns.length : 0 );
|
|
};
|
|
|
|
/**
|
|
* Returns all buttons number in group which are nested in subgroups also.
|
|
*
|
|
* @param {Object} group
|
|
* @param {ToolbarModifier.FullToolbarEditor}
|
|
* @static
|
|
* @returns {Number}
|
|
*/
|
|
ToolbarModifier.getTotalGroupButtonsNumber = function( group, fullToolbarEditor ) {
|
|
var total = 0,
|
|
subgroups = group.groups;
|
|
|
|
var max = subgroups ? subgroups.length : 0;
|
|
for ( var i = 0; i < max; i += 1 )
|
|
total += ToolbarModifier.getTotalSubGroupButtonsNumber( subgroups[ i ], fullToolbarEditor );
|
|
|
|
return total;
|
|
};
|
|
|
|
/**
|
|
* Creates HTML subgroup list element based on subgroup field in config.
|
|
*
|
|
* @param {Object} subgroup
|
|
* @param {Array} groupBtns
|
|
* @returns {String}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._getToolbarSubgroupString = function( subgroup, groupBtns ) {
|
|
var subgroupString = '';
|
|
|
|
subgroupString += [
|
|
'<li ',
|
|
'data-type="subgroup" ',
|
|
'data-name="', subgroup.name, '" ',
|
|
( subgroup.totalBtns ? '' : 'class="empty" ' ),
|
|
'>'
|
|
].join( '' );
|
|
subgroupString += ToolbarModifier.getToolbarElementPreString( subgroup.name );
|
|
subgroupString += '<ul>';
|
|
|
|
var max = groupBtns ? groupBtns.length : 0;
|
|
for ( var i = 0; i < max; i += 1 )
|
|
subgroupString += this.getButtonString( groupBtns[ i ] );
|
|
|
|
subgroupString += '</ul>';
|
|
|
|
subgroupString += '</li>';
|
|
|
|
return subgroupString;
|
|
};
|
|
|
|
/**
|
|
* @param {String} buttonName
|
|
* @returns {String|null}
|
|
* @private
|
|
*/
|
|
ToolbarModifier.prototype._getConfigButtonName = function( buttonName ) {
|
|
var items = this.fullToolbarEditor.editorInstance.ui.items;
|
|
|
|
var name;
|
|
for ( name in items ) {
|
|
if ( items[ name ].name == buttonName )
|
|
return name;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {String} buttonName
|
|
* @returns {Boolean}
|
|
*/
|
|
ToolbarModifier.prototype.isButtonRemoved = function( buttonName ) {
|
|
return CKEDITOR.tools.indexOf( this.removedButtons, this._getConfigButtonName( buttonName ) ) != -1;
|
|
};
|
|
|
|
/**
|
|
* @param {CKEDITOR.ui.button/CKEDITOR.ui.richCombo} button
|
|
* @returns {String}
|
|
* @public
|
|
*/
|
|
ToolbarModifier.prototype.getButtonString = function( button ) {
|
|
var checked = ( this.isButtonRemoved( button.name ) ? '' : 'checked="checked"' );
|
|
|
|
return [
|
|
'<li data-tab="true" data-type="button" data-name="', this._getConfigButtonName( button.name ), '">',
|
|
'<label title="', button.label, '" >',
|
|
'<input ',
|
|
'tabindex="-1"',
|
|
'type="checkbox"',
|
|
checked,
|
|
'/>',
|
|
button.$.getOuterHtml(),
|
|
'</label>',
|
|
'</li>'
|
|
].join( '' );
|
|
};
|
|
|
|
/**
|
|
* Creates group header string.
|
|
*
|
|
* @param {Object|String} group
|
|
* @returns {String}
|
|
* @static
|
|
*/
|
|
ToolbarModifier.getToolbarElementPreString = function( group ) {
|
|
var name = ( group.name ? group.name : group );
|
|
|
|
return [
|
|
'<p>',
|
|
'<span>',
|
|
'<button title="Move element upward" data-tab="true" data-direction="up" class="move icon-up-big"></button>',
|
|
'<button title="Move element downward" data-tab="true" data-direction="down" class="move icon-down-big"></button>',
|
|
( name == 'row separator' ? '<button title="Remove element" data-tab="true" class="remove icon-trash"></button>' : '' ),
|
|
name,
|
|
'</span>',
|
|
'</p>'
|
|
].join( '' );
|
|
};
|
|
|
|
/**
|
|
* @static
|
|
* @param {String} cfg
|
|
* @returns {String}
|
|
*/
|
|
ToolbarModifier.evaluateToolbarGroupsConfig = function( cfg ) {
|
|
cfg = ( function( cfg ) {
|
|
var config = {}, result;
|
|
|
|
/*jshint -W002 */
|
|
try {
|
|
result = eval( '(' + cfg + ')' );
|
|
} catch ( e ) {
|
|
try {
|
|
result = eval( cfg );
|
|
} catch ( e ) {
|
|
return null;
|
|
}
|
|
}
|
|
/*jshint +W002 */
|
|
|
|
if ( config.toolbarGroups && typeof config.toolbarGroups.length === 'number' ) {
|
|
return JSON.stringify( config );
|
|
} else if ( result && typeof result.length === 'number' ) {
|
|
return JSON.stringify( { toolbarGroups: result } );
|
|
} else if ( result && result.toolbarGroups ) {
|
|
return JSON.stringify( result );
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
}( cfg ) );
|
|
|
|
return cfg;
|
|
};
|
|
|
|
return ToolbarModifier;
|
|
} )();
|
|
|