UNPKG

emitter-b

Version:

An enhanced EventEmitter with extra methods for detecting whether an event has any handlers or not for efficient event handler attachment.

213 lines (184 loc) 7.76 kB
var EventEmitter = require('events').EventEmitter var proto = require("proto") module.exports = proto(EventEmitter, function(superclass) { this.init = function() { superclass.apply(this, arguments) this.ifonHandlers = {} this.ifoffHandlers = {} this.ifonAllHandlers = [] this.ifoffAllHandlers = [] } // callback will be triggered immediately if there is already a listener attached, or // callback will be triggered when the first listener for the event is added // (regardless of whether its done through on or once) // parameters can be: // event, callback - attach an ifon handler for the passed event // callback - attach an ifon handler for all events this.ifon = function(event, callback) { if(event instanceof Function) { // event not passed, only a callback callback = event // fix the argument for(var eventName in this._events) { if(this.listeners(eventName).length > 0) { callback(eventName) } } } else if(this.listeners(event).length > 0) { callback(event) } addHandlerToList(this, 'ifonHandlers', event, callback) } // removes either: // removeIfon() - all ifon handlers (if no arguments are passed), or // removeIfon(event) - all ifon handlers for the passed event, or // removeIfon(callback) - the passed ifon-all handler (if the first parameter is the callback) // removeIfon(event, callback) - the specific passed callback for the passed event this.removeIfon = function(event, callback) { removeFromHandlerList(this, 'ifonHandlers', event, callback) } // callback will be triggered when the last listener for the 'click' event is removed (will not trigger immediately if there is no event listeners on call of ifoff) // (regardless of whether this is done through removeListener or as a result of 'once' being fulfilled) // parameters can be: // event, callback - attach an ifoff handler for the passed event // callback - attach an ifoff handler for all events this.ifoff = function(event, callback) { addHandlerToList(this, 'ifoffHandlers', event, callback) } // removes either: // removeIfoff() - all ifoff handlers (if no arguments are passed), or // removeIfoff(event) - all ifoff handlers for the passed event, or // removeIfoff(callback) - the passed ifoff-all handler (if the first parameter is the callback) // removeIfoff(event, callback) - the specific passed callback for the passed event this.removeIfoff = function(event, callback) { removeFromHandlerList(this, 'ifoffHandlers', event, callback) } // emitter is the emitter to proxy handler binding to // options can have one of the following properties: // only - an array of events to proxy // except - an array of events to *not* proxy this.proxy = function(emitter, options) { if(options === undefined) options = {} if(options.except !== undefined) { var except = arrayToMap(options.except) var handleIt = function(event){return !(event in except)} } else if(options.only !== undefined) { var only = arrayToMap(options.only) var handleIt = function(event){return event in only} } else { var handleIt = function(){return true} } var that = this, handler; this.ifon(function(event) { if(handleIt(event)) { emitter.on(event, handler = function() { that.emit.apply(that, [event].concat(Array.prototype.slice.call(arguments))) }) } }) this.ifoff(function(event) { if(handleIt(event)) emitter.off(event, handler) }) } /*override*/ this.on = this.addListener = function(event, callback) { var triggerIfOn = this.listeners(event).length === 0 superclass.prototype.on.apply(this,arguments) if(triggerIfOn) triggerIfHandlers(this, 'ifonHandlers', event) } /*override*/ this.off = this.removeListener = function(event, callback) { var triggerIfOff = this.listeners(event).length === 1 superclass.prototype.removeListener.apply(this,arguments) if(triggerIfOff) triggerIfHandlers(this, 'ifoffHandlers', event) } /*override*/ this.removeAllListeners = function(event) { var triggerIfOffForEvents = [] if(event !== undefined) { if(this.listeners(event).length > 0) { triggerIfOffForEvents.push(event) } } else { for(var event in this._events) { if(this.listeners(event).length > 0) { triggerIfOffForEvents.push(event) } } } superclass.prototype.removeAllListeners.apply(this,arguments) for(var n=0; n<triggerIfOffForEvents.length; n++) { triggerIfHandlers(this, 'ifoffHandlers', triggerIfOffForEvents[n]) } } }) // triggers the if handlers from the normal list and the "all" list function triggerIfHandlers(that, handlerListName, event) { triggerIfHandlerList(that[handlerListName][event], event) triggerIfHandlerList(that[normalHandlerToAllHandlerProperty(handlerListName)], event) } // triggers the if handlers from a specific list // ya these names are confusing, sorry : ( function triggerIfHandlerList(handlerList, event) { if(handlerList !== undefined) { for(var n=0; n<handlerList.length; n++) { handlerList[n](event) } } } function addHandlerToList(that, handlerListName, event, callback) { if(event instanceof Function) { // correct arguments callback = event event = undefined } if(event !== undefined && callback !== undefined) { var handlerList = that[handlerListName][event] if(handlerList === undefined) { handlerList = that[handlerListName][event] = [] } handlerList.push(callback) } else { that[normalHandlerToAllHandlerProperty(handlerListName)].push(callback) } } function removeFromHandlerList(that, handlerListName, event, callback) { if(event instanceof Function) { // correct arguments callback = event event = undefined } if(event !== undefined && callback !== undefined) { removeCallbackFromList(that[handlerListName][event], callback) } else if(event !== undefined) { delete that[handlerListName][event] } else if(callback !== undefined) { var allHandlerListName = normalHandlerToAllHandlerProperty(handlerListName) removeCallbackFromList(that[allHandlerListName], callback) } else { var allHandlerListName = normalHandlerToAllHandlerProperty(handlerListName) that[handlerListName] = {} that[allHandlerListName] = [] } } function normalHandlerToAllHandlerProperty(handlerListName) { if(handlerListName === 'ifonHandlers') return 'ifonAllHandlers' if(handlerListName === 'ifoffHandlers') return 'ifoffAllHandlers' } function removeCallbackFromList(list, callback) { var index = list.indexOf(callback) list.splice(index,1) } function getTrace() { try { throw new Error() } catch(e) { return e } } // turns an array of values into a an object where those values are all keys that point to 'true' function arrayToMap(array) { var result = {} array.forEach(function(v) { result[v] = true }) return result }