# File: js/event/EventDispatcher.js

    var __extends = (this && this.__extends) || function (d, b) {
        for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
    (function (factory) {
        if (typeof module === 'object' && typeof module.exports === 'object') {
            var v = factory(require, exports); if (v !== undefined) module.exports = v;
        }
        else if (typeof define === 'function' && define.amd) {
            define(["require", "exports", '../ObjectManager', './BaseEvent'], factory);
        }
    })(function (require, exports) {
        var ObjectManager_1 = require('../ObjectManager');
        var BaseEvent_1 = require('./BaseEvent');
        /**
         * EventDispatcher is the base class for all classes that dispatch events. It is the base class for the {{#crossLink "DisplayObjectContainer"}}{{/crossLink}} class.
         * EventDispatcher provides methods for managing prioritized queues of event listeners and dispatching events.
         *
         * @class EventDispatcher
         * @extends ObjectManager
         * @module StructureJS
         * @submodule event
         * @requires Extend
         * @requires ObjectManager
         * @requires BaseEvent
         * @constructor
         * @author Robert S. (www.codeBelt.com)
         * @example
         *      // Another way to use the EventDispatcher.
         *      let eventDispatcher = new EventDispatcher();
         *      eventDispatcher.addEventListener('change', this._handlerMethod, this);
         *      eventDispatcher.dispatchEvent('change');
         */
        var EventDispatcher = (function (_super) {
            __extends(EventDispatcher, _super);
            function EventDispatcher() {
                _super.call(this);
                /**
                 * Holds a reference to added listeners.
                 *
                 * @property _listeners
                 * @type {Array.<any>}
                 * @protected
                 */
                this._listeners = null;
                /**
                 * Indicates the object that contains a child object. Uses the parent property
                 * to specify a relative path to display objects that are above the current display object in the display
                 * list hierarchy and helps facilitate event bubbling.
                 *
                 * @property parent
                 * @type {any}
                 * @public
                 */
                this.parent = null;
                this._listeners = [];
            }
            /**
             * Registers an event listener object with an EventDispatcher object so the listener receives notification of an event.
             *
             * @method addEventListener
             * @param type {String} The type of event.
             * @param callback {Function} The listener function that processes the event. This function must accept an Event object as its only parameter and must return nothing, as this example shows. @example function(event:Event):void
             * @param scope {any} Binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in JavaScript because scope isn't generally maintained.
             * @param [priority=0] {int} Influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
             * @public
             * @chainable
             * @example
             *      this.addEventListener(BaseEvent.CHANGE, this._handlerMethod, this);
             *
             *      _handlerMethod(event) {
             *          console.log(event.target + " sent the event.");
             *          console.log(event.type, event.data);
             *      }
             */
            EventDispatcher.prototype.addEventListener = function (type, callback, scope, priority) {
                if (priority === void 0) { priority = 0; }
                // Get the list of event listeners by the associated type value that is passed in.
                var list = this._listeners[type];
                if (list == null) {
                    // If a list of event listeners do not exist for the type value passed in then create a new empty array.
                    this._listeners[type] = list = [];
                }
                var index = 0;
                var listener;
                var i = list.length;
                while (--i > -1) {
                    listener = list[i];
                    if (listener.callback === callback && listener.scope === scope) {
                        // If the same callback and scope are found then remove it and add the current one below.
                        list.splice(i, 1);
                    }
                    else if (index === 0 && listener.priority < priority) {
                        index = i + 1;
                    }
                }
                // Add the event listener to the list array at the index value.
                list.splice(index, 0, { callback: callback, scope: scope, priority: priority, once: false });
                return this;
            };
            /**
             * Registers an event listener object once with an EventDispatcher object so the listener will receive the notification of an event.
             *
             * @method addEventListenerOnce
             * @param type {String} The type of event.
             * @param callback {Function} The listener function that processes the event. This function must accept an Event object as its only parameter and must return nothing, as this example shows. @example function(event:Event):void
             * @param scope {any} Binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in JavaScript because scope isn't generally maintained.
             * @param [priority=0] {int} Influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
             * @public
             * @chainable
             * @example
             *      this.addEventListenerOnce(BaseEvent.CHANGE, this._handlerMethod, this);
             *
             *      _handlerMethod(event) {
             *          console.log(event.target + " sent the event.");
             *          console.log(event.type, event.data);
             *      }
             */
            EventDispatcher.prototype.addEventListenerOnce = function (type, callback, scope, priority) {
                if (priority === void 0) { priority = 0; }
                // Add the event listener the normal way.
                this.addEventListener(type, callback, scope, priority);
                // Get the event listeners we just added.
                var list = this._listeners[type];
                var listener = list[0];
                // Change the value to true so it will be remove after dispatchEvent is called.
                listener.once = true;
                return this;
            };
            /**
             * Removes a specified listener from the EventDispatcher object.
             *
             * @method removeEventListener
             * @param type {String} The type of event.
             * @param callback {Function} The listener object to remove.
             * @param scope {any} The scope of the listener object to be removed.
             * @hide This was added because it was needed for the {{#crossLink "EventBroker"}}{{/crossLink}} class. To keep things consistent this parameter is required.
             * @public
             * @chainable
             * @example
             *      this.removeEventListener(BaseEvent.CHANGE, this._handlerMethod, this);
             */
            EventDispatcher.prototype.removeEventListener = function (type, callback, scope) {
                // Get the list of event listeners by the associated type value that is passed in.
                var list = this._listeners[type];
                if (list !== void 0) {
                    var i_1 = list.length;
                    while (--i_1 > -1) {
                        // If the callback and scope are the same then remove the event listener.
                        if (list[i_1].callback === callback && list[i_1].scope === scope) {
                            list.splice(i_1, 1);
                            break;
                        }
                    }
                }
                return this;
            };
            /**
             * <p>Dispatches an event into the event flow. The event target is the EventDispatcher object upon which the dispatchEvent() method is called.</p>
             *
             * @method dispatchEvent
             * @param event {string|BaseEvent} The Event object or event type string you want to dispatch. You can create custom events, the only requirement is all events must extend {{#crossLink "BaseEvent"}}{{/crossLink}}.
             * @param [data=null] {any} The optional data you want to send with the event. Do not use this parameter if you are passing in a {{#crossLink "BaseEvent"}}{{/crossLink}}.
             * @public
             * @chainable
             * @example
             *      this.dispatchEvent('change');
             *
             *      // Example: Sending data with the event:
             *      this.dispatchEvent('change', {some: 'data'});
             *
             *      // Example: With an event object
             *      // (event type, bubbling set to true, cancelable set to true and passing data) :
             *      let event = new BaseEvent(BaseEvent.CHANGE, true, true, {some: 'data'});
             *      this.dispatchEvent(event);
             *
             *      // Here is a common inline event object being dispatched:
             *      this.dispatchEvent(new BaseEvent(BaseEvent.CHANGE));
             */
            EventDispatcher.prototype.dispatchEvent = function (type, data) {
                if (data === void 0) { data = null; }
                var event = type;
                if (typeof event === 'string') {
                    event = new BaseEvent_1.default(type, false, true, data);
                }
                // If target is null then set it to the object that dispatched the event.
                if (event.target == null) {
                    event.target = this;
                    event.currentTarget = this;
                }
                // Get the list of event listener by the associated type value.
                var list = this._listeners[event.type];
                if (list !== void 0) {
                    var i_2 = list.length;
                    var listener;
                    while (--i_2 > -1) {
                        // If cancelable and isImmediatePropagationStopped are true then break out of the while loop.
                        if (event.cancelable === true && event.isImmediatePropagationStopped === true) {
                            break;
                        }
                        listener = list[i_2];
                        listener.callback.call(listener.scope, event);
                        // If the once value is true we want to remove the listener right after this callback was called.
                        if (listener.once === true) {
                            this.removeEventListener(event.type, listener.callback, listener.scope);
                        }
                    }
                }
                //Dispatches up the chain of classes that have a parent.
                if (this.parent != null && event.bubbles === true) {
                    // If cancelable and isPropagationStopped are true then don't dispatch the event on the parent object.
                    if (event.cancelable === true && event.isPropagationStopped === true) {
                        return this;
                    }
                    // Assign the current object that is currently processing the event (i.e. event bubbling at).
                    event.currentTarget = this;
                    // Pass the event to the parent (event bubbling).
                    this.parent.dispatchEvent(event);
                }
                return this;
            };
            /**
             * Check if an object has a specific event listener already added.
             *
             * @method hasEventListener
             * @param type {String} The type of event.
             * @param callback {Function} The listener method to call.
             * @param scope {any} The scope of the listener object.
             * @return {boolean}
             * @public
             * @example
             *      this.hasEventListener(BaseEvent.CHANGE, this._handlerMethod, this);
             */
            EventDispatcher.prototype.hasEventListener = function (type, callback, scope) {
                if (this._listeners[type] !== void 0) {
                    var listener;
                    var numOfCallbacks = this._listeners[type].length;
                    for (var i_3 = 0; i_3 < numOfCallbacks; i_3++) {
                        listener = this._listeners[type][i_3];
                        if (listener.callback === callback && listener.scope === scope) {
                            return true;
                        }
                    }
                }
                return false;
            };
            /**
             * Generates a string output of event listeners for a given object.
             *
             * @method getEventListeners
             * @return {string}
             * @public
             * @example
             *      this.getEventListeners();
             *
             *      // [ClassName] is listening for the 'BaseEvent.change' event.
             */
            EventDispatcher.prototype.getEventListeners = function () {
                var str = '';
                var numOfCallbacks;
                var listener;
                for (var type in this._listeners) {
                    numOfCallbacks = this._listeners[type].length;
                    for (var i_4 = 0; i_4 < numOfCallbacks; i_4++) {
                        listener = this._listeners[type][i_4];
                        if (listener.scope && (typeof listener.scope.getQualifiedClassName === 'function')) {
                            str += '[' + listener.scope.getQualifiedClassName() + ']';
                        }
                        else {
                            str += '[Unknown]';
                        }
                        str += " is listen for '" + type + "' event.\n";
                    }
                }
                return str;
            };
            /**
             * @overridden BaseObject.destroy
             */
            EventDispatcher.prototype.destroy = function () {
                this.disable();
                _super.prototype.destroy.call(this);
            };
            return EventDispatcher;
        })(ObjectManager_1.default);
        Object.defineProperty(exports, "__esModule", { value: true });
        exports.default = EventDispatcher;
    });