# File: js/display/DOMElement.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", './DisplayObjectContainer', '../event/BaseEvent', '../util/TemplateFactory', '../util/ComponentFactory', '../plugin/jquery.eventListener'], factory);
        }
    })(function (require, exports) {
        var DisplayObjectContainer_1 = require('./DisplayObjectContainer');
        var BaseEvent_1 = require('../event/BaseEvent');
        var TemplateFactory_1 = require('../util/TemplateFactory');
        var ComponentFactory_1 = require('../util/ComponentFactory');
        var jquery_eventListener_1 = require('../plugin/jquery.eventListener');
        /**
         * The {{#crossLink "DOMElement"}}{{/crossLink}} class is the base view class for all objects that can be placed into the HTML DOM.
         *
         * @class DOMElement
         * @param type [any=null] Either a jQuery object or JavaScript template string reference you want to use as the view. Check out the examples below.
         * @param params [any=null] Any data you would like to pass into the jQuery element or template that is being created.
         * @extends DisplayObjectContainer
         * @module StructureJS
         * @submodule view
         * @requires Extend
         * @requires DisplayObjectContainer
         * @requires BaseEvent
         * @requires TemplateFactory
         * @requires ComponentFactory
         * @requires jQuery
         * @constructor
         * @author Robert S. (www.codeBelt.com)
         * @example
         *     // Example: Using DOMElement without extending it.
         *     let aLink = new DOMElement('a', {text: 'Google', href: 'http://www.google.com', 'class': 'externalLink'});
         *     this.addChild(aLink);
         *
         *     // Example: A view passing in a jQuery object.
         *     let view = new CustomView($('.selector'));
         *     this.addChild(view);
         *
         *     // Example: A view extending DOMElement while passing in a jQuery object.
         *     class ClassName extends DOMElement {
         *
         *          constructor($element) {
         *              super($element);
         *          }
         *
         *          create() {
         *              super.create();
         *
         *              // Create and add your child objects to this parent class.
         *          }
         *
         *          enable() {
         *              if (this.isEnabled === true) { return this; }
         *
         *              // Enable the child objects and add any event listeners.
         *
         *              return super.enable();
         *          }
         *
         *          disable() {
         *              if (this.isEnabled === false) { return this; }
         *
         *              // Disable the child objects and remove any event listeners.
         *
         *              return super.disable();
         *          }
         *
         *          layout() {
         *              // Layout or update the child objects in this parent class.
         *
         *              return this;
         *          }
         *
         *          destroy() {
         *              this.disable();
         *
         *              // Destroy the child objects and references in this parent class to prevent memory leaks.
         *
         *              super.destroy();
         *          }
         *
         *     }
         *
         *     // Example: A view extending DOMElement with a precompiled JavaScript template reference passed in.
         *     class ClassName extends DOMElement {
         *
         *          constructor() {
         *              _super();
         *          }
         *
         *          create() {
         *              super.create('templates/home/homeTemplate', {data: 'some data'});
         *
         *              // Create and add your child objects to this parent class.
         *          }
         *
         *          enable() {
         *              if (this.isEnabled === true) { return this; }
         *
         *              // Enable the child objects and add any event listeners.
         *
         *              return super.enable();
         *          }
         *
         *          disable() {
         *              if (this.isEnabled === false) { return this; }
         *
         *              // Disable the child objects and remove any event listeners.
         *
         *              return super.disable();
         *          }
         *
         *          layout() {
         *              // Layout or update the child objects in this parent class.
         *
         *              return this;
         *          }
         *
         *          destroy() {
         *              this.disable();
         *
         *              // Destroy the child objects and references in this parent class to prepare for garbage collection.
         *
         *              super.destroy();
         *          }
         *
         *     }
         */
        var DOMElement = (function (_super) {
            __extends(DOMElement, _super);
            function DOMElement(type, params) {
                if (type === void 0) { type = null; }
                if (params === void 0) { params = null; }
                _super.call(this);
                /**
                 * Tracks number of times an element's width has been checked
                 * in order to determine if the element has been added
                 * to the DOM.
                 *
                 * @property checkCount
                 * @type {number}
                 * @public
                 */
                this.checkCount = 0;
                /**
                 * A cached reference to the DOM Element
                 *
                 * @property element
                 * @type {HTMLElement}
                 * @default null
                 * @public
                 */
                this.element = null;
                /**
                 * A cached reference to the jQuery DOM element
                 *
                 * @property $element
                 * @type {JQuery}
                 * @default null
                 * @public
                 */
                this.$element = null;
                /**
                 * If a jQuery object was passed into the constructor this will be set as true and
                 * this class will not try to add the view to the DOM since it already exists.
                 *
                 * @property _isReference
                 * @type {boolean}
                 * @protected
                 */
                this._isReference = false;
                /**
                 * Holds onto the value passed into the constructor.
                 *
                 * @property _type
                 * @type {string}
                 * @default null
                 * @protected
                 */
                this._type = null;
                /**
                 * Holds onto the value passed into the constructor.
                 *
                 * @property _params
                 * @type {any}
                 * @default null
                 * @protected
                 */
                this._params = null;
                if (type instanceof jquery_eventListener_1.default) {
                    this.$element = type;
                    this.element = this.$element[0];
                    this._isReference = true;
                }
                else if (type) {
                    this._type = type;
                    this._params = params;
                }
            }
            /**
             * The create function is intended to provide a consistent place for the creation and adding
             * of children to the view. It will automatically be called the first time that the view is added
             * to another DisplayObjectContainer. It is critical that all subclasses call the super for this function in
             * their overridden methods.
             *
             * This method gets called once when the child view is added to another view. If the child view is removed
             * and added to another view the create method will not be called again.
             *
             * @method create
             * @param type [string=div] The HTML tag you want to create or the id/class selector of the template or the pre-compiled path to a template.
             * @param params [any=null] Any data you would like to pass into the jQuery element or template that is being created.
             * @returns {any} Returns an instance of itself.
             * @public
             * @chainable
             * @example
             *     // EXAMPLE 1: By default your view class will be a div element:
             *     create() {
             *          super.create();
             *
             *          this._childInstance = new DOMElement();
             *          this.addChild(this._childInstance);
             *     }
             *
             *     // EXAMPLE 2: But lets say you wanted the view to be a ul element:
             *     create() {
             *          super.create('ul');
             *     }
             *
             *     // Then you could nest other elements inside this base view/element.
             *     create() {
             *          super.create('ul', {id: 'myId', 'class': 'myClass anotherClass'});
             *
             *          let li = new DOMElement('li', {text: 'Robert is cool'});
             *          this.addChild(li);
             *     }
             *
             *     // EXAMPLE 3: So that's cool but what if you wanted a block of html to be your view. Let's say you had the below
             *     // inline Handlebar template in your html file.
             *     <script id="todoTemplate" type="text/template">
             *          <div id="htmlTemplate" class="js-todo">
             *              <div id="input-wrapper">
             *                  <input type="text" class="list-input" placeholder="{{ data.text }}">
             *                  <input type="button" class="list-item-submit" value="Add">
             *              </div>
             *          </div>
             *     </script>
             *
             *     // You would just pass in the id or class selector of the template which in this case is "#todoTemplate".
             *     // There is a second optional argument where you can pass data for the Handlebar template to use.
             *     create() {
             *          super.create('#todoTemplate', { data: this.viewData });
             *
             *     }
             *
             *     // EXAMPLE 4: Or maybe you're using grunt-contrib-handlebars, or similar, to precompile hbs templates
             *     create() {
             *          super.create('templates/HomeTemplate', {data: "some data"});
             *
             *     }
             */
            DOMElement.prototype.create = function (type, params) {
                if (type === void 0) { type = 'div'; }
                if (params === void 0) { params = null; }
                // Use the data passed into the constructor first else use the arguments from create.
                type = this._type || type;
                params = this._params || params;
                if (this.isCreated === true) {
                    throw new Error('[' + this.getQualifiedClassName() + '] You cannot call the create method manually. It is only called once automatically during the view lifecycle and should only be called once.');
                }
                if (this.$element == null) {
                    var html_1 = TemplateFactory_1.default.create(type, params);
                    if (html_1) {
                        this.$element = jquery_eventListener_1.default(html_1);
                    }
                    else {
                        this.$element = jquery_eventListener_1.default("<" + type + "/>", params);
                    }
                }
                this.element = this.$element[0];
                this.width = this.$element.width();
                this.height = this.$element.height();
                this.setSize(this.width, this.height);
                return this;
            };
            /**
             * @overridden DisplayObjectContainer.addChild
             * @method addChild
             * @param child {DOMElement} The DOMElement instance to add as a child of this object instance.
             * @returns {any} Returns an instance of itself.
             * @chainable
             * @example
             *     this.addChild(domElementInstance);
             */
            DOMElement.prototype.addChild = function (child) {
                if (this.$element == null) {
                    throw new Error('[' + this.getQualifiedClassName() + '] You cannot use the addChild method if the parent object is not added to the DOM.');
                }
                _super.prototype.addChild.call(this, child);
                // If an empty jQuery object is passed into the constructor then don't run the code below.
                if (child._isReference === true && child.$element.length === 0) {
                    return this;
                }
                if (child.isCreated === false) {
                    child.create(); // Render the item before adding to the DOM
                    child.isCreated = true;
                }
                // If the child object is not a reference of a jQuery object in the DOM then append it.
                if (child._isReference === false) {
                    this.$element.append(child.$element);
                }
                this._onAddedToDom(child);
                return this;
            };
            /**
             * Adds the sjsId to the DOM element so we can know what what Class object the HTMLElement belongs too.
             *
             * @method _addClientSideId
             * @param child {DOMElement} The DOMElement instance to add the sjsId too.
             * @protected
             */
            DOMElement.prototype._addClientSideId = function (child) {
                var type = child.$element.attr('data-sjs-type');
                var id = child.$element.attr('data-sjs-id');
                if (type === void 0) {
                    // Make them array's so the join method will work.
                    type = [child.getQualifiedClassName()];
                    id = [child.sjsId];
                }
                else {
                    // Split them so we can push/add the new values.
                    type = type.split(',');
                    id = id.split(',');
                    type.push(child.getQualifiedClassName());
                    id.push(child.sjsId);
                }
                // Updated list of id's and types
                child.$element.attr('data-sjs-id', id.join(','));
                child.$element.attr('data-sjs-type', type.join(','));
            };
            /**
             * Removes the sjsId and class type from the HTMLElement.
             *
             * @method _removeClientSideId
             * @param child {DOMElement} The DOMElement instance to add the sjsId too.
             * @protected
             * @return {boolean}
             */
            DOMElement.prototype._removeClientSideId = function (child) {
                var type = child.$element.attr('data-sjs-type');
                var id = child.$element.attr('data-sjs-id');
                // Split them so we can remove the child sjsId and type.
                var typeList = type.split(',');
                var idList = id.split(',').map(Number); // Convert each item into a number.
                var index = idList.indexOf(child.sjsId);
                if (index > -1) {
                    // Remove the id and type from the array.
                    typeList.splice(index, 1);
                    idList.splice(index, 1);
                    // Updated list of id's and types
                    child.$element.attr('data-sjs-type', typeList.join(','));
                    child.$element.attr('data-sjs-id', idList.join(','));
                }
                return idList.length === 0;
            };
            /**
             * Called when the child object is added to the DOM.
             * The method will call {{#crossLink "DOMElement/layout:method"}}{{/crossLink}} and dispatch the BaseEvent.ADDED_TO_STAGE event.
             *
             * @method _onAddedToDom
             * @protected
             */
            DOMElement.prototype._onAddedToDom = function (child) {
                var _this = this;
                child.checkCount++;
                if (child.$element.width() === 0 && child.checkCount < 5) {
                    setTimeout(function () {
                        _this._onAddedToDom(child);
                    }, 100);
                    return;
                }
                this._addClientSideId(child);
                child.width = child.$element.width();
                child.height = child.$element.height();
                child.setSize(child.width, child.height);
                child.enable();
                child.layout();
                child.dispatchEvent(new BaseEvent_1.default(BaseEvent_1.default.ADDED_TO_STAGE));
            };
            /**
             * @overridden DisplayObjectContainer.addChildAt
             */
            DOMElement.prototype.addChildAt = function (child, index) {
                var children = this.$element.children();
                var length = children.length;
                // If an empty jQuery object is passed into the constructor then don't run the code below.
                if (child._isReference === true && child.$element.length === 0) {
                    return this;
                }
                if (index < 0 || index >= length) {
                    // If the index passed in is less than 0 and greater than the total number of children then place the item at the end.
                    this.addChild(child);
                }
                else {
                    // Else get the child in the children array by the index passed in and place the item before that child.
                    if (child.isCreated === false) {
                        child.create(); // Render the item before adding to the DOM
                        child.isCreated = true;
                    }
                    // Adds the child at a specific index but also will remove the child from another parent object if one exists.
                    if (child.parent) {
                        child.parent.removeChild(child, false);
                    }
                    this.children.splice(index, 0, child);
                    this.numChildren = this.children.length;
                    child.parent = this;
                    // Adds the child before any child already added in the DOM.
                    jquery_eventListener_1.default(children.get(index)).before(child.$element);
                    this._onAddedToDom(child);
                }
                return this;
            };
            /**
             * @overridden DisplayObjectContainer.swapChildren
             */
            DOMElement.prototype.swapChildren = function (child1, child2) {
                var child1Index = child1.$element.index();
                var child2Index = child2.$element.index();
                this.addChildAt(child1, child2Index);
                this.addChildAt(child2, child1Index);
                return this;
            };
            /**
             * @overridden DisplayObjectContainer.getChildAt
             */
            DOMElement.prototype.getChildAt = function (index) {
                return _super.prototype.getChildAt.call(this, index);
            };
            /**
             * Returns a DOMElement object with the first found DOM element by the passed in selector.
             *
             * @method getChild
             * @param selector {string} DOM id name, DOM class name or a DOM tag name.
             * @returns {DOMElement}
             * @public
             */
            DOMElement.prototype.getChild = function (selector) {
                // Get the first match from the selector passed in.
                var jQueryElement = this.$element.find(selector).first();
                if (jQueryElement.length === 0) {
                    throw new TypeError('[' + this.getQualifiedClassName() + '] getChild(' + selector + ') Cannot find DOM $element');
                }
                // Check to see if the element has a sjsId value and is a child of this parent object.
                var sjsId = parseInt(jQueryElement.attr('data-sjs-id'));
                var domElement = this.getChildByCid(sjsId);
                // Creates a DOMElement from the jQueryElement.
                if (domElement == null) {
                    // Create a new DOMElement and assign the jQuery element to it.
                    domElement = new DOMElement();
                    domElement.$element = jQueryElement;
                    this._addClientSideId(domElement);
                    domElement.element = jQueryElement[0];
                    domElement.isCreated = true;
                    // Added to the super addChild method because we don't need to append the element to the DOM.
                    // At this point it already exists and we are just getting a reference to the DOM element.
                    _super.prototype.addChild.call(this, domElement);
                }
                return domElement;
            };
            /**
             * Gets all the HTML elements children of this object.
             *
             * @method getChildren
             * @param [selector] {string} You can pass in any type of jQuery selector. If there is no selector passed in it will get all the children of this parent element.
             * @returns {Array.<DOMElement>} Returns a list of DOMElement's. It will grab all children HTML DOM elements of this object and will create a DOMElement for each DOM child.
             * If the 'data-sjs-id' property exists is on an HTML element a DOMElement will not be created for that element because it will be assumed it already exists as a DOMElement.
             * @public
             */
            DOMElement.prototype.getChildren = function (selector) {
                if (selector === void 0) { selector = ''; }
                //TODO: Make sure the index of the children added is the same as the what is in the actual DOM.
                var $child;
                var domElement;
                var $list = this.$element.children(selector);
                var listLength = $list.length;
                for (var i_1 = 0; i_1 < listLength; i_1++) {
                    $child = $list.eq(i_1);
                    // If the jQuery element already has sjsId data property then it must be an existing DisplayObjectContainer (DOMElement) in the children array.
                    if ($child.attr('data-sjs-id') === void 0) {
                        domElement = new DOMElement();
                        domElement.$element = $child;
                        this._addClientSideId(domElement);
                        domElement.element = $child.get(0);
                        domElement.isCreated = true;
                        // Added to the super addChild method because we don't need to append the element to the DOM.
                        // At this point it already exists and we are just getting a reference to the DOM element.
                        _super.prototype.addChild.call(this, domElement);
                    }
                }
                return this.children;
            };
            /**
             * Removes the specified child object instance from the child list of the parent object instance.
             * The parent property of the removed child is set to null and the object is garbage collected if there are no other references
             * to the child. The index positions of any objects above the child in the parent object are decreased by 1.
             *
             * @method removeChild
             * @param child {DOMElement} The DisplayObjectContainer instance to remove.
             * @returns {any} Returns an instance of itself.
             * @override
             * @public
             * @chainable
             */
            DOMElement.prototype.removeChild = function (child, destroy) {
                if (destroy === void 0) { destroy = true; }
                var remove = this._removeClientSideId(child);
                child.disable();
                // Checks if destroy was called before removeChild so it doesn't error.
                if (remove === true && child.$element != null) {
                    child.$element.unbind();
                    child.$element.remove();
                }
                if (destroy === true) {
                    child.destroy();
                }
                _super.prototype.removeChild.call(this, child);
                return this;
            };
            /**
             * Removes the child display object instance that exists at the specified index.
             *
             * @method removeChildAt
             * @param index {int} The index position of the child object.
             * @public
             * @chainable
             */
            DOMElement.prototype.removeChildAt = function (index, destroy) {
                if (destroy === void 0) { destroy = true; }
                this.removeChild(this.getChildAt(index), destroy);
                return this;
            };
            /**
             * Removes all child object instances from the child list of the parent object instance.
             * The parent property of the removed children is set to null and the objects are garbage collected if no other
             * references to the children exist.
             *
             * @method removeChildren
             * @returns {DOMElement} Returns an instance of itself.
             * @override
             * @public
             * @chainable
             */
            DOMElement.prototype.removeChildren = function (destroy) {
                if (destroy === void 0) { destroy = true; }
                while (this.children.length > 0) {
                    this.removeChild(this.children.pop(), destroy);
                }
                this.$element.empty();
                return this;
            };
            /**
             * @overridden DisplayObjectContainer.destroy
             */
            DOMElement.prototype.destroy = function () {
                // Note: we can't just call destroy to remove the HTMLElement because there could be other views managing the same HTMLElement.
                /*if (this.$element != null) {
                     this.$element.unbind();
                     this.$element.remove();
                 }*/
                _super.prototype.destroy.call(this);
            };
            /**
             * A way to instantiate view classes by found html selectors.
             *
             * Example: It will find all children elements of the {{#crossLink "DOMElement/$element:property"}}{{/crossLink}} property with the 'js-shareEmail' selector.
             * If any selectors are found the EmailShareComponent class will be instantiated and pass the found jQuery element into the contructor.
             *
             * @method createComponents
             * @param componentList (Array.<{ selector: string; component: DOMElement }>
             * @return {Array.<DOMElement>} Returns all the items created from this createComponents method.
             * @public
             * @chainable
             * @example
             *      create() {
             *          super.create();
             *
             *          this.createComponents([
             *              {selector: '.js-shareEmail', component: EmailShareComponent},
             *              {selector: '.js-pagination', component: PaginationComponent},
             *              {selector: '.js-carousel', component: CarouselComponent}
             *          ]);
             *      }
             */
            DOMElement.prototype.createComponents = function (componentList) {
                var list;
                var createdChildren = [];
                var length = componentList.length;
                var obj;
                for (var i_2 = 0; i_2 < length; i_2++) {
                    obj = componentList[i_2];
                    list = ComponentFactory_1.default.create(this.$element.find(obj.selector), obj.component, this);
                    createdChildren = createdChildren.concat(list);
                }
                return createdChildren;
            };
            return DOMElement;
        })(DisplayObjectContainer_1.default);
        Object.defineProperty(exports, "__esModule", { value: true });
        exports.default = DOMElement;
    });