"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var HaikuBase_1 = require("./HaikuBase");
var cssMatchOne_1 = require("./helpers/cssMatchOne");
var Layout3D_1 = require("./Layout3D");
exports.HAIKU_ID_ATTRIBUTE = 'haiku-id';
exports.HAIKU_TITLE_ATTRIBUTE = 'haiku-title';
exports.HAIKU_VAR_ATTRIBUTE = 'haiku-var';
exports.HAIKU_SOURCE_ATTRIBUTE = 'haiku-source';
var DEFAULT_TAG_NAME = 'div';
var COMPONENT_PSEUDO_TAG_NAME = DEFAULT_TAG_NAME;
var TEXT_PSEUDO_TAG_NAME = '__text__';
var CSS_QUERY_MAPPING = {
    name: 'elementName',
    attributes: 'attributes',
    children: 'children',
};
var HaikuElement = /** @class */ (function (_super) {
    __extends(HaikuElement, _super);
    function HaikuElement() {
        return _super.call(this) || this;
    }
    Object.defineProperty(HaikuElement.prototype, "childNodes", {
        get: function () {
            return (this.node && this.node.children) || [];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "children", {
        get: function () {
            return this.childNodes.map(function (childNode) {
                // To avoid unnecessary up-front work, we create HaikuElement instances
                // on demand rather than hydrating the collection on load
                return HaikuElement.findOrCreateByNode(childNode);
            });
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "attributes", {
        get: function () {
            return (this.node && this.node.attributes) || {};
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "type", {
        get: function () {
            return (this.node && this.node.elementName) || DEFAULT_TAG_NAME;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "title", {
        get: function () {
            return this.attributes[exports.HAIKU_TITLE_ATTRIBUTE];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "source", {
        get: function () {
            return this.attributes[exports.HAIKU_SOURCE_ATTRIBUTE];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "id", {
        get: function () {
            return this.attributes.id;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "className", {
        get: function () {
            return this.attributes.class;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "tagName", {
        get: function () {
            if (this.isTextNode()) {
                return TEXT_PSEUDO_TAG_NAME;
            }
            if (this.isComponent()) {
                return COMPONENT_PSEUDO_TAG_NAME;
            }
            return this.type || DEFAULT_TAG_NAME;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "nodeType", {
        get: function () {
            if (this.isTextNode()) {
                return 3;
            }
            return 1;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "subcomponent", {
        /**
         * @method subcomponent
         * @description Returns the HaikuComponent instance that manages nodes below this one.
         * This node is considered the 'wrapper' node and its child is considered the 'root'.
         */
        get: function () {
            return this.node && this.node.__subcomponent;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "instance", {
        /**
         * @method subcomponent
         * @description Returns the HaikuComponent instance that manages this node and those beneath.
         * This node is considered the 'root' node of the instance.
         */
        get: function () {
            return this.node && this.node.__instance;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "instanceContext", {
        get: function () {
            return this.node && this.node.__context;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "parentNode", {
        get: function () {
            return this.node && this.node.__parent;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "parent", {
        get: function () {
            return this.parentNode && this.parentNode.__element;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "layout", {
        get: function () {
            return this.node && this.node.layout && this.node.layout.computed;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "layoutMatrix", {
        get: function () {
            return (this.layout && this.layout.matrix) || Layout3D_1.default.createMatrix();
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "layoutAncestry", {
        get: function () {
            if (!this.layout) {
                return [];
            }
            var ancestry = [this.layout];
            // tslint:disable-next-line:no-this-assignment
            var ancestor = this;
            while (ancestor.parent) {
                ancestor = ancestor.parent;
                var layout = ancestor.layout;
                if (layout) {
                    ancestry.unshift(layout);
                }
            }
            return ancestry;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "layoutAncestryMatrices", {
        get: function () {
            return this.layoutAncestry.map(function (layout) { return layout.matrix; });
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "rootSVG", {
        get: function () {
            var parent = this.parent;
            while (parent) {
                if (parent.type === 'svg') {
                    return parent;
                }
                parent = parent.parent;
            }
            return undefined;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "isChildOfDefs", {
        get: function () {
            var parent = this.parent;
            while (parent) {
                if (parent.type === 'defs') {
                    return true;
                }
                parent = parent.parent;
            }
            return false;
        },
        enumerable: true,
        configurable: true
    });
    HaikuElement.prototype.getTranscludedElement = function () {
        if (this.type !== 'use') {
            return this;
        }
        var href = this.attributes['xlink:href'] || this.attributes.href;
        if (!href) {
            return;
        }
        var rootSVG = this.rootSVG;
        if (!rootSVG) {
            return;
        }
        var address = href.substr(1);
        var out;
        this.rootSVG.visit(function (desc) {
            if (desc.id === address) {
                out = desc;
                return false;
            }
        });
        return out;
    };
    Object.defineProperty(HaikuElement.prototype, "rawLayout", {
        get: function () {
            return this.node && this.node.layout;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "translation", {
        get: function () {
            return this.layout && this.layout.translation;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "rotation", {
        get: function () {
            return this.layout && this.layout.rotation;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "scale", {
        get: function () {
            return this.layout && this.layout.scale;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "origin", {
        get: function () {
            return this.layout && this.layout.origin;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "mount", {
        get: function () {
            return this.layout && this.layout.mount;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "align", {
        get: function () {
            return this.layout && this.layout.align;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "size", {
        get: function () {
            return this.layout && this.layout.size;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "targets", {
        get: function () {
            return (this.node && this.node.__targets) || [];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "target", {
        get: function () {
            // Assume the most recently added target is the canonical target due to an implementation
            // detail in the Haiku editing environment; FIXME. On 3 Jun 2018 was changed from the first
            // added to the last added one to fix a bug related to ungrouping
            return this.targets[this.targets.length - 1];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "rotationX", {
        get: function () {
            return this.rotation && this.rotation.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "rotationY", {
        get: function () {
            return this.rotation && this.rotation.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "rotationZ", {
        get: function () {
            return this.rotation && this.rotation.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "scaleX", {
        get: function () {
            return this.scale && this.scale.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "scaleY", {
        get: function () {
            return this.scale && this.scale.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "scaleZ", {
        get: function () {
            return this.scale && this.scale.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "positionX", {
        get: function () {
            return this.translation && this.translation.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "positionY", {
        get: function () {
            return this.translation && this.translation.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "positionZ", {
        get: function () {
            return this.translation && this.translation.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "translationX", {
        get: function () {
            return this.translation && this.translation.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "translationY", {
        get: function () {
            return this.translation && this.translation.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "translationZ", {
        get: function () {
            return this.translation && this.translation.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "sizeX", {
        get: function () {
            return this.size && this.size.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "sizeY", {
        get: function () {
            return this.size && this.size.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "sizeZ", {
        get: function () {
            return this.size && this.size.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "originX", {
        get: function () {
            return this.origin && this.origin.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "originY", {
        get: function () {
            return this.origin && this.origin.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "originZ", {
        get: function () {
            return this.origin && this.origin.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "mountX", {
        get: function () {
            return this.mount && this.mount.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "mountY", {
        get: function () {
            return this.mount && this.mount.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "mountZ", {
        get: function () {
            return this.mount && this.mount.z;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "alignX", {
        get: function () {
            return this.align && this.align.x;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "alignY", {
        get: function () {
            return this.align && this.align.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(HaikuElement.prototype, "alignZ", {
        get: function () {
            return this.align && this.align.z;
        },
        enumerable: true,
        configurable: true
    });
    HaikuElement.prototype.getComponentId = function () {
        return this.attributes[exports.HAIKU_ID_ATTRIBUTE];
    };
    HaikuElement.prototype.isSimpleNode = function () {
        return !this.isComponent();
    };
    HaikuElement.prototype.isTextNode = function () {
        return typeof this.node === 'string';
    };
    HaikuElement.prototype.isComponent = function () {
        return !!this.instance;
    };
    HaikuElement.prototype.componentMatches = function (selector) {
        if (!this.isComponent()) {
            return false;
        }
        var trimmed = selector.trim();
        var source = this.source;
        var title = this.title;
        var id = this.id;
        return (trimmed === COMPONENT_PSEUDO_TAG_NAME ||
            trimmed === source ||
            trimmed === title ||
            trimmed === id);
    };
    HaikuElement.prototype.matches = function (selector) {
        return (this.componentMatches(selector) ||
            cssMatchOne_1.default(this.node, selector, CSS_QUERY_MAPPING));
    };
    HaikuElement.prototype.visit = function (iteratee, filter) {
        if (iteratee(this) !== false) {
            return this.visitDescendants(iteratee, filter);
        }
    };
    HaikuElement.prototype.visitDescendants = function (iteratee, filter) {
        var children = filter ? this.children.filter(filter) : this.children;
        for (var i = 0; i < children.length; i++) {
            if (children[i].visit(iteratee, filter) === false) {
                break;
            }
        }
    };
    HaikuElement.prototype.querySelector = function (selector) {
        var _this = this;
        return this.cacheFetch("querySelector:" + selector, function () {
            var out;
            _this.visitDescendants(function (element) {
                if (element.matches(selector)) {
                    out = element;
                    // Returning `false` short-circuits the visitor
                    return false;
                }
            });
            return out;
        });
    };
    HaikuElement.prototype.querySelectorAll = function (selector) {
        var _this = this;
        return this.cacheFetch("querySelectorAll:" + selector, function () {
            var out = [];
            _this.visitDescendants(function (element) {
                if (element.matches(selector)) {
                    out.push(element);
                }
            });
            return out;
        });
    };
    // tslint:disable-next-line:variable-name
    HaikuElement.__name__ = 'HaikuElement';
    HaikuElement.findByNode = function (node) {
        var registry = HaikuBase_1.default.getRegistryForClass(HaikuElement);
        for (var i = 0; i < registry.length; i++) {
            if (registry[i].node === node) {
                return registry[i];
            }
        }
        return;
    };
    HaikuElement.connectNodeWithElement = function (node, element) {
        // In case the element wasn't initialized yet
        if (element) {
            element.node = node;
        }
        // In case we got a string or null node
        if (node && typeof node === 'object') {
            node.__element = element;
        }
    };
    HaikuElement.createByNode = function (node) {
        var element = new HaikuElement();
        HaikuElement.connectNodeWithElement(node, element);
        return element;
    };
    HaikuElement.findOrCreateByNode = function (node) {
        var found = HaikuElement.findByNode(node);
        if (found) {
            return found;
        }
        return HaikuElement.createByNode(node);
    };
    return HaikuElement;
}(HaikuBase_1.default));
exports.default = HaikuElement;
//# sourceMappingURL=HaikuElement.js.map