Source: events/eventdispatcher.js

goog.provide('lime.events.EventDispatcher');

goog.require('lime.events.Event');

/**
 * EventDispatcher object. Deals with event handlers
 * @param {lime.Director} director Director object.
 * @constructor
 */
lime.events.EventDispatcher = function(director) {
    this.director = director;
    this.handlers = {};
    this.swallows = {};

};

/**
 * Register the event listener for node
 * @param {lime.Node} node Node that responds to events.
 * @param {string} eventType type of event to listen.
 */
lime.events.EventDispatcher.prototype.register = function(node, eventType) {
    if (!goog.isDef(this.handlers[eventType])) {
        this.handlers[eventType] = [node];
        this.handlers[eventType].sorted = true;
        //base element switch here because safari fires touchend on
        //dom tree changes otherwise
        goog.events.listen(eventType.substring(0, 5) == 'touch' && node != this.director ?
            document : (eventType.substring(0, 3) == 'key' ?
            window : this.director.domElement.parentNode), eventType,
            this, false, this);
    } else {
        if (!goog.array.contains(this.handlers[eventType], node)) {
            this.handlers[eventType].push(node);
            this.handlers[eventType].sorted = false;
        }
    }
};

/**
 * Release the event listener for node
 * @param {lime.Node} node Node that responds to events.
 * @param {string} eventType type of event to release.
 */
lime.events.EventDispatcher.prototype.release = function(node, eventType) {
    if (goog.isDef(this.handlers[eventType])) {
        goog.array.remove(this.handlers[eventType], node);
        if (!this.handlers[eventType].length) {
            goog.events.unlisten(this.director.domElement.parentNode,
                eventType, this, false, this);
            delete this.handlers[eventType];
        }
    }
};

/**
 * Update order of handler nodes. Called on tree changes.
 * @param {lime.Node} node Node that has changed.
 */
lime.events.EventDispatcher.prototype.updateDispatchOrder = function(node) {
    for (var eventType in this.handlers) {
        var handlers = this.handlers[eventType];
        if (goog.array.contains(handlers, node)) {
            handlers.sorted = false;
        }
    }
}

/**
 * Setup swallow rule for an event. Swallow means that next events from
 * same interaction will go straight to the handler
 * @param {lime.events.Event} e Event.
 * @param {string} type Event type.
 * @param {function(lime.events.Event)} handler Function to call.
 */
lime.events.EventDispatcher.prototype.swallow = function(e, type, handler) {
    /*
   // don't remember why this check was needed
   if (e.type != 'mousedown' && e.type != 'touchstart' &&
        e.type != 'touchmove' && e.type != 'keydown') return;*/
    var id = e.identifier;
    if (!goog.isDef(this.swallows[id])) {
        this.swallows[id] = [];
    }
    this.swallows[id].push([e.targetObject, type, handler]);
    //console.log('listen');
    goog.events.listen(e.targetObject, type, goog.nullFunction);
};

/**
 * Handle DOM event
 * @param {Event} e Event.
 */
lime.events.EventDispatcher.prototype.handleEvent = function(e) {

    if (!goog.isDef(this.handlers[e.type])) return;

    if (!this.handlers[e.type].sorted) {
        this.handlers[e.type].sort(lime.Node.compareNode);
        this.handlers[e.type].sorted = true;
    }

    var handlers = this.handlers[e.type].slice(),
        didhandle = false;

    var touchIndex = 0,
        doBreak = 0;

    while (!doBreak) {

        var ee = new lime.events.Event(this);
        ee.type = e.type;
        ee.event = e;

        if (e.type.substring(0, 5) == 'touch') {
            var touch = e.getBrowserEvent().changedTouches[touchIndex];
            ee.screenPosition = new goog.math.Coordinate(touch.pageX, touch.pageY);
            ee.identifier = touch.identifier;
            touchIndex++;

            if (touchIndex >= e.getBrowserEvent().changedTouches.length) {
                doBreak = 1;
            }
        } else {
            ee.screenPosition = new goog.math.Coordinate(
                e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
                e.clientY + document.body.scrollTop + document.documentElement.scrollTop);
            doBreak = 1;
        }

        if (goog.isDef(this.swallows[ee.identifier])) {
            var s = this.swallows[ee.identifier];

            for (var i = 0; i < s.length; i++) {
                if (s[i][1] == e.type || (goog.isArray(s[i][1]) &&
                    goog.array.contains(s[i][1], e.type))) {

                    var handler = s[i][0];
                    ee.targetObject = handler;
                    ee.position = handler.screenToLocal(ee.screenPosition);
                    s[i][2].call(handler, ee);
                    didhandle = true;
                }
            }
            //handler.dispatchEvent(ee);

            if (e.type == 'touchend' || e.type == 'touchcancel' ||
                e.type == 'mouseup' || e.type == 'keyup') {
                delete ee.targetObject;
                ee.release();
            }
        } else {
            for (var i = 0; i < handlers.length; i++) {

                var handler = handlers[i];

                if (this.director.getCurrentScene() != handler.getScene() &&
                    handler != this.director) continue;

                if (handler.getHidden() || !handler.inTree_) continue;

                ee.targetObject = handler;

                if (handler.hitTest(ee) || e.type.substring(0, 3) == 'key') {

                    ee.targetObject = handler;
                    handler.dispatchEvent(ee);
                    didhandle = true;

                    if (ee.event.propagationStopped_) break;
                }

            }

        }

    }

    if (didhandle)
        e.preventDefault();

};