mediawiki-extensions-Visual.../modules/ve/ve.EventEmitter.js
Trevor Parscal d8ee3c2c29 After much research on error objects, native = good, custom = bad
Stack traces, line numbers, etc. All the approaches I've seen are bad hacks. This is the best way to go.

Change-Id: Ib12e9d2ecfe610bcc89d046005e35cc13efa3d99
2012-08-08 10:48:53 -07:00

197 lines
5.2 KiB
JavaScript

/**
* VisualEditor EventEmitter class.
*
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Event emitter.
*
* @class
* @abstract
* @constructor
* @property events {Object}
*/
ve.EventEmitter = function () {
this.events = {};
};
/* Methods */
/**
* Emits an event.
*
* @method
* @param type {String} Type of event
* @param args {Mixed} First in a list of variadic arguments passed to event handler (optional)
* @returns {Boolean} If event was handled by at least one listener
*/
ve.EventEmitter.prototype.emit = function ( type ) {
if ( type === 'error' && !( 'error' in this.events ) ) {
throw new Error( 'Missing error handler error.' );
}
if ( !( type in this.events ) ) {
return false;
}
var i,
listeners = this.events[type].slice(),
length = listeners.length,
args = Array.prototype.slice.call( arguments, 1 );
for ( i = 0; i < length; i++ ) {
listeners[i].apply( this, args );
}
return true;
};
/**
* Adds a listener to events of a specific type.
*
* @method
* @param type {String} Type of event to listen to
* @param listener {Function} Listener to call when event occurs
* @returns {ve.EventEmitter} This object
* @throws "Invalid listener error" if listener argument is not a function
*/
ve.EventEmitter.prototype.addListener = function ( type, listener ) {
if ( typeof listener !== 'function' ) {
throw new Error( 'Invalid listener error. Function expected.' );
}
this.emit( 'newListener', type, listener );
if ( type in this.events ) {
this.events[type].push( listener );
} else {
this.events[type] = [listener];
}
return this;
};
/**
* Add multiple listeners at once.
*
* @method
* @param listeners {Object} List of event/callback pairs
* @returns {ve.EventEmitter} This object
*/
ve.EventEmitter.prototype.addListeners = function ( listeners ) {
for ( var event in listeners ) {
this.addListener( event, listeners[event] );
}
return this;
};
/**
* Add a listener, mapped to a method on a target object.
*
* @method
* @param target {Object} Object to call methods on when events occur
* @param event {String} Name of event to trigger on
* @param method {String} Name of method to call
* @returns {ve.EventEmitter} This object
*/
ve.EventEmitter.prototype.addListenerMethod = function ( target, event, method ) {
return this.addListener( event, function () {
if ( typeof target[method] === 'function' ) {
target[method].apply( target, Array.prototype.slice.call( arguments, 0 ) );
} else {
throw new Error( 'Listener method error. Target has no such method: ' + method );
}
} );
};
/**
* Add multiple listeners, each mapped to a method on a target object.
*
* @method
* @param target {Object} Object to call methods on when events occur
* @param methods {Object} List of event/method name pairs
* @returns {ve.EventEmitter} This object
*/
ve.EventEmitter.prototype.addListenerMethods = function ( target, methods ) {
for ( var event in methods ) {
this.addListenerMethod( target, event, methods[event] );
}
return this;
};
/**
* Alias for addListener
*
* @method
*/
ve.EventEmitter.prototype.on = ve.EventEmitter.prototype.addListener;
/**
* Adds a one-time listener to a specific event.
*
* @method
* @param type {String} Type of event to listen to
* @param listener {Function} Listener to call when event occurs
* @returns {ve.EventEmitter} This object
*/
ve.EventEmitter.prototype.once = function ( type, listener ) {
var eventEmitter = this;
return this.addListener( type, function listenerWrapper() {
eventEmitter.removeListener( type, listenerWrapper );
listener.apply( eventEmitter, Array.prototype.slice.call( arguments, 0 ) );
} );
};
/**
* Removes a specific listener from a specific event.
*
* @method
* @param type {String} Type of event to remove listener from
* @param listener {Function} Listener to remove
* @returns {ve.EventEmitter} This object
* @throws "Invalid listener error" if listener argument is not a function
*/
ve.EventEmitter.prototype.removeListener = function ( type, listener ) {
if ( typeof listener !== 'function' ) {
throw new Error( 'Invalid listener error. Function expected.' );
}
if ( !( type in this.events ) || !this.events[type].length ) {
return this;
}
var i,
handlers = this.events[type];
if ( handlers.length === 1 && handlers[0] === listener ) {
delete this.events[type];
} else {
i = ve.inArray( listener, handlers );
if ( i < 0 ) {
return this;
}
handlers.splice( i, 1 );
if ( handlers.length === 0 ) {
delete this.events[type];
}
}
return this;
};
/**
* Removes all listeners from a specific event.
*
* @method
* @param type {String} Type of event to remove listeners from
* @returns {ve.EventEmitter} This object
*/
ve.EventEmitter.prototype.removeAllListeners = function ( type ) {
if ( type in this.events ) {
delete this.events[type];
}
return this;
};
/**
* Gets a list of listeners attached to a specific event.
*
* @method
* @param type {String} Type of event to get listeners for
* @returns {Array} List of listeners to an event
*/
ve.EventEmitter.prototype.listeners = function ( type ) {
return type in this.events ? this.events[type] : [];
};