Last active
August 29, 2015 14:05
-
-
Save jpiccari/9cc2a46e4d39eb44bb2f to your computer and use it in GitHub Desktop.
Reincarnation of EventEmitter, stripped down for speed and size. Still supports filters. <1k minified
This file contains hidden or 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
define( | |
'Eventable', | |
function() { | |
function extend(dest, src) { | |
for (var key in src) { | |
if (src.hasOwnProperty(key)) { | |
dest[key] = src[key]; | |
} | |
} | |
return dest; | |
} | |
/** | |
* Parses and iterates an events string | |
* @param {object} context - Context to be passed to the iterator function | |
* @param {string} events - Events string to be parsed and iterated | |
* @param {function} fn - Iterator function called for each event topic | |
* @param {mixed} args - Additional arguments to be passed to the iterator | |
* @returns {object} The object passed as the context | |
*/ | |
function iterateEventsString(context, events, fn, args) { | |
events = events.split(' '); | |
var filters; | |
while ((filters = events.pop())) { | |
filters = filters.split('.'); | |
fn(context, filters.shift(), filters, args); | |
} | |
return context; | |
} | |
/** | |
* Checks if all filters listed in target also exist in test. | |
* @param {array} target - List of filters to match | |
* @param {array} target - List of filters to test | |
* | |
* NOTE: Don't be fooled by the naive algorithm below, this method is | |
* highly optimized for performance and size (minified). | |
* @see http://jsperf.com/match-array-subsets | |
*/ | |
function matchFilters(target, test) { | |
if (target.length > test.length) { | |
return false; | |
} | |
var filter, | |
i; | |
for (i = 0; (filter = target[i]); i++) { | |
if (test.indexOf(filter) === -1) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Removes an event handler | |
* @param {string} events - Event topics (separated by spaces) and filters (separated by dots) | |
* @returns {object} The object from which the events were removed | |
*/ | |
function off(events) { | |
return iterateEventsString(this, events, _off); | |
} | |
function _off(context, topic, filters) { | |
var handlers = context._topics[topic], | |
handler, | |
i; | |
if (!filters.length) { | |
handlers.length = 0; | |
} else { | |
for (i = 0; (handler = handlers[i]); i++) { | |
if (matchFilters(filters, handler.filters)) { | |
handlers.splice(i--, 1); | |
} | |
} | |
} | |
} | |
/** | |
* Attaches an event handler | |
* @param {string} events - Event topics (separated by spaces) and filters (separated by dots) | |
* @param {function} fn - Handler to be called when the event is triggered | |
* @returns {object} The object from which the events are attached | |
*/ | |
function on(events, fn) { | |
return iterateEventsString(this, events, _on, fn); | |
} | |
function _on(context, topic, filters, fn) { | |
var topics = context._topics; | |
(topics[topic] = topics[topic] || []).push({ | |
filters: filters, | |
fn: fn | |
}); | |
} | |
/** | |
* Attaches an event handler to be used only once | |
* @param {string} events - Event topics (separated by spaces) and filters (separated by dots) | |
* @param {function} fn - Handler to be called when the event is triggered | |
* @returns {object} The object from which the events are attached | |
*/ | |
function one(events, fn) { | |
return iterateEventsString(this, events, _one, fn); | |
} | |
function _one(context, topic, filters, fn) { | |
_on(context, topic, filters, function() { | |
context.off([topic].concat(filters).join('.')); | |
fn.apply(context, arguments); | |
}); | |
} | |
/** | |
* Triggers all handlers associated with an event topic | |
* @param {string} topic - The name of the event topic to trigger | |
* @returns {object} The object from which the events are triggered | |
*/ | |
function trigger(events) { | |
return iterateEventsString(this, events, _trigger, Array.prototype.slice.call(arguments, 1)); | |
} | |
function _trigger(context, topic, filters, args) { | |
var handlers, | |
i; | |
if ((handlers = context._topics[topic])) { | |
for (i = 0; (handler = handlers[i]); i++) { | |
handler.fn.apply(context, args); | |
} | |
} | |
} | |
/** | |
* Mixin for the eventing behavior | |
* @param {object} obj - Object to have on/one/off/trigger mixed in | |
* @returns {object} The updated object | |
*/ | |
return function(obj) { | |
if (obj !== Object(obj)) { | |
throw new TypeError(); | |
} | |
return extend(obj, { | |
_topics: obj._topics || {}, | |
off: off, | |
on: on, | |
one: one, | |
trigger: trigger | |
}); | |
}; | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment