source/event-object.js
// ##EventObject
// `Struck.EventObject` normalizes an event API
// for adding event listeners and listening to
// objects externally. Using the listen methods
// automates undelgating events of view removal.
Struck.EventObject = (function () {
'use strict';
var EventObject = Struck.BaseObject.extend();
EventObject.prototype.initializeObject = function () {
// all event objects need an intercom for
// emiting and listening to events
this.com = Struck.Intercom.create();
// call super after defining com which
// is used for base hooks
Struck.BaseObject.prototype.initializeObject.apply(this, arguments);
this.listenedEvents = [];
return this;
};
// #####hook
// trigger intercom events for hook
EventObject.prototype.hook = function (name, mod, args) {
var postfix = '';
if (mod !== undefined && mod !== 'on') {
postfix = ':' + mod;
}
Struck.BaseObject.prototype.hook.apply(this, arguments);
this.com.emit.apply(this.com, [name + postfix].concat(args));
return this;
};
// #####listenTo
// Registers a event listener to the
// appropriate subsystem. Delegates jquery
// objects to the jq event system and struck
// objects to the instance's intercom
// we then keep a secondary object of events
// to remove when the object is deconstructed
EventObject.prototype.listenTo = function (obj, events, func, context) {
var opts = _.chain(arguments).rest(4).first().value();
addListener(this, {
obj: obj,
events: events,
func: func,
single: firstDef(opts && opts.single, false),
context: firstDef(context, this)
});
return this;
};
// #####listenOnce
EventObject.prototype.listenOnce = function (obj, events, func, context) {
return this.listenTo(obj, events, func, context, { single: true });
};
// #####stopListening
// removes an event listener from the
// appropriate subsystem
EventObject.prototype.stopListening = function (obj, events, func, context) {
removeListener(this, obj, events, func);
return this;
};
EventObject.prototype.trigger = function(events) {
this.com.emit.apply(this.com, [events].concat(_.rest(arguments, 1)));
return this;
};
// #####destroy
// when an object is removed, the destroy function
// should be called to remove attached event listeners
EventObject.prototype.destroy = function () {
Struck.BaseObject.prototype.destroy.apply(this, arguments);
// remove all event listeners listeners
this.stopListening();
_.defer(function(self) {
// destroy com interface
self.com.destroy();
}, this);
return this;
};
function addListener(self, opts) {
var obj = opts.obj,
events = splitName(opts.events),
func = opts.func;
var callback = !opts.single ? func : function() {
func.apply(this, arguments);
removeListener(self, obj, opts.events, callback);
};
_.each(events, function(ev) {
self.listenedEvents.push({
events: ev,
func: callback,
obj: obj,
context: opts.context
});
if (obj instanceof jQuery) {
obj[opts.single ? 'one' : 'on'](ev, _.bind(callback, opts.context));
} else if (obj instanceof Struck.EventObject) {
obj.com[opts.single ? 'once' : 'on'].call(obj.com, ev, callback, opts.context);
}
});
}
function removeListener(self, obj, events, func, context) {
events = splitName(events);
var rejects = [],
passes = [];
function pushResults(rejected, ev) {
if (rejected) {
rejects.push(ev);
} else {
passes.push(ev);
}
}
_.each(self.listenedEvents, function(ev) {
_.each(events, function(name) {
if (func) {
pushResults(ev.obj === obj && ev.events === name && ev.func === func, ev);
} else if (events) {
pushResults(ev.obj === obj && ev.events === name, ev);
}
});
});
if (!events && !func && obj) {
_.each(self.listenedEvents, function(ev) {
pushResults(ev.obj === obj, ev);
});
} else if (!events) {
rejects = self.listenedEvents;
}
self.listenedEvents = passes;
console.log(rejects);
_.each(rejects, function(reject) {
if (reject.obj instanceof jQuery) {
reject.obj.off(reject.events, _.bind(reject.func, firstDef(context, reject.context)));
} else if (reject.obj instanceof Struck.EventObject) {
reject.obj.com.off(reject.events, rejects.func);
}
});
}
return EventObject;
})();