define(function(require, exports, module) { /** * When an EventEmitter instance experiences an error, the typical action is to * emit an 'error' event. Error events are treated as a special case. If there * is no listener for it, then the default action is for the error to throw. * * All EventEmitters emit the event 'newListener' when new listeners are added. * * @name events.EventEmitter * @api public * * ```javascript * * // create an event emitter * var emitter = new EventEmitter(); * ``` */ var EventEmitter = exports.EventEmitter = function() {}; var toString = Object.prototype.toString; var isArray = Array.isArray || function(obj) { return toString.call(obj) === '[object Array]'; }; /** * By default EventEmitters will print a warning if more than 10 listeners are * added for a particular event. This is a useful default which helps finding * memory leaks. Obviously not all Emitters should be limited to 10. This * function allows that to be increased. Set to zero for unlimited. * * @name emitter.setMaxListeners(n) * @param {Number} n - The maximum number of listeners * @api public */ // By default EventEmitters will print a warning if more than // 10 listeners are added to it. This is a useful default which // helps finding memory leaks. // // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. var defaultMaxListeners = 10; EventEmitter.prototype.setMaxListeners = function(n) { if (!this._events) this._events = {}; this._events.maxListeners = n; }; /** * Execute each of the listeners in order with the supplied arguments. * * @name emitter.emit(event, [arg1], [arg2], [...]) * @param {String} event - The event name/id to fire * @api public */ EventEmitter.prototype.emit = function(type) { if (!this._events) return; if (arguments[2] === true) throw new Error("Please use emit.sticky() instead of passing sticky=true for event: " + type); var handler = this._events[type]; if (!handler) return; var returnValue; if (typeof handler == 'function') { switch (arguments.length) { // fast cases case 1: return handler.call(this); case 2: return handler.call(this, arguments[1]); case 3: return handler.call(this, arguments[1], arguments[2]); // slower default: var args = Array.prototype.slice.call(arguments, 1); returnValue = handler.apply(this, args); } } else if (isArray(handler)) { var args = Array.prototype.slice.call(arguments, 1); var listeners = handler.slice(), temp; for (var i = 0, l = listeners.length; i < l; i++) { temp = listeners[i].apply(this, args); if (temp !== undefined) returnValue = temp; } } return returnValue; }; /** * Adds a listener to the end of the listeners array for the specified event. * * @name emitter.on(event, listener) | emitter.addListener(event, listener) * @param {String} event - The event name/id to listen for * @param {Function} listener - The function to bind to the event * @api public * * ```javascript * session.on('change', function (userCtx) { * console.log('session changed!'); * }); * ``` */ // EventEmitter is defined in src/node_events.cc // EventEmitter.prototype.emit() is also defined there. EventEmitter.prototype.addListener = function(type, listener, plugin) { if ('function' !== typeof listener) { throw new Error('addListener only takes instances of Function'); } if (!this._events) this._events = {}; var eventList = this._events[type]; if (!eventList) { // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; } else if (isArray(eventList)) { // if (eventList.indexOf(listener) != -1) // return console.warn("adding same listener twice", type) // Check for listener leak if (!eventList.warned) { var m; if (this._events.maxListeners !== undefined) { m = this._events.maxListeners; } else { m = defaultMaxListeners; } if (m && m > 0 && eventList.length > m) { eventList.warned = true; console.error('warning: possible EventEmitter memory ' + 'leak detected. " + eventList.length + " listeners of type "' + type + '" added. ' + 'Use emitter.setMaxListeners() to increase limit.' ); console.trace(); } } // If we've already got an array, just append. eventList.push(listener); } else { // if (eventList == listener) // return console.warn("adding same listener twice", type); // Adding the second element, need to change to array. this._events[type] = [eventList, listener]; } if (type != "newListener") this.emit('newListener', type, listener); plugin && plugin.addEvent(this, type, listener); return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; /** * Adds a one time listener for the event. This listener is invoked only the * next time the event is fired, after which it is removed. * * @name emitter.once(event, listener) * @param {String} event- The event name/id to listen for * @param {Function} listener - The function to bind to the event * @api public * * ```javascript * db.once('unauthorized', function (req) { * // this event listener will fire once, then be unbound * }); * ``` */ EventEmitter.prototype.once = function(type, listener) { var self = this; var wrapped = function() { self.removeListener(type, listener); return listener.apply(self, arguments); } wrapped.listener = listener; self.on(type, wrapped); return this; }; /** * Remove a listener from the listener array for the specified event. Caution: * changes array indices in the listener array behind the listener. * * @name emitter.removeListener(event, listener) * @param {String} event - The event name/id to remove the listener from * @param {Function} listener - The listener function to remove * @api public * * ```javascript * var callback = function (init) { * console.log('duality app loaded'); * }; * devents.on('init', callback); * // ... * devents.removeListener('init', callback); * ``` */ EventEmitter.prototype.removeListener = function(type, listener) { if ('function' !== typeof listener) { throw new Error('removeListener only takes instances of Function'); } // does not use listeners(), so no side effect of creating _events[type] if (!this._events || !this._events[type]) return this; var list = this._events[type]; if (isArray(list)) { if (!list.some(function(l, i){ if ((l.listener || l) == listener) { list.splice(i, 1); return true; } })) return this; if (list.length === 0) delete this._events[type]; } else if ((this._events[type].listener || this._events[type]) === listener) { delete this._events[type]; } if (type != "removeListener") this.emit('removeListener', type, listener); return this; }; EventEmitter.prototype.off = EventEmitter.prototype.removeListener; /** * Removes all listeners, or those of the specified event. * * @name emitter.removeAllListeners([event]) * @param {String} event - Event name/id to remove all listeners for (optional) * @api public */ EventEmitter.prototype.removeAllListeners = function(type) { // does not use listeners(), so no side effect of creating _events[type] if (type && this._events && this._events[type]) this._events[type] = null; return this; }; /** * Returns an array of listeners for the specified event. This array can be * manipulated, e.g. to remove listeners. * * @name emitter.listeners(event) * @param {String} events - The event name/id to return listeners for * @api public * * ```javascript * session.on('change', function (stream) { * console.log('session changed'); * }); * console.log(util.inspect(session.listeners('change'))); // [ [Function] ] * ``` */ EventEmitter.prototype.listeners = function(type) { if (!this._events) this._events = {}; if (!this._events[type]) this._events[type] = []; if (!isArray(this._events[type])) { this._events[type] = [this._events[type]]; } return this._events[type]; }; /** * @name emitter Event: 'newListener' * * This event is emitted any time someone adds a new listener. * * ```javascript * emitter.on('newListener', function (event, listener) { * // new listener added * }); * ``` */ });