Catrope has uploaded a new change for review.
https://gerrit.wikimedia.org/r/155589
Change subject: Make OO.EventEmitter.static.validateMethod private
......................................................................
Make OO.EventEmitter.static.validateMethod private
To avoid namespace pollution
Change-Id: I89056c47fa14af3d7ba8cd326bcd84302353fe0c
---
M src/EventEmitter.js
1 file changed, 229 insertions(+), 223 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/oojs/core refs/changes/89/155589/1
diff --git a/src/EventEmitter.js b/src/EventEmitter.js
index b9949c7..661c5c2 100644
--- a/src/EventEmitter.js
+++ b/src/EventEmitter.js
@@ -1,249 +1,255 @@
/*global hasOwn */
-/**
- * @class OO.EventEmitter
- *
- * @constructor
- */
-oo.EventEmitter = function OoEventEmitter() {
- // Properties
+( function () {
/**
- * Storage of bound event handlers by event name.
+ * @class OO.EventEmitter
*
- * @property
+ * @constructor
*/
- this.bindings = {};
-};
+ oo.EventEmitter = function OoEventEmitter() {
+ // Properties
-oo.initClass( oo.EventEmitter );
+ /**
+ * Storage of bound event handlers by event name.
+ *
+ * @property
+ */
+ this.bindings = {};
+ };
-/* Methods */
+ oo.initClass( oo.EventEmitter );
-/**
- * Add a listener to events of a specific event.
- *
- * The listener can be a function or the string name of a method; if the
latter, then the
- * name lookup happens at the time the listener is called.
- *
- * @param {string} event Type of event to listen to
- * @param {Function|string} method Function or method name to call when event
occurs
- * @param {Array} [args] Arguments to pass to listener, will be prepended to
emitted arguments
- * @param {Object} [context=null] Context object for function or method call
- * @throws {Error} Listener argument is not a function or a valid method name
- * @chainable
- */
-oo.EventEmitter.prototype.on = function ( event, method, args, context ) {
- var bindings;
+ /* Private helper functions */
- this.constructor.static.validateMethod( method, context );
-
- if ( hasOwn.call( this.bindings, event ) ) {
- bindings = this.bindings[event];
- } else {
- // Auto-initialize bindings list
- bindings = this.bindings[event] = [];
- }
- // Add binding
- bindings.push( {
- method: method,
- args: args,
- context: ( arguments.length < 4 ) ? null : context
- } );
- return this;
-};
-
-/**
- * Adds a one-time listener to a specific event.
- *
- * @param {string} event Type of event to listen to
- * @param {Function} listener Listener to call when event occurs
- * @chainable
- */
-oo.EventEmitter.prototype.once = function ( event, listener ) {
- var eventEmitter = this,
- listenerWrapper = function () {
- eventEmitter.off( event, listenerWrapper );
- listener.apply( eventEmitter,
Array.prototype.slice.call( arguments, 0 ) );
- };
- return this.on( event, listenerWrapper );
-};
-
-/**
- * Remove a specific listener from a specific event.
- *
- * @param {string} event Type of event to remove listener from
- * @param {Function|string} [method] Listener to remove. Must be in the same
form as was passed
- * to "on". Omit to remove all listeners.
- * @param {Object} [context=null] Context object function or method call
- * @chainable
- * @throws {Error} Listener argument is not a function or a valid method name
- */
-oo.EventEmitter.prototype.off = function ( event, method, context ) {
- var i, bindings;
-
- if ( arguments.length === 1 ) {
- // Remove all bindings for event
- delete this.bindings[event];
- return this;
- }
-
- this.constructor.static.validateMethod( method, context );
-
- if ( !( event in this.bindings ) || !this.bindings[event].length ) {
- // No matching bindings
- return this;
- }
-
- // Default to null context
- if ( arguments.length < 3 ) {
- context = null;
- }
-
- // Remove matching handlers
- bindings = this.bindings[event];
- i = bindings.length;
- while ( i-- ) {
- if ( bindings[i].method === method && bindings[i].context ===
context ) {
- bindings.splice( i, 1 );
- }
- }
-
- // Cleanup if now empty
- if ( bindings.length === 0 ) {
- delete this.bindings[event];
- }
- return this;
-};
-
-/**
- * Emit an event.
- *
- * TODO: Should this be chainable? What is the usefulness of the boolean
- * return value here?
- *
- * @param {string} event Type of event
- * @param {Mixed} args First in a list of variadic arguments passed to event
handler (optional)
- * @return {boolean} If event was handled by at least one listener
- */
-oo.EventEmitter.prototype.emit = function ( event ) {
- var i, len, binding, bindings, args, method;
-
- if ( event in this.bindings ) {
- // Slicing ensures that we don't get tripped up by event
handlers that add/remove bindings
- bindings = this.bindings[event].slice();
- args = Array.prototype.slice.call( arguments, 1 );
- for ( i = 0, len = bindings.length; i < len; i++ ) {
- binding = bindings[i];
- if ( typeof binding.method === 'string' ) {
- // Lookup method by name (late binding)
- method = binding.context[ binding.method ];
- } else {
- method = binding.method;
+ /**
+ * Validate a function or method call in a context
+ *
+ * For a method name, check that it names a function in the context
object
+ *
+ * @private
+ * @param {Function|string} method Function or method name
+ * @param {Mixed} context The context of the call
+ * @throws {Error} A method name is given but there is no context
+ * @throws {Error} In the context object, no property exists with the
given name
+ * @throws {Error} In the context object, the named property is not a
function
+ */
+ function validateMethod( method, context ) {
+ // Validate method and context
+ if ( typeof method === 'string' ) {
+ // Validate method
+ if ( context === undefined || context === null ) {
+ throw new Error( 'Method name "' + method + '"
has no context.' );
}
- method.apply(
- binding.context,
- binding.args ? binding.args.concat( args ) :
args
- );
+ if ( !( method in context ) ) {
+ // Technically the method does not need to
exist yet: it could be
+ // added before call time. But this probably
signals a typo.
+ throw new Error( 'Method not found: "' + method
+ '"' );
+ }
+ if ( typeof context[method] !== 'function' ) {
+ // Technically the property could be replaced
by a function before
+ // call time. But this probably signals a typo.
+ throw new Error( 'Property "' + method + '" is
not a function' );
+ }
+ } else if ( typeof method !== 'function' ) {
+ throw new Error( 'Invalid callback. Function or method
name expected.' );
}
- return true;
}
- return false;
-};
-/**
- * Connect event handlers to an object.
- *
- * @param {Object} context Object to call methods on when events occur
- * @param
{Object.<string,string>|Object.<string,Function>|Object.<string,Array>} methods
List of
- * event bindings keyed by event name containing either method names,
functions or arrays containing
- * method name or function followed by a list of arguments to be passed to
callback before emitted
- * arguments
- * @chainable
- */
-oo.EventEmitter.prototype.connect = function ( context, methods ) {
- var method, args, event;
+ /* Methods */
- for ( event in methods ) {
- method = methods[event];
- // Allow providing additional args
- if ( Array.isArray( method ) ) {
- args = method.slice( 1 );
- method = method[0];
+ /**
+ * Add a listener to events of a specific event.
+ *
+ * The listener can be a function or the string name of a method; if
the latter, then the
+ * name lookup happens at the time the listener is called.
+ *
+ * @param {string} event Type of event to listen to
+ * @param {Function|string} method Function or method name to call when
event occurs
+ * @param {Array} [args] Arguments to pass to listener, will be
prepended to emitted arguments
+ * @param {Object} [context=null] Context object for function or method
call
+ * @throws {Error} Listener argument is not a function or a valid
method name
+ * @chainable
+ */
+ oo.EventEmitter.prototype.on = function ( event, method, args, context
) {
+ var bindings;
+
+ validateMethod( method, context );
+
+ if ( hasOwn.call( this.bindings, event ) ) {
+ bindings = this.bindings[event];
} else {
- args = [];
+ // Auto-initialize bindings list
+ bindings = this.bindings[event] = [];
}
// Add binding
- this.on( event, method, args, context );
- }
- return this;
-};
+ bindings.push( {
+ method: method,
+ args: args,
+ context: ( arguments.length < 4 ) ? null : context
+ } );
+ return this;
+ };
-/**
- * Disconnect event handlers from an object.
- *
- * @param {Object} context Object to disconnect methods from
- * @param
{Object.<string,string>|Object.<string,Function>|Object.<string,Array>}
[methods] List of
- * event bindings keyed by event name. Values can be either method names or
functions, but must be
- * consistent with those used in the corresponding call to "connect".
- * @chainable
- */
-oo.EventEmitter.prototype.disconnect = function ( context, methods ) {
- var i, event, bindings;
+ /**
+ * Adds a one-time listener to a specific event.
+ *
+ * @param {string} event Type of event to listen to
+ * @param {Function} listener Listener to call when event occurs
+ * @chainable
+ */
+ oo.EventEmitter.prototype.once = function ( event, listener ) {
+ var eventEmitter = this,
+ listenerWrapper = function () {
+ eventEmitter.off( event, listenerWrapper );
+ listener.apply( eventEmitter,
Array.prototype.slice.call( arguments, 0 ) );
+ };
+ return this.on( event, listenerWrapper );
+ };
- if ( methods ) {
- // Remove specific connections to the context
- for ( event in methods ) {
- this.off( event, methods[event], context );
+ /**
+ * Remove a specific listener from a specific event.
+ *
+ * @param {string} event Type of event to remove listener from
+ * @param {Function|string} [method] Listener to remove. Must be in the
same form as was passed
+ * to "on". Omit to remove all listeners.
+ * @param {Object} [context=null] Context object function or method call
+ * @chainable
+ * @throws {Error} Listener argument is not a function or a valid
method name
+ */
+ oo.EventEmitter.prototype.off = function ( event, method, context ) {
+ var i, bindings;
+
+ if ( arguments.length === 1 ) {
+ // Remove all bindings for event
+ delete this.bindings[event];
+ return this;
}
- } else {
- // Remove all connections to the context
- for ( event in this.bindings ) {
- bindings = this.bindings[event];
- i = bindings.length;
- while ( i-- ) {
- // bindings[i] may have been removed by the
previous step's
- // this.off so check it still exists
- if ( bindings[i] && bindings[i].context ===
context ) {
- this.off( event, bindings[i].method,
context );
+
+ validateMethod( method, context );
+
+ if ( !( event in this.bindings ) ||
!this.bindings[event].length ) {
+ // No matching bindings
+ return this;
+ }
+
+ // Default to null context
+ if ( arguments.length < 3 ) {
+ context = null;
+ }
+
+ // Remove matching handlers
+ bindings = this.bindings[event];
+ i = bindings.length;
+ while ( i-- ) {
+ if ( bindings[i].method === method &&
bindings[i].context === context ) {
+ bindings.splice( i, 1 );
+ }
+ }
+
+ // Cleanup if now empty
+ if ( bindings.length === 0 ) {
+ delete this.bindings[event];
+ }
+ return this;
+ };
+
+ /**
+ * Emit an event.
+ *
+ * TODO: Should this be chainable? What is the usefulness of the boolean
+ * return value here?
+ *
+ * @param {string} event Type of event
+ * @param {Mixed} args First in a list of variadic arguments passed to
event handler (optional)
+ * @return {boolean} If event was handled by at least one listener
+ */
+ oo.EventEmitter.prototype.emit = function ( event ) {
+ var i, len, binding, bindings, args, method;
+
+ if ( event in this.bindings ) {
+ // Slicing ensures that we don't get tripped up by
event handlers that add/remove bindings
+ bindings = this.bindings[event].slice();
+ args = Array.prototype.slice.call( arguments, 1 );
+ for ( i = 0, len = bindings.length; i < len; i++ ) {
+ binding = bindings[i];
+ if ( typeof binding.method === 'string' ) {
+ // Lookup method by name (late binding)
+ method = binding.context[
binding.method ];
+ } else {
+ method = binding.method;
+ }
+ method.apply(
+ binding.context,
+ binding.args ? binding.args.concat(
args ) : args
+ );
+ }
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Connect event handlers to an object.
+ *
+ * @param {Object} context Object to call methods on when events occur
+ * @param
{Object.<string,string>|Object.<string,Function>|Object.<string,Array>} methods
List of
+ * event bindings keyed by event name containing either method names,
functions or arrays containing
+ * method name or function followed by a list of arguments to be
passed to callback before emitted
+ * arguments
+ * @chainable
+ */
+ oo.EventEmitter.prototype.connect = function ( context, methods ) {
+ var method, args, event;
+
+ for ( event in methods ) {
+ method = methods[event];
+ // Allow providing additional args
+ if ( Array.isArray( method ) ) {
+ args = method.slice( 1 );
+ method = method[0];
+ } else {
+ args = [];
+ }
+ // Add binding
+ this.on( event, method, args, context );
+ }
+ return this;
+ };
+
+ /**
+ * Disconnect event handlers from an object.
+ *
+ * @param {Object} context Object to disconnect methods from
+ * @param
{Object.<string,string>|Object.<string,Function>|Object.<string,Array>}
[methods] List of
+ * event bindings keyed by event name. Values can be either method
names or functions, but must be
+ * consistent with those used in the corresponding call to "connect".
+ * @chainable
+ */
+ oo.EventEmitter.prototype.disconnect = function ( context, methods ) {
+ var i, event, bindings;
+
+ if ( methods ) {
+ // Remove specific connections to the context
+ for ( event in methods ) {
+ this.off( event, methods[event], context );
+ }
+ } else {
+ // Remove all connections to the context
+ for ( event in this.bindings ) {
+ bindings = this.bindings[event];
+ i = bindings.length;
+ while ( i-- ) {
+ // bindings[i] may have been removed by
the previous step's
+ // this.off so check it still exists
+ if ( bindings[i] && bindings[i].context
=== context ) {
+ this.off( event,
bindings[i].method, context );
+ }
}
}
}
- }
- return this;
-};
+ return this;
+ };
-/**
- * Validate a function or method call in a context
- *
- * For a method name, check that it names a function in the context object
- *
- * @static
- * @param {Function|string} method Function or method name
- * @param {Mixed} context The context of the call
- * @throws {Error} A method name is given but there is no context
- * @throws {Error} In the context object, no property exists with the given
name
- * @throws {Error} In the context object, the named property is not a function
- */
-oo.EventEmitter.static.validateMethod = function ( method, context ) {
- // Validate method and context
- if ( typeof method === 'string' ) {
- // Validate method
- if ( context === undefined || context === null ) {
- throw new Error( 'Method name "' + method + '" has no
context.' );
- }
- if ( !( method in context ) ) {
- // Technically the method does not need to exist yet:
it could be
- // added before call time. But this probably signals a
typo.
- throw new Error( 'Method not found: "' + method + '"' );
- }
- if ( typeof context[method] !== 'function' ) {
- // Technically the property could be replaced by a
function before
- // call time. But this probably signals a typo.
- throw new Error( 'Property "' + method + '" is not a
function' );
- }
- } else if ( typeof method !== 'function' ) {
- throw new Error( 'Invalid callback. Function or method name
expected.' );
- }
-};
+}() );
--
To view, visit https://gerrit.wikimedia.org/r/155589
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I89056c47fa14af3d7ba8cd326bcd84302353fe0c
Gerrit-PatchSet: 1
Gerrit-Project: oojs/core
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits