"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 __());
    };
})();
var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var Config_1 = require("./Config");
var HaikuElement_1 = require("./HaikuElement");
var HaikuBase_1 = require("./HaikuBase");
var HaikuTimeline_1 = require("./HaikuTimeline");
var vanities_1 = require("./properties/dom/vanities");
var consoleErrorOnce_1 = require("./helpers/consoleErrorOnce");
var cssQueryList_1 = require("./helpers/cssQueryList");
var xmlToMana_1 = require("./helpers/xmlToMana");
var interactionModes_1 = require("./helpers/interactionModes");
var isMutableProperty_1 = require("./helpers/isMutableProperty");
var manaFlattenTree_1 = require("./helpers/manaFlattenTree");
var scopifyElements_1 = require("./helpers/scopifyElements");
var upgradeBytecodeInPlace_1 = require("./helpers/upgradeBytecodeInPlace");
var Layout3D_1 = require("./Layout3D");
var ValueBuilder_1 = require("./ValueBuilder");
var assign_1 = require("./vendor/assign");
var pkg = require('./../package.json');
exports.VERSION = pkg.version;
var STRING_TYPE = 'string';
var OBJECT_TYPE = 'object';
var HAIKU_ID_ATTRIBUTE = 'haiku-id';
var DEFAULT_TIMELINE_NAME = 'Default';
var GENERIC_SELECTOR = '*';
var CSS_QUERY_MAPPING = {
    name: 'elementName',
    attributes: 'attributes',
    children: 'children',
};
var HaikuComponent = (function (_super) {
    __extends(HaikuComponent, _super);
    function HaikuComponent(bytecode, context, config, container) {
        var _this = _super.call(this) || this;
        if (typeof bytecode.template === STRING_TYPE) {
            console.warn('[haiku core] converting template xml string to object');
            bytecode.template = xmlToMana_1.default(bytecode.template);
        }
        if (!bytecode.template) {
            console.warn('[haiku core] adding missing template object');
            bytecode.template = { elementName: 'div', attributes: {}, children: [] };
        }
        if (!bytecode.timelines) {
            console.warn('[haiku core] adding missing timelines object');
            bytecode.timelines = {};
        }
        if (!bytecode.timelines[DEFAULT_TIMELINE_NAME]) {
            console.warn('[haiku core] adding missing default timeline');
            bytecode.timelines[DEFAULT_TIMELINE_NAME] = {};
        }
        if (!context) {
            throw new Error('Component requires a context');
        }
        if (!config) {
            throw new Error('Config options required');
        }
        if (!config.seed) {
            throw new Error('Seed value must be provided');
        }
        _this.PLAYER_VERSION = exports.VERSION;
        _this.CORE_VERSION = exports.VERSION;
        _this._bytecode = bytecode;
        assertTemplate(_this._bytecode.template);
        if (_this._bytecode.methods) {
            for (var methodNameGiven in _this._bytecode.methods) {
                if (!_this[methodNameGiven]) {
                    _this[methodNameGiven] = _this._bytecode.methods[methodNameGiven].bind(_this);
                }
            }
        }
        _this.routeEventToHandlerAndEmit(HaikuBase_1.GLOBAL_LISTENER_KEY, 'component:will-initialize', [_this]);
        try {
            upgradeBytecodeInPlace_1.default(_this._bytecode, {
                referenceUniqueness: (config.hotEditingMode)
                    ? void (0)
                    : Math.random().toString(36).slice(2),
            });
        }
        catch (e) {
            console.warn('[haiku core] caught error during attempt to upgrade bytecode in place');
        }
        _this._context = context;
        _this._builder = new ValueBuilder_1.default(_this);
        _this._states = {};
        _this.state = {};
        _this.assignConfig(config);
        _this._timelineInstances = {};
        _this._mutableTimelines = undefined;
        _this._hydrateMutableTimelines();
        _this._flatManaTree = [];
        _this.reinitializeTree(container);
        _this._needsFullFlush = false;
        _this._alwaysFlush = false;
        _this._lastDeltaPatches = null;
        _this.registeredEventHandlers = {};
        _this._matchedElementCache = {};
        _this._renderScopes = {};
        _this.nestedComponentElements = {};
        _this._hashTableOfIdsToElements = {};
        _this._horizonElements = {};
        _this._controlFlowData = {};
        _this._isDeactivated = false;
        _this._isSleeping = false;
        _this.startTimeline(DEFAULT_TIMELINE_NAME);
        _this.routeEventToHandlerAndEmit(HaikuBase_1.GLOBAL_LISTENER_KEY, 'component:did-initialize', [_this]);
        return _this;
    }
    HaikuComponent.prototype._markHorizonElement = function (virtualElement) {
        if (virtualElement && virtualElement.attributes) {
            var flexId = virtualElement.attributes[HAIKU_ID_ATTRIBUTE] || virtualElement.attributes.id;
            if (flexId) {
                this._horizonElements[flexId] = virtualElement;
            }
        }
    };
    HaikuComponent.prototype._isHorizonElement = function (virtualElement) {
        if (virtualElement && virtualElement.attributes) {
            var flexId = virtualElement.attributes[HAIKU_ID_ATTRIBUTE] || virtualElement.attributes.id;
            return !!this._horizonElements[flexId];
        }
        return false;
    };
    HaikuComponent.prototype._getRealElementAtId = function (flexId) {
        return this._hashTableOfIdsToElements[flexId];
    };
    HaikuComponent.prototype._addElementToHashTable = function (realElement, virtualElement) {
        if (virtualElement && virtualElement.attributes) {
            var flexId = virtualElement.attributes['haiku-id'] || virtualElement.attributes.id;
            if (flexId) {
                this._hashTableOfIdsToElements[flexId] = realElement;
            }
        }
    };
    HaikuComponent.prototype._didElementRenderSurrogate = function (virtualElement, surrogatePlacementKey, surrogateObject) {
        if (!virtualElement) {
            return false;
        }
        if (!virtualElement.attributes) {
            return false;
        }
        var flexId = virtualElement.attributes['haiku-id'] || virtualElement.attributes.id;
        if (!flexId) {
            return false;
        }
        if (!this._controlFlowData[flexId]) {
            return false;
        }
        if (!this._controlFlowData[flexId].renderedSurrogates) {
            return false;
        }
        return this._controlFlowData[flexId].renderedSurrogates[surrogatePlacementKey] === surrogateObject;
    };
    HaikuComponent.prototype._markElementSurrogateAsRendered = function (virtualElement, surrogatePlacementKey, surrogateObject) {
        var flexId = (virtualElement &&
            virtualElement.attributes && (virtualElement.attributes['haiku-id'] ||
            virtualElement.attributes.id));
        if (flexId) {
            if (!this._controlFlowData[flexId]) {
                this._controlFlowData[flexId] = {};
            }
            if (!this._controlFlowData[flexId].renderedSurrogates) {
                this._controlFlowData[flexId].renderedSurrogates = {};
            }
            this._controlFlowData[flexId].renderedSurrogates[surrogatePlacementKey] = surrogateObject;
        }
    };
    HaikuComponent.prototype.callRemount = function (incomingConfig, skipMarkForFullFlush) {
        this.routeEventToHandlerAndEmit(HaikuBase_1.GLOBAL_LISTENER_KEY, 'component:will-mount', [this]);
        if (incomingConfig) {
            this.assignConfig(incomingConfig);
        }
        if (!skipMarkForFullFlush) {
            this._markForFullFlush();
            this.clearCaches(null);
        }
        var timelineInstances = this.getTimelines();
        for (var timelineName in timelineInstances) {
            var timelineInstance = timelineInstances[timelineName];
            if (this.config.autoplay) {
                if (timelineName === DEFAULT_TIMELINE_NAME) {
                    timelineInstance.play({ skipMarkForFullFlush: skipMarkForFullFlush });
                }
            }
            else {
                timelineInstance.pause();
            }
        }
        this._context.contextMount();
        this.routeEventToHandlerAndEmit(HaikuBase_1.GLOBAL_LISTENER_KEY, 'component:did-mount', [this]);
    };
    HaikuComponent.prototype.callUnmount = function (incomingConfig) {
        if (incomingConfig) {
            this.assignConfig(incomingConfig);
        }
        var timelineInstances = this.getTimelines();
        for (var timelineName in timelineInstances) {
            var timelineInstance = timelineInstances[timelineName];
            timelineInstance.pause();
        }
        this._context.contextUnmount();
        this.routeEventToHandlerAndEmit(HaikuBase_1.GLOBAL_LISTENER_KEY, 'component:will-unmount', [this]);
    };
    HaikuComponent.prototype.assignConfig = function (incomingConfig) {
        this.config = Config_1.default.build(this.config || {}, incomingConfig || {});
        this._context.assignConfig(this.config, { skipComponentAssign: true });
        for (var timelineName in this._timelineInstances) {
            var timelineInstance = this._timelineInstances[timelineName];
            timelineInstance.assignOptions(this.config);
        }
        bindStates(this._states, this, this.config.states);
        bindEventHandlers(this, this.config.eventHandlers);
        assign_1.default(this._bytecode.timelines, this.config.timelines);
        return this;
    };
    HaikuComponent.prototype.set = function (key, value) {
        this.state[key] = value;
        return this;
    };
    HaikuComponent.prototype.get = function (key) {
        return this.state[key];
    };
    HaikuComponent.prototype.setState = function (states) {
        if (!states) {
            return this;
        }
        if (typeof states !== 'object') {
            return this;
        }
        for (var key in states) {
            this.set(key, states[key]);
        }
        return this;
    };
    HaikuComponent.prototype.getStates = function () {
        return this.state;
    };
    HaikuComponent.prototype.clearCaches = function (options) {
        if (options === void 0) { options = {}; }
        this._states = {};
        if (options['clearStates'] !== false) {
            bindStates(this._states, this, this.config.states);
        }
        if (options['clearEventHandlers'] !== false) {
            bindEventHandlers(this, this.config.eventHandlers);
        }
        this._needsFullFlush = false;
        this._lastDeltaPatches = null;
        this._renderScopes = {};
        this._controlFlowData = {};
        this._rehydrateFlatManaTree();
        this._matchedElementCache = {};
        this._builder.clearCaches(options);
        this._hydrateMutableTimelines();
        if (this._bytecode.timelines) {
            for (var timelineName in this._bytecode.timelines) {
                delete this._bytecode.timelines[timelineName].__max;
            }
        }
        return this;
    };
    HaikuComponent.prototype.getClock = function () {
        return this._context.getClock();
    };
    HaikuComponent.prototype.getTemplate = function () {
        return this._bytecode.template;
    };
    HaikuComponent.prototype.getTimelines = function () {
        return this.fetchTimelines();
    };
    HaikuComponent.prototype.fetchTimelines = function () {
        var names = Object.keys(this._bytecode.timelines);
        for (var i = 0; i < names.length; i++) {
            var name_1 = names[i];
            if (!name_1) {
                continue;
            }
            var descriptor = this.getTimelineDescriptor(name_1);
            var existing = this._timelineInstances[name_1];
            if (!existing) {
                this._timelineInstances[name_1] = new HaikuTimeline_1.default(this, name_1, descriptor, this.config);
            }
        }
        return this._timelineInstances;
    };
    HaikuComponent.prototype.getTimeline = function (name) {
        return this.getTimelines()[name];
    };
    HaikuComponent.prototype.getDefaultTimeline = function () {
        var timelines = this.getTimelines();
        return timelines[DEFAULT_TIMELINE_NAME];
    };
    HaikuComponent.prototype.stopAllTimelines = function () {
        for (var timelineName in this._timelineInstances) {
            this.stopTimeline(timelineName);
        }
    };
    HaikuComponent.prototype.startAllTimelines = function () {
        for (var timelineName in this._timelineInstances) {
            this.startTimeline(timelineName);
        }
    };
    HaikuComponent.prototype.startTimeline = function (timelineName) {
        var time = this._context.clock.getExplicitTime();
        var descriptor = this.getTimelineDescriptor(timelineName);
        var existing = this._timelineInstances[timelineName];
        if (existing) {
            existing.start(time, descriptor);
        }
        else {
            var fresh = new HaikuTimeline_1.default(this, timelineName, descriptor, this.config);
            fresh.start(time, descriptor);
            this._timelineInstances[timelineName] = fresh;
        }
    };
    HaikuComponent.prototype.stopTimeline = function (timelineName) {
        var time = this._context.clock.getExplicitTime();
        var descriptor = this.getTimelineDescriptor(timelineName);
        var existing = this._timelineInstances[timelineName];
        if (existing) {
            existing.stop(time, descriptor);
        }
    };
    HaikuComponent.prototype.getTimelineDescriptor = function (timelineName) {
        return this._bytecode.timelines[timelineName];
    };
    HaikuComponent.prototype.getInjectables = function (element) {
        var injectables = {};
        assign_1.default(injectables, this._builder.getSummonablesSchema(element));
        for (var key in this._states) {
            var type = this._states[key].type;
            if (!type) {
                type = typeof this._states[key];
            }
            injectables[key] = type;
        }
        return injectables;
    };
    HaikuComponent.prototype.deactivate = function () {
        this._isDeactivated = true;
        return this;
    };
    HaikuComponent.prototype.isDeactivated = function () {
        return this._isDeactivated;
    };
    HaikuComponent.prototype.sleepOn = function () {
        this._isSleeping = true;
        return this;
    };
    HaikuComponent.prototype.sleepOff = function () {
        this._isSleeping = false;
        return this;
    };
    HaikuComponent.prototype.isSleeping = function () {
        return this._isSleeping;
    };
    HaikuComponent.prototype.dump = function () {
        var metadata = this.getBytecodeMetadata();
        return metadata.relpath + ":" + this.getComponentId();
    };
    HaikuComponent.prototype.getBytecodeMetadata = function () {
        return this._bytecode.metadata;
    };
    HaikuComponent.prototype.getAddressableProperties = function (out) {
        var _this = this;
        if (out === void 0) { out = {}; }
        if (!this._bytecode.states) {
            return out;
        }
        var _loop_1 = function (name_2) {
            var state = this_1._bytecode.states[name_2];
            out[name_2] = {
                name: name_2,
                type: 'state',
                prefix: name_2,
                suffix: undefined,
                fallback: state.value,
                typedef: state.type,
                mock: state.mock,
                target: this_1,
                value: function () {
                    return _this.state[name_2];
                },
            };
        };
        var this_1 = this;
        for (var name_2 in this._bytecode.states) {
            _loop_1(name_2);
        }
        return out;
    };
    HaikuComponent.prototype.eachEventHandler = function (iteratee) {
        if (this._bytecode.eventHandlers) {
            for (var eventSelector in this._bytecode.eventHandlers) {
                for (var eventName in this._bytecode.eventHandlers[eventSelector]) {
                    iteratee(eventSelector, eventName, this._bytecode.eventHandlers[eventSelector][eventName]);
                }
            }
        }
    };
    HaikuComponent.prototype.routeEventToHandler = function (eventSelectorGiven, eventNameGiven, eventArgs) {
        var _this = this;
        this.eachEventHandler(function (eventSelector, eventName, _a) {
            var handler = _a.handler;
            if (eventNameGiven === eventName) {
                if (eventSelectorGiven === eventSelector ||
                    eventSelectorGiven === HaikuBase_1.GLOBAL_LISTENER_KEY) {
                    handler.apply(_this, eventArgs);
                    return;
                }
            }
        });
    };
    HaikuComponent.prototype.routeEventToHandlerAndEmit = function (eventSelectorGiven, eventNameGiven, eventArgs) {
        this.routeEventToHandler(eventSelectorGiven, eventNameGiven, eventArgs);
        this.emit.apply(this, [eventNameGiven].concat(eventArgs));
    };
    HaikuComponent.prototype._markForFullFlush = function () {
        this._needsFullFlush = true;
        return this;
    };
    HaikuComponent.prototype._unmarkForFullFlush = function () {
        this._needsFullFlush = false;
        return this;
    };
    HaikuComponent.prototype._shouldPerformFullFlush = function () {
        return this._needsFullFlush || this._alwaysFlush;
    };
    HaikuComponent.prototype.patch = function (container, patchOptions, skipCache) {
        if (skipCache === void 0) { skipCache = false; }
        if (this.isDeactivated()) {
            return {};
        }
        var time = this._context.clock.getExplicitTime();
        var timelinesRunning = [];
        for (var timelineName in this._timelineInstances) {
            var timeline = this._timelineInstances[timelineName];
            if (timeline.isPlaying()) {
                timeline.doUpdateWithGlobalClockTime(time);
            }
            if (timelineName === 'Default' || !timeline.isFinished()) {
                timelinesRunning.push(timeline);
            }
        }
        this._lastDeltaPatches = gatherDeltaPatches(this, this.getTemplate(), container, this._context, timelinesRunning, patchOptions, skipCache);
        for (var flexId in this.nestedComponentElements) {
            var compElement = this.nestedComponentElements[flexId];
            this._lastDeltaPatches[flexId] = compElement;
            compElement.__instance.patch(compElement, {});
        }
        return this._lastDeltaPatches;
    };
    HaikuComponent.prototype._getPrecalcedPatches = function () {
        if (this.isDeactivated()) {
            return {};
        }
        return this._lastDeltaPatches || {};
    };
    HaikuComponent.prototype._rehydrateFlatManaTree = function () {
        this._flatManaTree = manaFlattenTree_1.default(this.getTemplate(), CSS_QUERY_MAPPING);
        return this._flatManaTree;
    };
    HaikuComponent.prototype.renderUsingOwnContext = function () {
        return this._context.render();
    };
    HaikuComponent.prototype.reinitializeTree = function (container) {
        exports.initializeComponentTree(container, null, this._context, this);
    };
    HaikuComponent.prototype.render = function (container, renderOptions) {
        if (this.isDeactivated()) {
            return;
        }
        this.reinitializeTree(container);
        var time = this._context.clock.getExplicitTime();
        var prevFlatManaTreeLen = this._flatManaTree.length;
        this._rehydrateFlatManaTree();
        if (prevFlatManaTreeLen.length !== this._flatManaTree.length) {
            this._matchedElementCache = {};
        }
        for (var timelineName in this._timelineInstances) {
            var timeline = this._timelineInstances[timelineName];
            if (timeline.isPlaying()) {
                timeline.doUpdateWithGlobalClockTime(time);
            }
        }
        applyContextChanges(this, this.getTemplate(), container, this._context, renderOptions || {});
        var templateExpansion = expandTreeNode(this.getTemplate(), this, this._context);
        this._unmarkForFullFlush();
        return templateExpansion;
    };
    HaikuComponent.prototype.findElementsByHaikuId = function (componentId) {
        return findMatchingElementsByCssSelector('haiku:' + componentId, this._flatManaTree, this._matchedElementCache);
    };
    HaikuComponent.prototype._hydrateMutableTimelines = function () {
        this._mutableTimelines = {};
        if (this._bytecode.timelines) {
            for (var timelineName in this._bytecode.timelines) {
                for (var selector in this._bytecode.timelines[timelineName]) {
                    for (var propertyName in this._bytecode.timelines[timelineName][selector]) {
                        if (isMutableProperty_1.default(this._bytecode.timelines[timelineName][selector][propertyName], propertyName)) {
                            var timeline = this._mutableTimelines[timelineName] || {};
                            var propertyGroup = timeline[selector] || {};
                            this._mutableTimelines = __assign({}, this._mutableTimelines, (_a = {}, _a[timelineName] = __assign({}, timeline, (_b = {}, _b[selector] = __assign({}, propertyGroup, (_c = {}, _c[propertyName] = this._bytecode.timelines[timelineName][selector][propertyName], _c)), _b)), _a));
                        }
                    }
                }
            }
        }
        var _a, _b, _c;
    };
    HaikuComponent.prototype.addHotComponent = function (hotComponent) {
        if (!this._bytecode.timelines ||
            !this._bytecode.timelines[hotComponent.timelineName] ||
            !this._bytecode.timelines[hotComponent.timelineName][hotComponent.selector]) {
            return;
        }
        var propertyGroup = this._bytecode.timelines[hotComponent.timelineName][hotComponent.selector];
        var timeline = this._mutableTimelines[hotComponent.timelineName] || {};
        var mutablePropertyGroup = timeline[hotComponent.selector] || {};
        this._mutableTimelines = __assign({}, this._mutableTimelines, (_a = {}, _a[hotComponent.timelineName] = __assign({}, timeline, (_b = {}, _b[hotComponent.selector] = __assign({}, mutablePropertyGroup, hotComponent.propertyNames.reduce(function (hotProperties, propertyName) { return (hotProperties[propertyName] = propertyGroup[propertyName], hotProperties); }, {})), _b)), _a));
        var _a, _b;
    };
    return HaikuComponent;
}(HaikuElement_1.default));
exports.default = HaikuComponent;
function isBytecode(thing) {
    return thing && typeof thing === OBJECT_TYPE && thing.template;
}
exports.initializeComponentTree = function (node, host, context, instance) {
    if (node && typeof node === 'object') {
        node.__context = context;
    }
    if (isBytecode(node.elementName)) {
        if (instance) {
            node.__instance = instance;
        }
        if (!node.__instance) {
            node.__instance = new HaikuComponent(node.elementName, context, context.config, node);
        }
        HaikuElement_1.default['connectNodeWithElement'](node, node.__instance);
        if (host) {
            var flexId_1 = (node &&
                node.attributes &&
                (node.attributes[HAIKU_ID_ATTRIBUTE] || node.attributes.id));
            host.nestedComponentElements[flexId_1] = node;
            if (node.__instance) {
                if (node.__listener) {
                    node.__instance.off('*', node.__listener);
                }
                node.__listener = function (key) {
                    var args = [];
                    for (var _i = 1; _i < arguments.length; _i++) {
                        args[_i - 1] = arguments[_i];
                    }
                    host.routeEventToHandler("haiku:" + flexId_1, key, [node.instance].concat(args));
                };
                node.__instance.on('*', node.__listener);
            }
        }
    }
    if (node && typeof node === 'object' && node.children) {
        for (var i = 0; i < node.children.length; i++) {
            var child = node.children[i];
            if (child && typeof child === 'object') {
                child.__parent = node;
                exports.initializeComponentTree(child, host, context, null);
            }
        }
    }
};
function assertTemplate(template) {
    if (!template) {
        throw new Error('Empty template not allowed');
    }
    if (typeof template === OBJECT_TYPE) {
        if (template.attributes) {
            if (!template.attributes[HAIKU_ID_ATTRIBUTE]) {
                console.warn('[haiku core] bytecode template has no id');
            }
        }
        else {
            console.warn('[haiku core] bytecode template has no attributes');
        }
        if (!template.elementName) {
            console.warn('[haiku core] unexpected bytecode template format');
        }
        return template;
    }
    throw new Error('Unknown bytecode template format');
}
function bindEventHandlers(component, extraEventHandlers) {
    var allEventHandlers = assign_1.default({}, component._bytecode.eventHandlers, extraEventHandlers);
    for (var selector in allEventHandlers) {
        var handlerGroup = allEventHandlers[selector];
        for (var eventName in handlerGroup) {
            var eventHandlerDescriptor = handlerGroup[eventName];
            bindEventHandler(component, eventHandlerDescriptor, selector, eventName);
        }
    }
}
function bindEventHandler(component, eventHandlerDescriptor, selector, eventName) {
    if (eventHandlerDescriptor.original) {
        eventHandlerDescriptor.handler = eventHandlerDescriptor.original;
    }
    eventHandlerDescriptor.original = eventHandlerDescriptor.handler;
    eventHandlerDescriptor.handler = function (event) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        if (interactionModes_1.isPreviewMode(component.config.interactionMode)) {
            try {
                (_a = eventHandlerDescriptor.original).call.apply(_a, [component, event].concat(args));
            }
            catch (exception) {
                consoleErrorOnce_1.default(exception);
                return 1;
            }
        }
        var _a;
    };
}
function stateSpecValidityCheck(stateSpec, stateSpecName) {
    if (stateSpec.type === 'any' ||
        stateSpec.type === '*' ||
        stateSpec.type === undefined ||
        stateSpec.type === null) {
        return true;
    }
    if (stateSpec.type === 'event' || stateSpec.type === 'listener') {
        if (typeof stateSpec.value !== 'function' &&
            stateSpec.value !== null &&
            stateSpec.value !== undefined) {
            console.error('Property value `' +
                stateSpecName +
                '` must be an event listener function');
            return false;
        }
        return true;
    }
    if (stateSpec.type === 'array') {
        if (!Array.isArray(stateSpec.value)) {
            console.error('Property value `' + stateSpecName + '` must be an array');
            return false;
        }
    }
    else if (stateSpec.type === 'object') {
        if (stateSpec.value && typeof stateSpec.value !== 'object') {
            console.error('Property value `' + stateSpecName + '` must be an object');
            return false;
        }
    }
    else {
        if (typeof stateSpec.value !== stateSpec.type) {
            console.error('Property value `' + stateSpecName + '` must be a `' + stateSpec.type + '`');
            return false;
        }
    }
    return true;
}
function bindStates(statesTargetObject, component, extraStates) {
    var allStates = assign_1.default({}, component._bytecode.states, extraStates);
    for (var stateSpecName in allStates) {
        var stateSpec = allStates[stateSpecName];
        if (stateSpec.value === undefined) {
            console.error('Property `' +
                stateSpecName +
                '` cannot be undefined; use null for empty states');
            continue;
        }
        var isValid = stateSpecValidityCheck(stateSpec, stateSpecName);
        if (isValid) {
            statesTargetObject[stateSpecName] = stateSpec.value;
            defineSettableState(component, component.state, statesTargetObject, stateSpec, stateSpecName);
        }
    }
}
function defineSettableState(component, statesHostObject, statesTargetObject, stateSpec, stateSpecName) {
    Object.defineProperty(statesHostObject, stateSpecName, {
        configurable: true,
        get: function get() {
            return statesTargetObject[stateSpecName];
        },
        set: function set(inputValue) {
            if (stateSpec.setter) {
                statesTargetObject[stateSpecName] = stateSpec.setter.call(component, inputValue);
            }
            else {
                statesTargetObject[stateSpecName] = inputValue;
            }
            component.emit('state:set', stateSpecName, statesTargetObject[stateSpecName]);
            return statesTargetObject[stateSpecName];
        },
    });
}
function applyBehaviors(timelinesRunning, deltas, component, context, isPatchOperation, skipCache) {
    if (skipCache === void 0) { skipCache = false; }
    for (var i = 0; i < timelinesRunning.length; i++) {
        var timelineInstance = timelinesRunning[i];
        var timelineName = timelineInstance.getName();
        var timelineDescriptor = isPatchOperation
            ? component._mutableTimelines[timelineName]
            : component._bytecode.timelines[timelineName];
        if (typeof timelineDescriptor !== 'object') {
            continue;
        }
        var timelineTime = timelineInstance.getBoundedTime();
        for (var behaviorSelector in timelineDescriptor) {
            var propertiesGroup = timelineDescriptor[behaviorSelector];
            if (!propertiesGroup) {
                continue;
            }
            var matchingElementsForBehavior = findMatchingElementsByCssSelector(behaviorSelector, component._flatManaTree, component._matchedElementCache);
            if (!matchingElementsForBehavior || matchingElementsForBehavior.length < 1) {
                continue;
            }
            for (var j = 0; j < matchingElementsForBehavior.length; j++) {
                var matchingElement = matchingElementsForBehavior[j];
                var domId = matchingElement && matchingElement.attributes && matchingElement.attributes.id;
                var haikuId = matchingElement && matchingElement.attributes && matchingElement.attributes[HAIKU_ID_ATTRIBUTE];
                var flexId = haikuId || domId;
                var assembledOutputs = component._builder.build({}, timelineName, timelineTime, flexId, matchingElement, propertiesGroup, isPatchOperation, component, skipCache);
                if (assembledOutputs && deltas && flexId) {
                    deltas[flexId] = matchingElement;
                }
                if (assembledOutputs) {
                    for (var behaviorKey in assembledOutputs) {
                        var behaviorValue = assembledOutputs[behaviorKey];
                        applyPropertyToElement(matchingElement, behaviorKey, behaviorValue, context, component);
                    }
                }
            }
        }
    }
}
function gatherDeltaPatches(component, template, container, context, timelinesRunning, patchOptions, skipCache) {
    if (skipCache === void 0) { skipCache = false; }
    var deltas = {};
    Layout3D_1.default.initializeTreeAttributes(template, container);
    applyBehaviors(timelinesRunning, deltas, component, context, true, skipCache);
    if (patchOptions.sizing) {
        computeAndApplyPresetSizing(template, container, patchOptions.sizing, deltas);
    }
    for (var flexId in deltas) {
        var changedNode = deltas[flexId];
        computeAndApplyTreeLayouts(changedNode, changedNode.__parent, patchOptions, context);
    }
    return deltas;
}
function applyContextChanges(component, template, container, context, renderOptions) {
    var timelinesRunning = [];
    if (component._bytecode.timelines) {
        for (var timelineName in component._bytecode.timelines) {
            var timeline = component.getTimeline(timelineName);
            if (!timeline) {
                continue;
            }
            if (timeline.isFinished() && timelineName !== DEFAULT_TIMELINE_NAME) {
                continue;
            }
            timelinesRunning.push(timeline);
        }
    }
    Layout3D_1.default.initializeTreeAttributes(template, container);
    scopifyElements_1.default(template, null, null);
    applyBehaviors(timelinesRunning, null, component, context, false);
    component.eachEventHandler(function (eventSelector, eventName, _a) {
        var handler = _a.handler;
        if (component.registeredEventHandlers[eventName]) {
            return;
        }
        component.registeredEventHandlers[eventName] = true;
        component._context.renderer.mountEventListener(eventSelector, eventName, function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            component.routeEventToHandlerAndEmit(eventSelector, eventName, args);
        });
    });
    if (renderOptions.sizing) {
        computeAndApplyPresetSizing(template, container, renderOptions.sizing, null);
    }
    computeAndApplyTreeLayouts(template, container, renderOptions, context);
    return template;
}
function applyPropertyToElement(element, name, value, context, component) {
    var type;
    if (element.__instance) {
        type = 'div';
        var addressables = element.__instance.getAddressableProperties();
        if (addressables[name] !== undefined) {
            element.__instance.state[name] = value;
        }
        if (vanities_1.default[type] && vanities_1.default[type][name]) {
            vanities_1.default[type][name](name, element, value, context, component);
        }
    }
    else {
        type = element.elementName;
        if (vanities_1.default[type] && vanities_1.default[type][name]) {
            vanities_1.default[type][name](name, element, value, context, component);
        }
        else {
            element.attributes[name] = value;
        }
    }
}
function expandTreeNode(node, component, context) {
    if (!node || typeof node !== 'object') {
        return node;
    }
    if (node.__handlers) {
        for (var key in node.__handlers) {
            var handler = node.__handlers[key];
            if (!handler.__subscribed) {
                if (node.__instance) {
                    if (node.__instance.instance) {
                        node.__instance.instance.on(key, handler);
                        handler.__subscribed = true;
                    }
                }
            }
        }
    }
    if (node.__instance) {
        var subtree = node.__instance.render(node, node.__instance.config);
        if (subtree) {
            node.children = [
                expandTreeNode(subtree, node.__instance, context),
            ];
        }
        return node;
    }
    if (typeof node.elementName === STRING_TYPE && node.children) {
        for (var i = 0; i < node.children.length; i++) {
            node.children[i] = expandTreeNode(node.children[i], component, context);
        }
        return node;
    }
    return node;
}
function findMatchingElementsByCssSelector(selector, flatManaTree, cache) {
    if (cache[selector]) {
        return cache[selector];
    }
    return cache[selector] = cssQueryList_1.default(flatManaTree, selector, CSS_QUERY_MAPPING);
}
function computeAndApplyTreeLayouts(tree, container, options, context) {
    if (!tree || typeof tree === 'string') {
        return void 0;
    }
    computeAndApplyNodeLayout(tree, container);
    if (!tree.children || tree.children.length < 1) {
        return void 0;
    }
    for (var i = 0; i < tree.children.length; i++) {
        computeAndApplyTreeLayouts(tree.children[i], tree, options, context);
    }
}
function computeAndApplyNodeLayout(element, parent) {
    if (parent && parent.layout && parent.layout.computed) {
        var parentSize = parent.layout.computed.size;
        if (element.layout && element.layout.matrix) {
            element.layout.computed = Layout3D_1.default.computeLayout(element.layout, element.layout.matrix, parentSize);
        }
    }
}
function computeAndApplyPresetSizing(element, container, mode, deltas) {
    var elementWidth = element.layout.sizeAbsolute.x;
    var elementHeight = element.layout.sizeAbsolute.y;
    var containerWidth = container.layout.computed.size.x;
    var containerHeight = container.layout.computed.size.y;
    var scaleDiffX = containerWidth / elementWidth;
    var scaleDiffY = containerHeight / elementHeight;
    if (!element.attributes.style['transform-origin']) {
        element.attributes.style['transform-origin'] = '0% 0% 0px';
    }
    var changed = false;
    switch (mode) {
        case 'normal':
            if (element.layout.scale.x !== 1.0 || element.layout.scale.y !== 1.0) {
                changed = true;
                element.layout.scale.x = element.layout.scale.y = 1.0;
            }
            break;
        case 'stretch':
            if (scaleDiffX !== element.layout.scale.x) {
                changed = true;
                element.layout.scale.x = scaleDiffX;
            }
            if (scaleDiffY !== element.layout.scale.y) {
                changed = true;
                element.layout.scale.y = scaleDiffY;
            }
            break;
        case 'contain':
        case true:
            var containScaleToUse = null;
            if (~~(scaleDiffX * elementWidth) <= containerWidth &&
                ~~(scaleDiffX * elementHeight) <= containerHeight) {
                containScaleToUse = scaleDiffX;
            }
            if (~~(scaleDiffY * elementWidth) <= containerWidth &&
                ~~(scaleDiffY * elementHeight) <= containerHeight) {
                if (containScaleToUse === null) {
                    containScaleToUse = scaleDiffY;
                }
                else {
                    if (scaleDiffY >= containScaleToUse) {
                        containScaleToUse = scaleDiffY;
                    }
                }
            }
            if (element.layout.scale.x !== containScaleToUse) {
                changed = true;
                element.layout.scale.x = containScaleToUse;
            }
            if (element.layout.scale.y !== containScaleToUse) {
                changed = true;
                element.layout.scale.y = containScaleToUse;
            }
            var containTranslationOffsetX = -(containScaleToUse * elementWidth - containerWidth) / 2;
            var containTranslationOffsetY = -(containScaleToUse * elementHeight - containerHeight) / 2;
            if (element.layout.translation.x !== containTranslationOffsetX) {
                changed = true;
                element.layout.translation.x = containTranslationOffsetX;
            }
            if (element.layout.translation.y !== containTranslationOffsetY) {
                changed = true;
                element.layout.translation.y = containTranslationOffsetY;
            }
            break;
        case 'cover':
            var coverScaleToUse = null;
            if (~~(scaleDiffX * elementHeight) >= containerHeight) {
                coverScaleToUse = scaleDiffX;
            }
            else if (~~(scaleDiffY * elementWidth) >= containerWidth) {
                coverScaleToUse = scaleDiffY;
            }
            else {
                coverScaleToUse = Math.max(scaleDiffX, scaleDiffY);
            }
            if (element.layout.scale.x !== coverScaleToUse) {
                changed = true;
                element.layout.scale.x = coverScaleToUse;
            }
            if (element.layout.scale.y !== coverScaleToUse) {
                changed = true;
                element.layout.scale.y = coverScaleToUse;
            }
            var coverTranslationOffsetX = -(coverScaleToUse * elementWidth - containerWidth) / 2;
            var coverTranslationOffsetY = -(coverScaleToUse * elementHeight - containerHeight) / 2;
            if (element.layout.translation.x !== coverTranslationOffsetX) {
                changed = true;
                element.layout.translation.x = coverTranslationOffsetX;
            }
            if (element.layout.translation.y !== coverTranslationOffsetY) {
                changed = true;
                element.layout.translation.y = coverTranslationOffsetY;
            }
            break;
    }
    if (changed && deltas) {
        deltas[element.attributes[HAIKU_ID_ATTRIBUTE]] = element;
    }
}
HaikuComponent['PLAYER_VERSION'] = exports.VERSION;
HaikuComponent['CORE_VERSION'] = exports.VERSION;
//# sourceMappingURL=HaikuComponent.js.map