Skip to content

Instantly share code, notes, and snippets.

@RubaXa
Created January 19, 2014 06:56
Show Gist options
  • Save RubaXa/8501409 to your computer and use it in GitHub Desktop.
Save RubaXa/8501409 to your computer and use it in GitHub Desktop.
EventEmitter.js
/**
* @author RubaXa <trash@rubaxa.org>
* @license MIT
*/
(function (){
"use strict";
var _rspace = /\s+/;
function _getListeners(obj, name){
var list = obj.__emList;
name = name.toLowerCase();
if( list === void 0 ){
list = obj.__emList = {};
list[name] = [];
}
else if( list[name] === void 0 ){
list[name] = [];
}
return list[name];
}
/**
* @class Event
* @param {String} type
* @constructor
*/
function Event(type){
this.type = type.toLowerCase();
}
Event.fn = Event.prototype = {
constructor: Event,
isDefaultPrevented: function (){
},
preventDefault: function (){
this.isDefaultPrevented = function (){
return true;
};
}
};
/**
* @class EventEmitter
* @constructor
*/
var EventEmitter = function (){};
EventEmitter.fn = EventEmitter.prototype = {
constructor: EventEmitter,
__lego: function (){
/* dummy */
},
/**
* Attach an event handler function for one or more events to the selected elements.
* @param {String} events One or more space-separated event types.
* @param {Function} fn A function to execute when the event is triggered.
* @returns {EventEmitter}
*/
on: function (events, fn){
events = events.split(_rspace);
var n = events.length, list;
while( n-- ){
list = _getListeners(this, events[n]);
list.push(fn);
}
return this;
},
/**
* Remove an event handler.
* @param {String} [events] One or more space-separated event types.
* @param {Function} [fn] A handler function previously attached for the event(s).
* @returns {EventEmitter}
*/
off: function (events, fn){
if( events === void 0 ){
this.__emList = void 0;
}
else {
events = events.split(_rspace);
var n = events.length;
while( n-- ){
var list = _getListeners(this, events[n]), i = list.length, idx = -1;
if( arguments.length === 1 ){
list.splice(0, 1e5); // dirty hack
} else {
if( list.indexOf ){
idx = list.indexOf(fn);
} else {
while( i-- ){
if( list[i] === fn ){
idx = i;
break;
}
}
}
if( idx !== -1 ){
list.splice(idx, 1);
}
}
}
}
return this;
},
/**
* Execute all handlers attached to an element for an event
* @param {String} type One event types
* @param {Array} [args] An array of parameters to pass along to the event handler.
*/
emit: function (type, args){
var list = _getListeners(this, type), i = list.length;
type = 'on'+type.charAt(0).toUpperCase()+type.substr(1);
if( typeof this[type] === 'function' ){
this[type].apply(this, args);
}
if( i > 0 ){
args = [].concat(args);
while( i-- ){
list[i].apply(this, args);
}
}
},
/**
* Execute all handlers attached to an element for an event
* @param {String} type One event types
* @param {Array} [args] An array of additional parameters to pass along to the event handler.
* @returns {EventEmitter}
*/
trigger: function (type, args){
var evt = new Event(type);
evt.target = this;
this.emit(type, [evt].concat(args));
return this;
}
};
EventEmitter.apply = function (target){
target.on = EventEmitter.fn.on;
target.off = EventEmitter.fn.off;
target.emit = EventEmitter.fn.emit;
target.trigger = EventEmitter.fn.trigger;
};
// exports
EventEmitter.Event = Event;
if( typeof define === "function" && define.amd ){
define(function (){
return EventEmitter;
});
} else if( typeof module != "undefined" && module.exports ){
module.exports = EventEmitter;
}
else {
window.EventEmitter = EventEmitter;
}
})();
module('EventEmitter');
test('all', function (){
var log = [];
var emitter = new EventEmitter;
var fooPush = function (a, b){ log.push('foo:'+a+':'+b) };
emitter.on('foo', function (){ log.push('foo') });
emitter.on('bar', function (){ log.push('bar') });
emitter.on('foo', fooPush);
emitter.emit('foo', [1, 2]);
emitter.emit('bar');
emitter.emit('foo', [3, 4]);
equal(log.join('->'), 'foo:1:2->foo->bar->foo:3:4->foo');
log = [];
emitter.off('bar', void 0);
emitter.emit('bar', [3, 4]);
equal(log.join('->'), 'bar');
log = [];
emitter.off('bar');
emitter.off('foo', fooPush);
emitter.emit('bar', [3, 4]);
emitter.emit('foo', [5, 6]);
equal(log.join('->'), 'foo');
emitter.onFoo = function (a, b){
log.push(a + b);
};
emitter.off('foo');
emitter.emit('foo', [1, 1]);
equal(log.join('->'), 'foo->2');
});
test('foo + bar', function (){
var log = [];
var emitter = new EventEmitter;
emitter.on('foo bar', function (evt){
log.push(evt.type);
});
emitter.trigger('bar').trigger('foo');
equal(log.join('->'), 'bar->foo');
emitter.off('foo bar').trigger('bar').trigger('foo');
equal(log.join('->'), 'bar->foo');
});
test('foo, bar', function (){
var log = [];
var emitter = new EventEmitter;
emitter.on('foo', function (evt){ log.push(evt.type); });
emitter.on('bar', function (evt){ log.push(evt.type); });
emitter.trigger('bar').trigger('foo');
equal(log.join('->'), 'bar->foo');
emitter.off().trigger('bar').trigger('foo');
equal(log.join('->'), 'bar->foo');
});
test('trigger', function (){
var log = [];
var emitter = new EventEmitter;
emitter.on('foo', function (evt, a, b){
if( evt.isDefaultPrevented() ){
log.push('isDefaultPrevented');
log.push(a + b)
}
});
emitter.on('foo', function (evt){
evt.preventDefault();
log.push(evt.type);
});
emitter.trigger('foo', [1, 1]);
equal(log.join('->'), 'foo->isDefaultPrevented->2');
});
test('bench', function (){
var emitter = new EventEmitter;
var $emitter = $({}), i;
var ts = new Date;
for( i = 0; i < 1e4; i++ ){
emitter.trigger('foo', ["bar", "baz"]);
}
ts = new Date - ts;
var $ts = new Date;
for( i = 0; i < 1e4; i++ ){
$emitter.trigger('foo', ["bar", "baz"]);
}
$ts = new Date - $ts;
ok( $ts / ts > 5, 'RubaXa win: ' + ($ts / ts) );
console.log('jQuery.trigger: '+$ts+'ms');
console.log('EventEmitter.emit: '+ts+'ms');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment