Skip to content

Instantly share code, notes, and snippets.

@jpiccari
Last active August 29, 2015 14:05
Show Gist options
  • Save jpiccari/9cc2a46e4d39eb44bb2f to your computer and use it in GitHub Desktop.
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
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