Created
January 19, 2014 06:56
-
-
Save RubaXa/8501409 to your computer and use it in GitHub Desktop.
EventEmitter.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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