API Docs for: 1.0.1

File: js\grape\etc\event-emitter.js

define(['../class'], function (Class) {
    var EventEmitter;

    /**
     * A helper function for decomposing nested event handlers. When the method is a function, it is just added to the
     * target. If the method is an object, all of it's element are added with name.key.
     *
     * @example
     *      var target = {};
     *      Grape.EventEmitter.decompose(function(){},target,'name1');
     *      Grape.EventEmitter.decompose({
     *          x:function(){}
     *      },target,'name2');
     *
     *      //target will be {'name1':function(){},'name2.x':function(){}}
     *
     * @method decompose
     * @static
     * @param {Function|Object} method The method or methods
     * @param {Object} target The target the methods are added to.
     * @param {String} name method name or nested method prefix
     */
    function decompose(method, target, name) {
        var i;
        if (typeof method === 'object') { //nested methods
            for (i in method) {
                decompose(method[i], target, name + '.' + i);
            }
        } else {
            target[name] = method;
        }
    }

    Class.registerKeyword('event', {
        onInit: function (classInfo) {
            classInfo.events = {};
            classInfo.allEvent = {};
        },
        onAdd: function (classInfo, methodDescriptor) {
            if (!classInfo.extends(EventEmitter)) {
                throw new Error('To use "event" keyword, inherit the Grape.EventEmitter class!');
            }
            decompose(methodDescriptor.method, classInfo.events, methodDescriptor.name);
            return false;
        },
        onFinish: function (classInfo) {
            var i, event, events;
            //add parent events
            for (i = 0; i < classInfo.allParent.length; i++) {
                events = classInfo.allParent[i].events;
                for (event in events) {
                    (classInfo.allEvent[event] || (classInfo.allEvent[event] = [])).push(events[event]);
                }
            }
            //add own events
            events = classInfo.events;
            for (event in events) {
                (classInfo.allEvent[event] || (classInfo.allEvent[event] = [])).push(events[event]);
            }
        }
    });


    /**
     * An object which cna emit events, others can subscribe to it, and we can use the event keyword to make easier
     * the subscription when extending this class.
     *
     * @class Grape.EventEmitter
     */
    EventEmitter = Class('EventEmitter', {
        init: function () { //subscribe to events defined in class
            var i, myClass = this.getClass();
            this._events = {};
            for (i in myClass.allEvent) { //TODOv2 separate static and dynamic subscriptions
                this._events[i] = myClass.allEvent[i].slice(0);
            }
        },
        /**
         * Subscribes to an event. The event handler will be called with this instance as context.
         *
         * @method on
         * @param {String} event The event to subscribe
         * @param {Function} listener Event listener
         */
        on: function (event, listener) {
            (this._events[event] || (this._events[event] = [])).push(listener);
        },

        /**
         * Unsubscribes from an event.
         *
         * @method off
         * @param {String} event Event
         * @param {Function} listener Event listener
         */
        off: function (event, listener) { //todov2 check remove with indexOf speed
            //todov2 remove all listeners
            var i, listeners = this._events[event];
            for (i = 0; i < listeners.length; i++) {
                if (listeners[i] === listener) {
                    listeners.splice(i, 1);
                    i--;
                }
            }
        },
        //todov2 once
        /**
         * Emits an event to the instance: calls all event listeners subscribed to this event, or the 'any' event.
         *
         * @method emit
         * @param {String} event Event
         * @param {*} payload An object passed as parameter to all event listeners.
         */
        emit: function (event, payload) { //TODOv2 class level listeners?
            var i, listeners = this._events[event], n;
            if (listeners) {

                for (i = 0, n = listeners.length; i < n; i++) {
                    listeners[i].call(this, payload);
                }
            }
            /**
             * Emitted when any event is emitted. The parameters are the event and the payload.
             *
             * @event any
             */
            listeners = this._events.any;
            if (listeners) {
                for (i = 0, n = listeners.length; i < n; i++) {
                    listeners[i].call(this, event, payload);
                }
            }
        },
        'static decompose': decompose
    });


    return EventEmitter;
});