Created
March 16, 2012 00:52
-
-
Save bengillies/2047984 to your computer and use it in GitHub Desktop.
event-base - A simple Class Helper and a simple Event Helper.
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>EventBase Demo</title> | |
</head> | |
<body> | |
<div class="foo"> | |
<a href="">I'm a link</a> | |
<button>I'm a button</button> | |
</div> | |
<script type="text/javascript" src="events.js"></script> | |
<script type="text/javascript" src="exemplar.js"></script> | |
<script style="display: block; white-space: pre; font-family: courier;"> | |
var Ev = new Emitter(); | |
var Foo = Exemplar({ | |
constructor: function(options) { | |
this.el = options && options.el; | |
Emitter.call(this, this.events); | |
}, | |
foo: function() { | |
console.log('foo called with', arguments); | |
}, | |
events: { | |
foo: 'foo', | |
'el.click button': 'buttonClick' | |
}, | |
buttonClick: function() { | |
console.log('button clicked with arguments', arguments); | |
} | |
}); | |
var Bar = Foo.extend({ | |
constructor: function(options) { | |
Foo.call(this, options); | |
}, | |
events: { | |
bar: 'bar' | |
}, | |
bar: function() { | |
console.log('bar called with', arguments); | |
} | |
}); | |
var foo = new Foo({ el: document.querySelector('.foo') }); | |
foo.on('el.click a', function(ev) { | |
console.log('link clicked with arguments', arguments); | |
ev.preventDefault(); | |
ev.stopPropagation(); | |
}); | |
var bar = new Bar(); | |
Ev.on('global-event', function() { | |
console.log('global event called with', arguments); | |
}); | |
Ev.on('call-foo', foo.foo); | |
foo.trigger('foo', 'arg1', 'arg2'); | |
bar.trigger('bar', 'A man walked into a bar'); | |
Ev.trigger('global-event', 'foo', 'bar', 'baz'); | |
Ev.trigger('call-foo', 'foo', 'bar', 'baz'); | |
Ev.body = document.body; | |
Ev.on('body.click', function() { | |
console.log('body clicked with arguments', arguments); | |
}); | |
</script> | |
</body> | |
</html> |
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
(function(global) { | |
"use strict"; | |
var hasOwn = Object.prototype.hasOwnProperty, | |
define = Object.defineProperty, | |
slice = Array.prototype.slice, | |
hideFn = function(fn) { return { enumerable: false, value: fn }; }; | |
/* | |
* Run the given function asynchronously. This ensures that all events are | |
* asynchronous, rather than a mixture of both, which can get confusing. | |
*/ | |
function makeAsync(fn) { | |
window.setTimeout(fn, 1); | |
} | |
function Emitter(eventsObject) { | |
var self = (this === global) ? {} : this, | |
events = {}; | |
/* | |
* create a function that can be used to bind to DOM elements and | |
* delegate to sub DOM elements. Set thisArg to self; | |
*/ | |
function createEventListener(evObj, callback) { | |
return function(e) { | |
var el = e.target, | |
nodes = evObj.subDOM ? slice.call(evObj.el | |
.querySelectorAll(evObj.subDOM)) : [el]; | |
for(;el && !~nodes.indexOf(el); el = el.parentNode || null); | |
if (el) callback.call(self, e); | |
}; | |
} | |
/* | |
* take an event string and split it into it's component parts | |
*/ | |
function parseEventString(str) { | |
var split = /([^\.]+)\.?(([^\s]+)?\s?(.+)?)?/.exec(str), | |
el = split.length > 2 ? self[split[1]] : undefined; | |
return { | |
el: el, // the sub-emitter if it exists | |
ev: el ? split[2] : split[1], // the whole event name | |
isDOM: el && el instanceof HTMLElement? true : false, | |
subDOM: split[4], // e.g. #foo.bar in 'el.click #foo.bar' | |
DOMEv: split[3] // e.g. click in 'el.click #foo.bar' | |
}; | |
} | |
/* | |
* Call all functions bound to the given event with the given arguments | |
* delegate to a sub emitter if necessary | |
*/ | |
define(self, 'trigger', hideFn(function(ev/*, ...args*/) { | |
var args = slice.call(arguments, 1), | |
evObj = parseEventString(ev); | |
if (evObj.el && !evObj.isDOM) { | |
args.unshift(evObj.ev); | |
evObj.el.trigger.apply(evObj.el, args); | |
} else if (evObj.el && evObj.isDOM) { | |
ev = document.createEvent('Event'); | |
ev.initEvent(evObj.DOMEv, true, true); | |
evObj.el.dispatchEvent(ev); | |
} else if (hasOwn.call(events, ev)) { | |
events[ev].forEach(function(fn) { | |
makeAsync(function() { fn.apply(self, args); }); | |
}); | |
} | |
})); | |
/* | |
* bind a function to the event | |
* delegate to a sub-emitter if called | |
*/ | |
define(self, 'on', hideFn(function(ev, callback) { | |
var evObj = parseEventString(ev), fn; | |
if (evObj.el && evObj.isDOM) { | |
fn = createEventListener(evObj, callback); | |
evObj.el.addEventListener(evObj.DOMEv, fn); | |
return fn; | |
} else if (evObj.el) { | |
evObj.el.on(ev, callback); | |
} else if (!hasOwn.call(events, ev)) { | |
events[ev] = events[ev].push ? events[ev].push(callback) : | |
[callback]; | |
} | |
return callback; | |
})); | |
/* | |
* unbind + unbind from a sub-emitter if called for | |
*/ | |
define(self, 'off', hideFn(function(ev, fn) { | |
var evObj = parseEventString(ev); | |
if (evObj.el && evObj.isDOM) { | |
evObj.el.removeEventListener(evObj.DOMEv, fn); | |
} else if (evObj.el) { | |
evObj.el.off(ev, fn); | |
} else if (!fn) { | |
delete events[evObj.ev]; | |
} else if (hasOwn.call(events, evObj.ev)) { | |
events[evObj.ev] = events[evObj.ev] | |
.filter(function(handler) { | |
return fn === handler ? false : true; | |
}); | |
} | |
})); | |
/* | |
* parse and bind everything in the given object at once | |
*/ | |
function loadEventsObject(ev) { | |
for (var key in ev) if (hasOwn.call(ev, key)) { | |
self.on(key, (typeof ev[key] === 'function') ? | |
ev[key] : self[ev[key]]); | |
} | |
} | |
loadEventsObject(eventsObject); | |
return self; | |
} | |
global.Emitter = Emitter; | |
}(window)); |
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
(function(exports) { | |
"use strict"; | |
/* | |
* Shallow copy properties in source to target | |
*/ | |
function extend(target, source) { | |
Object.getOwnPropertyNames(source).forEach(function(key) { | |
Object.defineProperty(target, key, | |
Object.GetOwnPropertyDescriptor(source, key)); | |
}); | |
return target; | |
} | |
/* | |
* Turn proto into a function exemplar, and add a function to allow easy | |
* subclassing of it. | |
*/ | |
function Exemplar(proto) { | |
// define a function for subclassing existing classes | |
function _extend(_proto) { | |
return function(obj) { | |
var _myClass = obj.hasOwnProperty('constructor') ? | |
obj.constructor : function() { | |
_proto.constructor.apply(this, arguments); | |
}; | |
_myClass.prototype = Object.create(_proto); | |
extend(_myClass.prototype, obj); | |
// allow the subclass to be subclassed | |
Object.defineProperty(_myClass, 'extend', { | |
value: _extend(_myClass.prototype), | |
enumerable: false | |
}); | |
return _myClass; | |
}; | |
} | |
// use it to subclass Object | |
return _extend(Object.prototype)(proto); | |
} | |
exports.Exemplar = Exemplar; | |
exports.extend = extend; | |
}(window)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment