bar
Version:
Node.js framework for building large modular web applications
285 lines (235 loc) • 8.59 kB
JavaScript
;!function(exports, undefined) {
exports.EventEmitter2 = EventEmitter2;
function EventEmitter2(conf) {
conf = conf || {};
if (conf.delimiter === '*') {
throw new Error('The event can not be delimited by the "*" (wild-card) character.');
}
this._caseSensitive = conf.caseSensitive;
this._delimiter = conf.delimiter || '.';
this._maxListeners = conf.maxListeners === 0 ? 0 : conf.maxListeners || 10;
this._events = {};
};
EventEmitter2.prototype.addListener = function(event, listener) {
var name, ns = this._events;
if(event[event.length-1] === this._delimiter || event[0] === this._delimiter) {
this.nameError();
}
if(this._caseSensitive === true) {
event = event.toLowerCase();
}
// Signal that a new listener is being added.
this.emit('newListener', event, listener);
// the name has a delimiter
if (~event.indexOf(this._delimiter)) {
//split the name into an array
name = event.split(this._delimiter);
// continue to build out additional namespaces and attach the listener to them
for(var i = 0, l = name.length; i < l; i++) {
// get the namespace
ns = ns[name[i]] || (ns[name[i]] = {});
}
}
// if the name does not have a delimiter
else {
// get a handle to the event
ns = ns[event] || (ns[event] = {});
}
if (ns._listeners && ns._listeners.length === this.maxListeners) {
this.emit('maxListeners', event);
return;
}
ns._listeners ? ns._listeners.push(listener) : ns._listeners = [listener];
};
EventEmitter2.prototype.on = EventEmitter2.prototype.addListener;
EventEmitter2.prototype.once = function(event, listener) {
this.many(event, 1, listener);
};
EventEmitter2.prototype.many = function(event, ttl, listener) {
var self = this;
this.addListener(event, function() {
if(ttl-- == 0) {
self.removeListener(event, listener);
}
else {
listener.apply(null, arguments);
}
});
};
EventEmitter2.prototype.emit = function(event) {
if(event[event.length-1] === this._delimiter || event[0] === this._delimiter) {
this.nameError();
}
var self = this, args = arguments, i = 0, j = 0;
function invokeListeners(val) {
for (var k = 0, l = val._listeners.length; k < l; k++) {
val._listeners[k].apply(this, args);
}
return true;
}
// If there is a delimiter in the event name
if (~event.indexOf(this._delimiter) || event === '*') {
// Split the name into an array
name = event.split(this._delimiter);
var explore = [this._events],
invoked = false,
key = null;
for (i = 0; i < name.length; i++) {
//
// Iterate over the parts of the potentially namespaced
// event.
//
// emit('foo/*/bazz') ==> ['foo', '*', 'bazz']
//
var part = self._caseSensitive === true ? name[i].toLowerCase() : name[i];
var newSets = [];
for (j = 0; j < explore.length; j++) {
//
// Iterative "unkown" set exploration: Iterate over each "unknown"
// set of objects in the events tree. If a wildcard is discovered,
// append that object to the unknown set and continue exploration.
//
var ns = explore[j];
if (i === name.length - 1) {
//
// Then if we are at the end of the iteration
// invoke all of the listeners, if not, continue
// iterating deeper in the object
//
if (part === '*') {
for (key in ns) {
if (ns.hasOwnProperty(key)) {
//
// Remark: This could cause some collisions for `_listeners`.
//
if (ns[key] && ns[key]._listeners) {
invokeListeners(ns[key]);
}
}
}
invoked = true;
}
else {
if (ns[part] && ns[part]._listeners && invokeListeners(ns[part])) {
invoked = true;
}
else if (ns['*'] && ns['*']._listeners && invokeListeners(ns['*'])) {
invoked = true;
}
}
}
else {
if (part !== '*') {
if (!ns[part] && !ns['*']) {
//
// If it's not a wild card and there isn't a wild
// card stored and the exact key isn't at the
// next step of the events object, break out
// of the loop and end evaluation.
//
continue;
}
if (ns[part]) {
//
// If it's not a wild card, but there is an exact
// match for this part of the namespaced event.
//
if (ns['*']) {
newSets.push(ns['*']);
}
explore[j] = explore[j][part];
}
else if (ns['*']) {
//
// If the part of the namespaced event is not a wildcard,
// but the set we are currently exploring has a wildcard
// at this level, nest deeper for that particular set.
//
explore[j] = explore[j]['*'];
if (ns['*'] && ns['*']._listeners && invokeListeners(ns['*'])) {
invoked = true;
}
}
}
else {
//
// Otherwise, this part of the namespaced event is a 'wildcard',
// in which case, we iterate over the keys of the current set,
// and add those objects to the set to be added to the "unknown" set
// after this level of exploration has completed.
//
for (key in ns) {
if (ns.hasOwnProperty(key)) {
newSets.push(ns[key]);
}
}
if (ns['*'] && ns['*']._listeners && invokeListeners(ns['*'])) {
invoked = true;
}
explore.splice(j, 1);
}
}
}
if (newSets.length) {
//
// If this level of exploration has yielded any new sets
// to be explored, then concatenate those sets to the "unknown" sets.
//
explore = explore.concat(newSets);
}
}
return invoked;
}
// if the name does not have a delimiter
else if (this._events[event] && this._events[event]._listeners) {
var listeners = this._events[event]._listeners;
// fire off each of them
for(i = 0, l = listeners.length; i < l; i++) {
listeners[i].apply(this, args);
}
}
// if this emitting event was never registerd, but a wildcard was
else if (this._events['*'] && this._events['*']._listeners){
invoked = invokeListeners(this._events['*']);
}
return true;
};
EventEmitter2.prototype.removeListener = function(event, listener) {
var name = event.split(this._delimiter),
_events = this._events;
event = name.pop();
for (var i = 0, len = name.length; i < len; i++) {
_events = _events[name[i]];
if (!_events) return;
}
if(listener && _events[event] && _events[event]._listeners) {
// Make a reference to all the listeners for the event.
var listeners = _events[event]._listeners;
// Loop through and check the signatures to find the exact listener to remove.
for(var i = 0, l = listeners.length; i < l; i++) {
if(listener === listeners[i]) {
// Break out and return the removed listener.
return listeners.splice(i, 1);
}
}
}
else {
_events[event] = {};
}
};
EventEmitter2.prototype.removeAllListeners = function(){ this._events = {}; };
EventEmitter2.prototype.setMaxListeners = function(n) {
this.maxListeners = n;
};
EventEmitter2.prototype.listeners = function(event) {
if(this._events[event]) {
return this._events[event]._listeners;
}
else {
return false;
}
};
EventEmitter2.prototype.nameError = function() {
throw new Error('Name can\'t end or begin with the "' + this._delimiter + '" character, it\'s the delimiter.');
};
}(typeof exports === 'undefined' ? window : exports);