mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-29 00:30:44 +00:00
73851696f1
This works just fine, as also previously tested/proven by ve.cloneObject, which uses the same concept of creating an object identical to what invoking the constructor with "new" would do, but without invoking the constructor function (which has side- effects). Except in this case we do invoke the constructor function, but we can't use new in ve.Factory because of the arbitrary number of arguments. Added a test to assert that 3+ arguments and that instanceof work as expected. Change-Id: If0add3da7475886e476900044acda2ba7d01fb11
92 lines
2.5 KiB
JavaScript
92 lines
2.5 KiB
JavaScript
/**
|
|
* VisualEditor Factory 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.Factory = function VeFactory() {
|
|
// Parent constructor
|
|
ve.EventEmitter.call( this );
|
|
|
|
// Properties
|
|
this.registry = [];
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.Factory, ve.EventEmitter );
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Register a constructor with the factory.
|
|
*
|
|
* Arguments will be passed through directly to the constructor.
|
|
* @see {ve.Factory.prototype.create}
|
|
*
|
|
* @method
|
|
* @param {String} type Object type
|
|
* @param {Function} constructor Constructor to use when creating object
|
|
* @throws 'Constructor must be a function, cannot be a string'
|
|
*/
|
|
ve.Factory.prototype.register = function ( type, constructor ) {
|
|
if ( typeof constructor !== 'function' ) {
|
|
throw new Error( 'Constructor must be a function, cannot be a ' + typeof constructor );
|
|
}
|
|
this.registry[type] = constructor;
|
|
this.emit( 'register', type, constructor );
|
|
};
|
|
|
|
/**
|
|
* Create an object based on a type.
|
|
*
|
|
* Type is used to look up the constructor to use, while all additional arguments are passed to the
|
|
* constructor directly, so leaving one out will pass an undefined to the constructor.
|
|
*
|
|
* @method
|
|
* @param {string} type Object type.
|
|
* @param {mixed} [...] Arguments to pass to the constructor.
|
|
* @returns {Object} The new object.
|
|
* @throws 'Unknown object type'
|
|
*/
|
|
ve.Factory.prototype.create = function ( type ) {
|
|
var args, obj,
|
|
constructor = this.registry[type];
|
|
|
|
if ( constructor === undefined ) {
|
|
throw new Error( 'Unknown object type: ' + type );
|
|
}
|
|
|
|
// Convert arguments to array and shift the first argument (type) off
|
|
args = Array.prototype.slice.call( arguments, 1 );
|
|
|
|
// We can't use the "new" operator with .apply directly because apply needs a
|
|
// context. So instead just do what "new" does: Create an object that inherits from
|
|
// the constructor's prototype (which also makes it an "instanceof" the constructor),
|
|
// then invoke the constructor with the object as context, and return it (ignoring
|
|
// the constructor's return value).
|
|
obj = ve.createObject( constructor.prototype );
|
|
constructor.apply( obj, args );
|
|
return obj;
|
|
};
|
|
|
|
/**
|
|
* Gets a constructor for a given type.
|
|
*
|
|
* @method
|
|
* @param {String} type Object type
|
|
* @returns {Function|undefined} Constructor for type
|
|
*/
|
|
ve.Factory.prototype.lookup = function ( type ) {
|
|
return this.registry[type];
|
|
};
|