From b735dcb31251a31f411adf119180b83547f3ee32 Mon Sep 17 00:00:00 2001 From: TJ Holowaychuk Date: Thu, 22 Dec 2011 05:03:16 -0800 Subject: [PATCH] Added guillermo's EventEmitter. Closes #16 --- lib/events.js | 171 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 25 deletions(-) diff --git a/lib/events.js b/lib/events.js index 91e6bf4e6..b8202ad93 100644 --- a/lib/events.js +++ b/lib/events.js @@ -1,55 +1,176 @@ -/*! - * EventEmitter - * Copyright (c) 2011 TJ Holowaychuk - * MIT Licensed +/** + * Module exports. + */ + +/** + * Check if `obj` is an array. */ -// TODO: own library, since tons of my libs use this :D +function isArray(obj) { + return '[object Array]' == {}.toString.call(obj); +} /** - * Slice reference. + * Event emitter constructor. + * + * @api public. */ -var slice = [].slice; +function EventEmitter(){}; /** - * EventEmitter. + * Adds a listener. + * + * @api public */ -function EventEmitter() { - this.callbacks = {}; +EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; }; +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + /** - * Listen on the given `event` with `fn`. + * Adds a volatile listener. * - * @param {String} event - * @param {Function} fn + * @api public */ -EventEmitter.prototype.on = function(event, fn){ - (this.callbacks[event] = this.callbacks[event] || []) - .push(fn); +EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + return this; }; /** - * Emit `event` with the given args. + * Removes a listener. * - * @param {String} event - * @param {Mixed} ... + * @api public */ -EventEmitter.prototype.emit = function(event){ - var args = slice.call(arguments, 1) - , callbacks = this.callbacks[event]; +EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; - if (callbacks) { - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); + if (isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; } } return this; }; + +/** + * Removes all listeners for an event. + * + * @api public + */ + +EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; +}; + +/** + * Gets all listeners for a certain event. + * + * @api publci + */ + +EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; +}; + +/** + * Emits an event. + * + * @api public + */ + +EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = [].slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; +}; \ No newline at end of file