Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active December 17, 2015 14:19
Show Gist options
  • Save sukima/5623141 to your computer and use it in GitHub Desktop.
Save sukima/5623141 to your computer and use it in GitHub Desktop.
// Damn Simple Event Dispatcher
// ============================
//
// Used to extend your own objects to handle events outside of the global event
// model introduced in Titanium. This is a implementation copy of
// Titanium.Proxy. Because Titanium objects are not JavaScript objects it's
// functionality is recreated here for our own app use.
//
// Requires [undersore](http://underscorejs.org/).
//
// Usage / Examples
// ----------------
//
// There are two ways to use this. Stand alone and inherited.
//
// Stand Alone Event Dispatching is not as useful in practise but can be used
// in fun ways:
//
// var e = new EventEmitter();
// var callback = function(data) { console.log(data); };
// e.addEventListener("custom_event", callback);
// e.fireEvent("custom_event", "custom_event_data");
// e.removeEventListener("custom_event", callback);
//
// Inherited Pattern is more useful especially to make your own objects an
// event dispatcher:
//
// function MyObject() {
// EventEmitter.call(this); // Required
// this.data = "custom_event_data";
// }
// MyObject.prototype = new EventEmitter(); // Required
// MyObject.prototype.fire = function() {
// this.fireEvent("custom_event", this.data);
// };
//
// var myobj = new MyObject();
// var callback = function(data) { console.log(data); };
// myobj.addEventListener("custom_event", callback);
// myobj.fire();
// myobj.removeEventListener("custom_event", callback);
//
(function(exports) {
exports.EventEmitter = (function() {
function EventEmitter() {
this.event_handlers = {};
}
var fn = EventEmitter.prototype;
fn.addEventListener = function(name, func) {
this.event_handlers[name] = _.chain(this.event_handlers[name])
.union(func).filter(_.isFunction).value();
return this;
};
fn.removeEventListener = function(name, func) {
if (!_.isEmpty(this.event_handlers[name])) {
this.event_handlers[name] = _.without(this.event_handlers[name], func);
}
return this;
};
fn.fireEvent = function(name, data, context) {
if (context == null) { context = this; }
_.each(this.event_handlers[name], function(func) { func.call(context, data); });
};
return EventEmitter;
})();
})(this);
describe "EventEmitter", ->
beforeEach ->
@callback = createSpy("event callback")
@eventEmitter = new EventEmitter();
describe "#addEventListener", ->
it "should create an event when one does not exist", ->
@eventEmitter.addEventListener("test_event_not_exist_yet", @callback)
expect( @eventEmitter.event_handlers["test_event_not_exist_yet"] ).toBeDefined()
it "should handle array of functions", ->
expect( => @eventEmitter.addEventListener("test_event", [@callback]) ).not.toThrow()
it "should handle single functions", ->
expect( => @eventEmitter.addEventListener("test_event", @callback) ).not.toThrow()
it "should ignore non functions", ->
expect( => @eventEmitter.addEventListener("test_event", null) ).not.toThrow()
expect( => @eventEmitter.addEventListener("test_event", undefined) ).not.toThrow()
expect( => @eventEmitter.addEventListener("test_event", 1) ).not.toThrow()
expect( => @eventEmitter.addEventListener("test_event", "x") ).not.toThrow()
expect( @eventEmitter.event_handlers["test_event"].length ).toBe 0
it "should be chainable", ->
expect( @eventEmitter.addEventListener() ).toEqual @eventEmitter
describe "#removeEventListener", ->
it "should remove registered events", ->
@eventEmitter.addEventListener("test_event", @callback)
@eventEmitter.removeEventListener("test_event", @callback)
expect( @eventEmitter.event_handlers["test_event"].length ).toBe 0
it "should ignore unknown events", ->
expect( => @eventEmitter.removeEventListener("test_event_not_exist_yet", @callback) )
.not.toThrow()
it "should ignore non functions", ->
@eventEmitter.addEventListener("test_event", @callback)
expect( => @eventEmitter.removeEventListener("test_event", 1234) )
.not.toThrow()
it "should be chainable", ->
expect( @eventEmitter.removeEventListener() ).toEqual @eventEmitter
describe "#fireEvent", ->
it "should call callback functions", ->
@eventEmitter.addEventListener("test_event", @callback)
@eventEmitter.fireEvent("test_event", 1)
expect( @callback ).toHaveBeenCalledWith(1)
it "should be chainable", ->
expect( @eventEmitter.fireEvent() ).toEqual @eventEmitter
describe "Inheritance Pattern", ->
beforeEach ->
# Use clasical javascript inheritance. Not coffeescript's.
`this.TestEmitter = function() { EventEmitter.call(this); }`
`this.TestEmitter.prototype = new EventEmitter()`
@instance = new @TestEmitter()
it "should add methods to original object", ->
expect( @instance.addEventListener ).toBeDefined()
expect( @instance.removeEventListener).toBeDefined()
expect( @instance.fireEvent ).toBeDefined()
it "should be an instance of EventEmitter", ->
expect( @instance instanceof EventEmitter ).toBeTruthy()
it "should be an instance of the original object", ->
expect( @instance instanceof @TestEmitter ).toBeTruthy()
it "should isolate instances", ->
other = new @TestEmitter()
expect( @instance.event_handlers ).not.toBe other.event_handlers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment