mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-24 10:48:42 +00:00
Refactored commands into a registry
* Now ve.Factory inherits from the more general ve.Registry * New class ve.CommandRegistry * Refactored setupToolbar and command setup code into setupComands Change-Id: Ic548e5de95b77889727362d3e66d7be83c12a603
This commit is contained in:
parent
ab57bed7da
commit
443c5438ab
|
@ -154,9 +154,11 @@ $wgResourceModules += array(
|
|||
'ext.visualEditor.core' => $wgVisualEditorResourceTemplate + array(
|
||||
'scripts' => array(
|
||||
// ve
|
||||
've/ve.Registry.js',
|
||||
've/ve.Factory.js',
|
||||
've/ve.Position.js',
|
||||
've/ve.Command.js',
|
||||
've/ve.CommandRegistry.js',
|
||||
've/ve.Range.js',
|
||||
've/ve.Node.js',
|
||||
've/ve.BranchNode.js',
|
||||
|
|
|
@ -88,9 +88,11 @@ $html = '<div>' . file_get_contents( $page ) . '</div>';
|
|||
|
||||
<!-- Generated by maintenance/makeStaticLoader.php (cont.) -->
|
||||
<!-- ext.visualEditor.core -->
|
||||
<script src="../../modules/ve/ve.Registry.js"></script>
|
||||
<script src="../../modules/ve/ve.Factory.js"></script>
|
||||
<script src="../../modules/ve/ve.Position.js"></script>
|
||||
<script src="../../modules/ve/ve.Command.js"></script>
|
||||
<script src="../../modules/ve/ve.CommandRegistry.js"></script>
|
||||
<script src="../../modules/ve/ve.Range.js"></script>
|
||||
<script src="../../modules/ve/ve.Node.js"></script>
|
||||
<script src="../../modules/ve/ve.BranchNode.js"></script>
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
<script src="../../ve/init/sa/ve.init.sa.js"></script>
|
||||
<script src="../../ve/init/sa/ve.init.sa.Platform.js"></script>
|
||||
<!-- ext.visualEditor.core -->
|
||||
<script src="../../ve/ve.Registry.js"></script>
|
||||
<script src="../../ve/ve.Factory.js"></script>
|
||||
<script src="../../ve/ve.Position.js"></script>
|
||||
<script src="../../ve/ve.Command.js"></script>
|
||||
<script src="../../ve/ve.CommandRegistry.js"></script>
|
||||
<script src="../../ve/ve.Range.js"></script>
|
||||
<script src="../../ve/ve.Node.js"></script>
|
||||
<script src="../../ve/ve.BranchNode.js"></script>
|
||||
|
|
68
modules/ve/ve.CommandRegistry.js
Normal file
68
modules/ve/ve.CommandRegistry.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* VisualEditor CommandRegistry class.
|
||||
*
|
||||
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Command registry.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @extends {ve.Registry}
|
||||
*/
|
||||
ve.CommandRegistry = function VeCommandRegistry() {
|
||||
// Parent constructor
|
||||
ve.Registry.call( this );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.CommandRegistry, ve.Registry );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Register a constructor with the factory.
|
||||
*
|
||||
* @method
|
||||
* @param {String|String[]} name Symbolic name or list of symbolic names
|
||||
* @param {String|String[]} trigger Command string of keys that should trigger the command
|
||||
* @param {String} action Action to execute when command is triggered
|
||||
* @param {String} method Method to call on action when executing
|
||||
* @param {Mixed} [...] Additional data to pass to the action when executing
|
||||
* @throws 'trigger must be a string or array'
|
||||
* @throws 'action must be a string'
|
||||
* @throws 'method must be a string'
|
||||
*/
|
||||
ve.CommandRegistry.prototype.register = function ( name, trigger, action, method ) {
|
||||
if ( typeof trigger !== 'string' && !ve.isArray( trigger ) ) {
|
||||
throw new Error( 'trigger must be a string or array, cannot be a ' + typeof trigger );
|
||||
}
|
||||
if ( typeof action !== 'string' ) {
|
||||
throw new Error( 'action must be a string, cannot be a ' + typeof action );
|
||||
}
|
||||
if ( typeof method !== 'string' ) {
|
||||
throw new Error( 'method must be a string, cannot be a ' + typeof method );
|
||||
}
|
||||
ve.Registry.prototype.register.call(
|
||||
this, name, { 'trigger': trigger, 'action': Array.prototype.slice.call( arguments, 2 ) }
|
||||
);
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
ve.commandRegistry = new ve.CommandRegistry();
|
||||
|
||||
// TODO: Move these somewhere else
|
||||
|
||||
ve.commandRegistry.register(
|
||||
'bold', ['cmd+b', 'ctrl+b'], 'annotation', 'toggle', 'textStyle/bold'
|
||||
);
|
||||
ve.commandRegistry.register(
|
||||
'italic', ['cmd+i', 'ctrl+i'], 'annotation', 'toggle', 'textStyle/italic'
|
||||
);
|
||||
ve.commandRegistry.register( 'link', ['cmd+k', 'ctrl+k'], 'inspector', 'open', 'link' );
|
||||
ve.commandRegistry.register( 'undo', ['cmd+z', 'ctrl+z'], 'history', 'undo' );
|
||||
ve.commandRegistry.register( 'redo', ['cmd+shift+z', 'ctrl+shift+z'], 'history', 'redo' );
|
|
@ -11,11 +11,11 @@
|
|||
* @class
|
||||
* @abstract
|
||||
* @constructor
|
||||
* @extends {ve.EventEmitter}
|
||||
* @extends {ve.Registry}
|
||||
*/
|
||||
ve.Factory = function VeFactory() {
|
||||
// Parent constructor
|
||||
ve.EventEmitter.call( this );
|
||||
ve.Registry.call( this );
|
||||
|
||||
// Properties
|
||||
this.registry = [];
|
||||
|
@ -23,36 +23,23 @@ ve.Factory = function VeFactory() {
|
|||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.Factory, ve.EventEmitter );
|
||||
ve.inheritClass( ve.Factory, ve.Registry );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Register a constructor with the factory.
|
||||
*
|
||||
* Arguments will be passed through directly to the constructor.
|
||||
* @see {ve.Factory.prototype.create}
|
||||
*
|
||||
* @method
|
||||
* @param {String|String[]} name Symbolic name or list of symbolic names
|
||||
* @param {Function} constructor Constructor to use when creating object
|
||||
* @throws 'Constructor must be a function, cannot be a string'
|
||||
* @throws 'constructor must be a function'
|
||||
*/
|
||||
ve.Factory.prototype.register = function ( name, constructor ) {
|
||||
var i, len;
|
||||
if ( typeof constructor !== 'function' ) {
|
||||
throw new Error( 'Constructor must be a function, cannot be a ' + typeof constructor );
|
||||
}
|
||||
if ( ve.isArray( name ) ) {
|
||||
for ( i = 0, len = name.length; i < len; i++ ) {
|
||||
this.register( name[i], constructor );
|
||||
}
|
||||
} else if ( typeof name === 'string' ) {
|
||||
this.registry[name] = constructor;
|
||||
this.emit( 'register', name, constructor );
|
||||
} else {
|
||||
throw new Error( 'Name must be a string or array of strings, cannot be a ' + typeof name );
|
||||
throw new Error( 'constructor must be a function, cannot be a ' + typeof constructor );
|
||||
}
|
||||
ve.Registry.prototype.register.call( this, name, constructor );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -72,7 +59,7 @@ ve.Factory.prototype.create = function ( name ) {
|
|||
constructor = this.registry[name];
|
||||
|
||||
if ( constructor === undefined ) {
|
||||
throw new Error( 'Unknown object name: ' + name );
|
||||
throw new Error( 'No class registered by that name: ' + name );
|
||||
}
|
||||
|
||||
// Convert arguments to array and shift the first argument (name) off
|
||||
|
@ -87,14 +74,3 @@ ve.Factory.prototype.create = function ( name ) {
|
|||
constructor.apply( obj, args );
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a constructor for a given name.
|
||||
*
|
||||
* @method
|
||||
* @param {String} name Object name
|
||||
* @returns {Function|undefined} Constructor for name
|
||||
*/
|
||||
ve.Factory.prototype.lookup = function ( name ) {
|
||||
return this.registry[name];
|
||||
};
|
||||
|
|
64
modules/ve/ve.Registry.js
Normal file
64
modules/ve/ve.Registry.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* VisualEditor Registry class.
|
||||
*
|
||||
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic object factory.
|
||||
*
|
||||
* @class
|
||||
* @abstract
|
||||
* @constructor
|
||||
* @extends {ve.EventEmitter}
|
||||
*/
|
||||
ve.Registry = function VeRegistry() {
|
||||
// Parent constructor
|
||||
ve.EventEmitter.call( this );
|
||||
|
||||
// Properties
|
||||
this.registry = [];
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.Registry, ve.EventEmitter );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Associate one or more symbolic names with some data.
|
||||
*
|
||||
* @method
|
||||
* @param {String|String[]} name Symbolic name or list of symbolic names
|
||||
* @param {Mixed} data Data to associate with symbolic name
|
||||
* @throws 'name must be a string'
|
||||
*/
|
||||
ve.Registry.prototype.register = function ( name, data ) {
|
||||
if ( typeof name !== 'string' && !ve.isArray( name ) ) {
|
||||
throw new Error( 'name must be a string or array, cannot be a ' + typeof name );
|
||||
}
|
||||
var i, len;
|
||||
if ( ve.isArray( name ) ) {
|
||||
for ( i = 0, len = name.length; i < len; i++ ) {
|
||||
this.register( name[i], data );
|
||||
}
|
||||
} else if ( typeof name === 'string' ) {
|
||||
this.registry[name] = data;
|
||||
this.emit( 'register', name, data );
|
||||
} else {
|
||||
throw new Error( 'Name must be a string or array of strings, cannot be a ' + typeof name );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets data for a given symbolic name.
|
||||
*
|
||||
* @method
|
||||
* @param {String} name Symbolic name
|
||||
* @returns {Mixed|undefined} Data associated with symbolic name
|
||||
*/
|
||||
ve.Registry.prototype.lookup = function ( name ) {
|
||||
return this.registry[name];
|
||||
};
|
|
@ -40,7 +40,7 @@ ve.Surface = function VeSurface( parent, dom, options ) {
|
|||
|
||||
// Initialization
|
||||
this.setupToolbars();
|
||||
this.addCommands( this.options.commands );
|
||||
this.setupCommands();
|
||||
ve.instances.push( this );
|
||||
this.model.startHistoryTracking();
|
||||
};
|
||||
|
@ -50,6 +50,7 @@ ve.Surface = function VeSurface( parent, dom, options ) {
|
|||
ve.Surface.defaultOptions = {
|
||||
'toolbars': {
|
||||
'top': {
|
||||
'float': true,
|
||||
'tools': [
|
||||
{ 'name': 'history', 'items' : ['undo', 'redo'] },
|
||||
{ 'name': 'textStyle', 'items' : ['format'] },
|
||||
|
@ -62,50 +63,6 @@ ve.Surface.defaultOptions = {
|
|||
'commands': ['bold', 'italic', 'link', 'undo', 'redo']
|
||||
};
|
||||
|
||||
/**
|
||||
* Common commands that can be invoked by their symbolic names.
|
||||
*
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.Surface.commands = {
|
||||
'bold': {
|
||||
'trigger': ['cmd+b', 'ctrl+b'],
|
||||
'action': ['annotation', 'toggle', 'textStyle/bold']
|
||||
},
|
||||
'italic': {
|
||||
'trigger': ['cmd+i', 'ctrl+i'],
|
||||
'action': ['annotation', 'toggle', 'textStyle/italic']
|
||||
},
|
||||
'link': {
|
||||
'trigger': ['cmd+k', 'ctrl+k'],
|
||||
'action': ['inspector', 'open', 'link']
|
||||
},
|
||||
'undo': {
|
||||
'trigger': ['cmd+z', 'ctrl+z'],
|
||||
'action': ['history', 'undo']
|
||||
},
|
||||
'redo': {
|
||||
'trigger': ['cmd+shift+z', 'ctrl+shift+z'],
|
||||
'action': ['history', 'redo']
|
||||
}
|
||||
};
|
||||
|
||||
/* Static Methods */
|
||||
|
||||
/**
|
||||
* Adds a command that can be referenced by a symbolic name.
|
||||
*
|
||||
* @static
|
||||
* @method
|
||||
* @param {String} name Symbolic name of command
|
||||
* @param {String|String[]} trigger One or more canonical representations of keyboard triggers
|
||||
* @param {Array} action Array containing the action name, method and additional arguments
|
||||
*/
|
||||
ve.Surface.registerCommand = function ( name, trigger, action ) {
|
||||
ve.Surface.commands[name] = { 'trigger': trigger, 'action': action };
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
|
@ -176,53 +133,33 @@ ve.Surface.prototype.execute = function ( action, method ) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Adds a link between a keyboard trigger and an action.
|
||||
* Adds all commands from initialization options.
|
||||
*
|
||||
* Commands must be registered through {ve.commandRegsitry} prior to constructing the surface.
|
||||
*
|
||||
* @method
|
||||
* @param {String|String[]} trigger One or more canonical representations of keyboard triggers
|
||||
* @param {Array} action Array containing the action name, method and additional arguments
|
||||
* @param {String[]} commands Array of symbolic names of registered commands
|
||||
*/
|
||||
ve.Surface.prototype.addCommand = function ( triggers, action ) {
|
||||
var i, len, trigger;
|
||||
if ( !ve.isArray( triggers ) ) {
|
||||
triggers = [triggers];
|
||||
}
|
||||
for ( i = 0, len = triggers.length; i < len; i++ ) {
|
||||
// Normalize
|
||||
trigger = ( new ve.Command( triggers[i] ) ).toString();
|
||||
// Validate
|
||||
if ( trigger.length === 0 ) {
|
||||
throw new Error( 'Incomplete command: ' + triggers[i] );
|
||||
ve.Surface.prototype.setupCommands = function () {
|
||||
var i, iLen, j, jLen, triggers, trigger, command,
|
||||
commands = this.options.commands;
|
||||
for ( i = 0, iLen = commands.length; i < iLen; i++ ) {
|
||||
command = ve.commandRegistry.lookup( commands[i] );
|
||||
if ( !command ) {
|
||||
throw new Error( 'No command registered by that name: ' + commands[i] );
|
||||
}
|
||||
this.commands[trigger] = action;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds multiple links between a keyboard triggers and an actions.
|
||||
*
|
||||
* Each object's trigger and action properties will be passed directly into
|
||||
* {ve.Surface.prototype.addCommand}.
|
||||
*
|
||||
* @method
|
||||
* @param {String[]|Object[]} commands Array of symbolic names of known commands, or objects that
|
||||
* each contain a trigger and action property
|
||||
*/
|
||||
ve.Surface.prototype.addCommands = function ( commands ) {
|
||||
var i, len;
|
||||
for ( i = 0, len = commands.length; i < len; i++ ) {
|
||||
if ( typeof commands[i] === 'string' ) {
|
||||
if ( !( commands[i] in ve.Surface.commands ) ) {
|
||||
throw new Error( 'Unknown command: ' + commands[i] );
|
||||
triggers = command.trigger;
|
||||
if ( !ve.isArray( triggers ) ) {
|
||||
triggers = [triggers];
|
||||
}
|
||||
for ( j = 0, jLen = triggers.length; j < jLen; j++ ) {
|
||||
// Normalize
|
||||
trigger = ( new ve.Command( triggers[j] ) ).toString();
|
||||
// Validate
|
||||
if ( trigger.length === 0 ) {
|
||||
throw new Error( 'Incomplete command: ' + triggers[j] );
|
||||
}
|
||||
this.addCommand(
|
||||
ve.Surface.commands[commands[i]].trigger,
|
||||
ve.Surface.commands[commands[i]].action
|
||||
);
|
||||
} else if ( ve.isPlainObject( commands[i] ) ) {
|
||||
this.addCommand( commands[i].trigger, commands[i].action );
|
||||
} else {
|
||||
throw new Error( 'Invalid command, must be name of known command or command object' );
|
||||
this.commands[trigger] = command.action;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -235,36 +172,32 @@ ve.Surface.prototype.addCommands = function ( commands ) {
|
|||
* @method
|
||||
*/
|
||||
ve.Surface.prototype.setupToolbars = function () {
|
||||
var surface = this;
|
||||
|
||||
// Build each toolbar
|
||||
$.each( this.options.toolbars, function ( name, config ) {
|
||||
if ( config !== null ) {
|
||||
var name, config, toolbar,
|
||||
toolbars = this.options.toolbars;
|
||||
for ( name in toolbars ) {
|
||||
config = toolbars[name];
|
||||
if ( ve.isPlainObject( config ) ) {
|
||||
this.toolbars[name] = toolbar = { '$': $( '<div class="ve-ui-toolbar"></div>' ) };
|
||||
if ( name === 'top' ) {
|
||||
surface.toolbars[name] = {
|
||||
'$wrapper': $( '<div class="ve-ui-toolbar-wrapper"></div>' ),
|
||||
'$': $( '<div class="ve-ui-toolbar"></div>' )
|
||||
.append(
|
||||
$( '<div class="ve-ui-actions"></div>' ),
|
||||
$( '<div style="clear:both"></div>' ),
|
||||
$( '<div class="ve-ui-toolbar-shadow"></div>' )
|
||||
)
|
||||
};
|
||||
surface.toolbars[name].$wrapper.append( surface.toolbars[name].$ );
|
||||
surface.$.before( surface.toolbars[name].$wrapper );
|
||||
|
||||
// Add extra sections to the toolbar
|
||||
toolbar.$.append(
|
||||
'<div class="ve-ui-actions"></div>' +
|
||||
'<div style="clear:both"></div>' +
|
||||
'<div class="ve-ui-toolbar-shadow"></div>'
|
||||
);
|
||||
// Wrap toolbar for floating
|
||||
toolbar.$wrapper = $( '<div class="ve-ui-toolbar-wrapper"></div>' )
|
||||
.append( this.toolbars[name].$ );
|
||||
// Add toolbar to surface
|
||||
this.$.before( toolbar.$wrapper );
|
||||
if ( 'float' in config && config.float === true ) {
|
||||
// Float top toolbar
|
||||
surface.floatTopToolbar();
|
||||
this.floatTopToolbar();
|
||||
}
|
||||
}
|
||||
surface.toolbars[name].instance = new ve.ui.Toolbar(
|
||||
surface.toolbars[name].$,
|
||||
surface,
|
||||
config.tools
|
||||
);
|
||||
toolbar.instance = new ve.ui.Toolbar( toolbar.$, this, config.tools );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue