/**
 * EventEmitter Mixin
 *
 * Designed to be used in conjunction with a mixin "augment" function,
 * such as http://chamnapchhorn.blogspot.com/2009/05/javascript-mixins.html
 *
 * @usage augment(MyClass, EventEmitter);
 * my_inst = new MyClass();
 * my_inst.on('someEvent', function(e){ console.dir(e); });
 * my_inst.trigger('someEvent', {eventProp:'value'});
 * 
 * @example
 * // create a 'class'
 * MyClass = function() {}
 * // augment it with EventEmitter
 * EventEmitter.augment(MyClass.prototype);
 * // create a method, which triggers an event
 * MyClass.prototype.scrollComplete = function() {
 *     this.trigger('scrolled', {baz:'eck'});
 * };
 * 
 * // this callback is pulled out into a named function so that we can unbind it
 * var callback = function(e) {
 *     console.log('the scrolled event was fired! this.foo='+this.foo+', e.baz='+e.baz);
 * };
 * // create an instance of th class
 * var myinstance = new MyClass();
 * // set a property on the instance
 * myinstance.foo = 'bar';
 * // bind to the scrollComplete event
 * myinstance.on('scrolled', callback, myinstance);
 * // fire the method, which should trigger the event and therefore our callback
 * myinstance.scrollComplete();
 * // unbind the event, so that our callback should not get called
 * myinstance.removeListener('scrolled', callback);
 * // this should now not fire the callback
 * myinstance.scrollComplete();
 */
var EventEmitter = function() {};
/**
 * Bind a callback to an event, with an option scope context
 *
 * @param {string} name the name of the event
 * @param {function} callback the callback function to fire when the event is triggered
 * @param {object} context the scope to use for the callback (which will become 'this' inside the callback)
 */
EventEmitter.prototype.on = function(name, callback, context) {
    if (!context) context = this;
    if (!this._listeners) this._listeners = {};
    if (!this._listeners[name]) this._listeners[name] = [];
    if (!this._unbinders) this._unbinders = {};
    if (!this._unbinders[name]) this._unbinders[name] = [];
    var f = function(e) {
        callback.apply(context, [e]);
    };
    this._unbinders[name].push(callback);
    this._listeners[name].push(f);
};
/**
 * Trigger an event, firing all bound callbacks
 * 
 * @param {string} name the name of the event
 * @param {object} event the event object to be passed through to the callback
 */
EventEmitter.prototype.trigger = function(name, event) {
    if (event === undefined) event = {}
    if (!this._listeners) this._listeners = {};
    if (!this._listeners[name]) return;
    var i = this._listeners[name].length;
    while (i--) this._listeners[name][i](event);
};
/**
 * Remove a bound listener
 * 
 * @param {string} name the name of the event
 * @param {object} event the event object to be passed through to the callback
 */
EventEmitter.prototype.removeListener = function(name, callback) {
    if (!this._unbinders) this._unbinders = {};
    if (!this._unbinders[name]) return;
    var i = this._unbinders[name].length;
    while (i--) {
        if (this._unbinders[name][i] === callback) {
            this._unbinders[name].splice(i, 1);
            this._listeners[name].splice(i, 1);
        }
    }
};
/**
 * Augment an object with the EventEmitter mixin
 * 
 * @param {object} obj The object to be augmented (often an object's protoype)
 */
EventEmitter.augment = function(obj) {
    for (var method in EventEmitter.prototype) {
        if (!obj[method]) obj[method] = EventEmitter.prototype[method];
    }
};