"use strict";
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 HaikuGlobal_1 = require("./HaikuGlobal");
var HaikuTimeline_1 = require("./HaikuTimeline");
var addElementToHashTable_1 = require("./helpers/addElementToHashTable");
var applyPropertyToElement_1 = require("./helpers/applyPropertyToElement");
var clone_1 = require("./helpers/clone");
var consoleErrorOnce_1 = require("./helpers/consoleErrorOnce");
var cssQueryList_1 = require("./helpers/cssQueryList");
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 SimpleEventEmitter_1 = require("./helpers/SimpleEventEmitter");
var upgradeBytecodeInPlace_1 = require("./helpers/upgradeBytecodeInPlace");
var initializeComponentTree_1 = require("./helpers/initializeComponentTree");
var Layout3D_1 = require("./Layout3D");
var ValueBuilder_1 = require("./ValueBuilder");
var assign_1 = require("./vendor/assign");
var pkg = require('./../package.json');
var VERSION = pkg.version;
var STRING_TYPE = 'string';
var OBJECT_TYPE = 'object';
var HAIKU_ID_ATTRIBUTE = 'haiku-id';
var DEFAULT_TIMELINE_NAME = 'Default';
function HaikuComponent(bytecode, context, config, metadata) {
    var _this = this;
    if (!bytecode.template) {
        console.warn('Adding missing template object to bytecode');
        bytecode.template = { elementName: 'div', attributes: {}, children: [] };
    }
    if (!bytecode.timelines) {
        console.warn('Adding missing timeline object to bytecode');
        bytecode.timelines = {};
    }
    if (!bytecode.timelines[DEFAULT_TIMELINE_NAME]) {
        console.warn('Adding missing default timeline to bytecode');
        bytecode.timelines[DEFAULT_TIMELINE_NAME] = {};
    }
    if (!context) {
        throw new Error('Component requires a context');
    }
    if (!config.options) {
        throw new Error('Config options required');
    }
    if (!config.options.seed) {
        throw new Error('Seed value must be provided');
    }
    SimpleEventEmitter_1.default.create(this);
    this.PLAYER_VERSION = VERSION;
    this.CORE_VERSION = VERSION;
    this.emit('haikuComponentWillInitialize', this);
    if (config.onHaikuComponentWillInitialize) {
        config.onHaikuComponentWillInitialize(this);
    }
    if (!config.options.hotEditingMode) {
        this._bytecode = clone_1.default(bytecode);
    }
    else {
        this._bytecode = bytecode;
    }
    upgradeBytecodeInPlace_1.default(this._bytecode, {
        referenceUniqueness: (config.options.hotEditingMode)
            ? void (0)
            : Math.random().toString(36).slice(2),
    });
    this._context = context;
    this.cache = {};
    this._builder = new ValueBuilder_1.default(this);
    this._states = {};
    this.state = {};
    this._stateChanges = {};
    this._anyStateChange = false;
    this._eventsFired = {};
    this._anyEventChange = false;
    this.assignConfig(config);
    this._metadata = metadata || {};
    this._timelineInstances = {};
    this._mutableTimelines = undefined;
    this._hydrateMutableTimelines();
    this._flatManaTree = [];
    if (!this._bytecode.template.attributes[HAIKU_ID_ATTRIBUTE]) {
        console.warn('[haiku core] bytecode template has no id');
    }
    assertTemplate(this._bytecode.template);
    if (!config.options.hotEditingMode) {
        this._template = cloneTemplate(this._bytecode.template);
    }
    else {
        this._template = this._bytecode.template;
    }
    this._needsFullFlush = false;
    this._alwaysFlush = false;
    this._lastTemplateExpansion = null;
    this._lastDeltaPatches = null;
    this._matchedElementCache = {};
    this._renderScopes = {};
    this._doesEmitEventsVerbosely = false;
    this._frameEventListeners = {};
    this._nestedComponentElements = {};
    this._hashTableOfIdsToElements = {};
    this._registeredElementEventListeners = {};
    this._horizonElements = {};
    this._controlFlowData = {};
    this.on('timeline:tick', function (timelineName, timelineFrame, timelineTime) {
        if (_this._frameEventListeners[timelineName]) {
            if (_this._frameEventListeners[timelineName][timelineFrame]) {
                for (var i = 0; i < _this._frameEventListeners[timelineName][timelineFrame].length; i++) {
                    _this._frameEventListeners[timelineName][timelineFrame][i](timelineFrame, timelineTime);
                }
            }
        }
    });
    this.emit('haikuComponentDidInitialize', this);
    if (config.onHaikuComponentDidInitialize) {
        config.onHaikuComponentDidInitialize(this);
    }
    this._isDeactivated = false;
    this._isSleeping = false;
    this._entityIndex = HaikuComponent['components'].push(this) - 1;
}
exports.default = HaikuComponent;
HaikuComponent['PLAYER_VERSION'] = VERSION;
HaikuComponent['CORE_VERSION'] = VERSION;
HaikuComponent['components'] = [];
HaikuGlobal_1.default['HaikuComponent'] = HaikuComponent;
HaikuComponent.prototype._markHorizonElement = function _markHorizonElement(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 _isHorizonElement(virtualElement) {
    if (virtualElement && virtualElement.attributes) {
        var flexId = virtualElement.attributes[HAIKU_ID_ATTRIBUTE] || virtualElement.attributes.id;
        return !!this._horizonElements[flexId];
    }
    return false;
};
HaikuComponent.prototype._getRealElementsAtId = function _getRealElementsAtId(flexId) {
    if (!this._hashTableOfIdsToElements[flexId]) {
        return [];
    }
    return this._hashTableOfIdsToElements[flexId];
};
HaikuComponent.prototype._addElementToHashTable = function _addElementToHashTable(realElement, virtualElement) {
    addElementToHashTable_1.default(this._hashTableOfIdsToElements, realElement, virtualElement);
    return this;
};
HaikuComponent.prototype._didElementRenderSurrogate = function _didElementRenderSurrogate(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 _markElementSurrogateAsRendered(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 _callRemount(incomingConfig, skipMarkForFullFlush) {
    this.emit('haikuComponentWillMount', this);
    if (this.config.onHaikuComponentWillMount) {
        this.config.onHaikuComponentWillMount(this);
    }
    if (incomingConfig) {
        this.assignConfig(incomingConfig);
    }
    if (!skipMarkForFullFlush) {
        this._markForFullFlush();
        this.clearCaches();
    }
    var timelineInstances = this.getTimelines();
    for (var timelineName in timelineInstances) {
        var timelineInstance = timelineInstances[timelineName];
        if (this.config.options.autoplay) {
            if (timelineName === DEFAULT_TIMELINE_NAME) {
                timelineInstance.play({ skipMarkForFullFlush: skipMarkForFullFlush });
            }
        }
        else {
            timelineInstance.pause();
        }
    }
    this._context.contextMount();
    this.emit('haikuComponentDidMount', this);
    if (this.config.onHaikuComponentDidMount) {
        this.config.onHaikuComponentDidMount(this);
    }
};
HaikuComponent.prototype.callUnmount = function _callUnmount(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.emit('haikuComponentWillUnmount', this);
    if (this.config.onHaikuComponentWillUnmount) {
        this.config.onHaikuComponentWillUnmount(this);
    }
};
HaikuComponent.prototype.assignConfig = function _assignConfig(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.options);
    }
    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 set(key, value) {
    this.state[key] = value;
    return this;
};
HaikuComponent.prototype.get = function get(key) {
    return this.state[key];
};
HaikuComponent.prototype.setState = function setState(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 getStates() {
    return this.state;
};
HaikuComponent.prototype.clearCaches = function clearCaches(options) {
    this._states = {};
    if (!options || (options && options.clearStates !== false)) {
        bindStates(this._states, this, this.config.states);
    }
    if (options && options.clearPreviouslyRegisteredEventListeners) {
        this.clearRegisteredElementEventListeners();
    }
    if (!options || (options && options.clearEventHandlers !== false)) {
        bindEventHandlers(this, this.config.eventHandlers);
    }
    this._stateChanges = {};
    this._anyStateChange = false;
    this._eventsFired = {};
    this._anyEventChange = false;
    this._needsFullFlush = false;
    this._lastTemplateExpansion = null;
    this._lastDeltaPatches = null;
    this._renderScopes = {};
    this._controlFlowData = {};
    this._rehydrateFlatManaTree();
    this._matchedElementCache = {};
    this._clearDetectedEventsFired();
    this._clearDetectedInputChanges();
    this._builder.clearCaches(options);
    this._hydrateMutableTimelines();
    this.cache = {};
    if (this._bytecode.timelines) {
        for (var timelineName in this._bytecode.timelines) {
            delete this._bytecode.timelines[timelineName].__max;
        }
    }
    return this;
};
HaikuComponent.prototype.getClock = function getClock() {
    return this._context.getClock();
};
HaikuComponent.prototype.getTimelines = function getTimelines() {
    return this._fetchTimelines();
};
HaikuComponent.prototype._fetchTimelines = function _fetchTimelines() {
    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.options);
        }
    }
    return this._timelineInstances;
};
HaikuComponent.prototype.getTimeline = function getTimeline(name) {
    return this.getTimelines()[name];
};
HaikuComponent.prototype.getDefaultTimeline = function getDefaultTimeline() {
    var timelines = this.getTimelines();
    return timelines[DEFAULT_TIMELINE_NAME];
};
HaikuComponent.prototype.stopAllTimelines = function stopAllTimelines() {
    for (var timelineName in this._timelineInstances) {
        this.stopTimeline(timelineName);
    }
};
HaikuComponent.prototype.startAllTimelines = function startAllTimelines() {
    for (var timelineName in this._timelineInstances) {
        this.startTimeline(timelineName);
    }
};
HaikuComponent.prototype.startTimeline = function startTimeline(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.options);
        fresh.start(time, descriptor);
        this._timelineInstances[timelineName] = fresh;
    }
};
HaikuComponent.prototype.stopTimeline = function startTimeline(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 _getTimelineDescriptor(timelineName) {
    return this._bytecode.timelines[timelineName];
};
HaikuComponent.prototype._getInjectables = function _getInjectables(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._getTopLevelElement = function _getTopLevelElement() {
    return this._template;
};
HaikuComponent.prototype.deactivate = function deactivate() {
    this._isDeactivated = true;
    return this;
};
HaikuComponent.prototype.isDeactivated = function isDeactivated() {
    return this._isDeactivated;
};
HaikuComponent.prototype.sleepOn = function sleepOn() {
    this._isSleeping = true;
    return this;
};
HaikuComponent.prototype.sleepOff = function sleepOff() {
    this._isSleeping = false;
    return this;
};
HaikuComponent.prototype.isSleeping = function isSleeping() {
    return this._isSleeping;
};
HaikuComponent.prototype.clearRegisteredElementEventListeners = function clearRegisteredElementEventListeners() {
    for (var flexId in this._registeredElementEventListeners) {
        for (var eventName in this._registeredElementEventListeners[flexId]) {
            var _a = this._registeredElementEventListeners[flexId][eventName], target = _a.target, handler = _a.handler;
            if (target && handler && this._context._renderer.removeListener) {
                this._context._renderer.removeListener(target, handler, eventName);
            }
            delete this._registeredElementEventListeners[flexId][eventName];
        }
    }
    this._frameEventListeners = {};
};
HaikuComponent.prototype.hasRegisteredListenerOnElement = function hasRegisteredListenerOnElement(virtualElement, eventName) {
    var flexId = virtualElement.attributes[HAIKU_ID_ATTRIBUTE] || virtualElement.attributes.id;
    if (!flexId) {
        return false;
    }
    return this._registeredElementEventListeners[flexId] && this._registeredElementEventListeners[flexId][eventName];
};
HaikuComponent.prototype.markDidRegisterListenerOnElement = function markDidRegisterListenerOnElement(virtualElement, domElement, eventName, listenerFunction) {
    var flexId = virtualElement.attributes[HAIKU_ID_ATTRIBUTE] || virtualElement.attributes.id;
    if (!flexId) {
        return this;
    }
    if (!this._registeredElementEventListeners[flexId]) {
        this._registeredElementEventListeners[flexId] = {};
    }
    this._registeredElementEventListeners[flexId][eventName] = {
        handler: listenerFunction,
        target: domElement,
    };
    return this;
};
HaikuComponent.prototype.dump = function dump() {
    var metadata = this.getBytecodeMetadata();
    return metadata.relpath + ":" + this.getComponentId() + "[" + this._entityIndex + "]";
};
HaikuComponent.prototype.getComponentId = function getComponentId() {
    return this._bytecode.template.attributes[HAIKU_ID_ATTRIBUTE];
};
HaikuComponent.prototype.getBytecodeMetadata = function getBytecodeMetadata() {
    return this._bytecode.metadata;
};
HaikuComponent.prototype.getAddressableProperties = function getAddressableProperties(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;
};
function cloneTemplate(mana) {
    if (!mana) {
        return mana;
    }
    if (typeof mana === STRING_TYPE) {
        return mana;
    }
    var out = {
        elementName: mana.elementName,
        attributes: null,
        children: null,
    };
    if (mana.attributes) {
        out.attributes = {};
        for (var key in mana.attributes) {
            if (mana.attributes[key] && typeof mana.attributes[key] === 'object') {
                out.attributes[key] = {};
                for (var subkey in mana.attributes[key]) {
                    out.attributes[key][subkey] = mana.attributes[key][subkey];
                }
            }
            else {
                out.attributes[key] = mana.attributes[key];
            }
        }
    }
    if (mana.children) {
        out.children = [];
        for (var i = 0; i < mana.children.length; i++) {
            out.children[i] = cloneTemplate(mana.children[i]);
        }
    }
    return out;
}
function assertTemplate(template) {
    if (!template) {
        throw new Error('Empty template not allowed');
    }
    if (typeof template === OBJECT_TYPE) {
        if (!template.elementName) {
            console.warn('[haiku core] warning: saw 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 _wrappedEventHandler(event) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        if (interactionModes_1.isPreviewMode(component.config.options.interactionMode)) {
            component._anyEventChange = true;
            if (!component._eventsFired[selector]) {
                component._eventsFired[selector] = {};
            }
            component._eventsFired[selector][eventName] =
                event || true;
            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) {
            component._stateChanges[stateSpecName] = inputValue;
            component._anyStateChange = true;
            if (stateSpec.setter) {
                statesTargetObject[stateSpecName] = stateSpec.setter.call(component, inputValue);
            }
            else {
                statesTargetObject[stateSpecName] = inputValue;
            }
            if (component._doesEmitEventsVerbosely) {
                component.emit('state:set', stateSpecName, statesTargetObject[stateSpecName], statesTargetObject);
            }
            return statesTargetObject[stateSpecName];
        },
    });
}
HaikuComponent.prototype._markForFullFlush = function _markForFullFlush() {
    this._needsFullFlush = true;
    return this;
};
HaikuComponent.prototype._unmarkForFullFlush = function _unmarkForFullFlush() {
    this._needsFullFlush = false;
    return this;
};
HaikuComponent.prototype._shouldPerformFullFlush = function _shouldPerformFullFlush() {
    return this._needsFullFlush || this._alwaysFlush;
};
HaikuComponent.prototype._clearDetectedEventsFired = function _clearDetectedEventsFired() {
    this._anyEventChange = false;
    this._eventsFired = {};
    return this;
};
HaikuComponent.prototype._clearDetectedInputChanges = function _clearDetectedInputChanges() {
    this._anyStateChange = false;
    this._stateChanges = {};
    return this;
};
HaikuComponent.prototype.patch = function patch(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._template, 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, {});
    }
    this._clearDetectedEventsFired();
    this._clearDetectedInputChanges();
    return this._lastDeltaPatches;
};
HaikuComponent.prototype._getPrecalcedPatches = function _getPrecalcedPatches() {
    if (this.isDeactivated()) {
        return {};
    }
    return this._lastDeltaPatches || {};
};
HaikuComponent.prototype._rehydrateFlatManaTree = function _rehydrateFlatManaTree() {
    this._flatManaTree = manaFlattenTree_1.default(this._template, CSS_QUERY_MAPPING);
    return this._flatManaTree;
};
HaikuComponent.prototype.render = function render(container, renderOptions) {
    if (this.isDeactivated()) {
        return void 0;
    }
    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._template, container, this._context, renderOptions || {});
    this._lastTemplateExpansion = expandTreeElement(this._template, this, this._context);
    this._unmarkForFullFlush();
    return this._lastTemplateExpansion;
};
HaikuComponent.prototype.findElementsByHaikuId = function findElementsByHaikuId(componentId) {
    return findMatchingElementsByCssSelector('haiku:' + componentId, this._flatManaTree, this._matchedElementCache);
};
HaikuComponent.prototype._hydrateMutableTimelines = function _hydrateMutableTimelines() {
    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;
};
function bindContextualEventHandlers(component) {
    if (component._bytecode.eventHandlers) {
        for (var eventSelector in component._bytecode.eventHandlers) {
            var eventHandlerGroup = component._bytecode.eventHandlers[eventSelector];
            for (var eventName1 in eventHandlerGroup) {
                var eventHandlerSpec1 = eventHandlerGroup[eventName1];
                if (!eventHandlerSpec1.handler.__subscribed && !eventHandlerSpec1.handler.__external) {
                    if (eventName1 === 'component:will-mount') {
                        component.on('haikuComponentWillMount', eventHandlerSpec1.handler);
                        eventHandlerSpec1.handler.__subscribed = true;
                        continue;
                    }
                    if (eventName1 === 'component:did-mount') {
                        component.on('haikuComponentDidMount', eventHandlerSpec1.handler);
                        eventHandlerSpec1.handler.__subscribed = true;
                        continue;
                    }
                    if (eventName1 === 'component:will-unmount') {
                        component.on('haikuComponentWillUnmount', eventHandlerSpec1.handler);
                        eventHandlerSpec1.handler.__subscribed = true;
                        continue;
                    }
                    var namePieces = eventName1.split(':');
                    if (namePieces.length > 1) {
                        if (namePieces[0] === 'timeline') {
                            var timelineNamePiece = namePieces[1];
                            var frameValuePiece = parseInt(namePieces[2], 10);
                            if (!component._frameEventListeners[timelineNamePiece]) {
                                component._frameEventListeners[timelineNamePiece] = {};
                            }
                            if (!component._frameEventListeners[timelineNamePiece][frameValuePiece]) {
                                component._frameEventListeners[timelineNamePiece][frameValuePiece] = [];
                            }
                            component._frameEventListeners[timelineNamePiece][frameValuePiece].push(eventHandlerSpec1.handler);
                            eventHandlerSpec1.handler.__subscribed = true;
                            continue;
                        }
                    }
                    eventHandlerSpec1.handler.__external = true;
                }
            }
            var matchingElementsForEvents = findMatchingElementsByCssSelector(eventSelector, component._flatManaTree, component._matchedElementCache);
            if (!matchingElementsForEvents || matchingElementsForEvents.length < 1) {
                continue;
            }
            for (var k = 0; k < matchingElementsForEvents.length; k++) {
                for (var eventName in eventHandlerGroup) {
                    var eventHandlerSpec = eventHandlerGroup[eventName];
                    if (!eventHandlerSpec.__subscribed) {
                        applyHandlerToElement(matchingElementsForEvents[k], eventName, eventHandlerSpec.handler);
                    }
                }
            }
        }
    }
}
function applyBehaviors(timelinesRunning, deltas, component, context, isPatchOperation, skipCache) {
    if (skipCache === void 0) { skipCache = false; }
    if (!isPatchOperation) {
        bindContextualEventHandlers(component);
    }
    for (var i = 0; i < timelinesRunning.length; i++) {
        var timelineInstance = timelinesRunning[i];
        var timelineName = timelineInstance.getName();
        var timelineDescriptor = isPatchOperation && !component.config.options.hotEditingMode
            ? 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 && assembledOutputs.transform) {
                    matchingElement.__transformed = true;
                }
                if (assembledOutputs && deltas && flexId) {
                    deltas[flexId] = matchingElement;
                }
                if (assembledOutputs) {
                    for (var behaviorKey in assembledOutputs) {
                        var behaviorValue = assembledOutputs[behaviorKey];
                        applyPropertyToElement_1.default(matchingElement, behaviorKey, behaviorValue, context, component);
                    }
                }
            }
        }
    }
}
function gatherDeltaPatches(component, template, container, context, timelinesRunning, patchOptions, skipCache) {
    if (skipCache === void 0) { skipCache = false; }
    Layout3D_1.default.initializeTreeAttributes(template, container);
    initializeComponentTree_1.default(template, component, context, null);
    var deltas = {};
    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);
    initializeComponentTree_1.default(template, component, context, null);
    scopifyElements_1.default(template, null, null);
    applyBehaviors(timelinesRunning, null, component, context, false);
    if (renderOptions.sizing) {
        computeAndApplyPresetSizing(template, container, renderOptions.sizing, null);
    }
    computeAndApplyTreeLayouts(template, container, renderOptions, context);
    return template;
}
function expandTreeElement(element, component, context) {
    if (element.__handlers) {
        for (var key in element.__handlers) {
            var handler = element.__handlers[key];
            if (!handler.__subscribed) {
                if (element.__instance) {
                    if (element.__instance.instance) {
                        element.__instance.instance.on(key, handler);
                        handler.__subscribed = true;
                    }
                }
            }
        }
    }
    if (element.__instance) {
        var wrapper = shallowCloneComponentTreeElement(element);
        var surrogates = wrapper.children;
        var subtree = element.__instance.render(element, element.__instance.config.options, surrogates);
        if (subtree) {
            var expansion = expandTreeElement(subtree, element.__instance, context);
            wrapper.children = [expansion];
        }
        else {
            wrapper.children = [];
        }
        return wrapper;
    }
    if (typeof element.elementName === STRING_TYPE) {
        var copy = shallowCloneComponentTreeElement(element);
        if (element.children && element.children.length > 0) {
            for (var i = 0; i < element.children.length; i++) {
                var child = element.children[i];
                copy.children[i] = expandTreeElement(child, component, context);
            }
        }
        return copy;
    }
    return element;
}
function shallowCloneComponentTreeElement(element) {
    var clone = {
        __instance: null,
        __handlers: null,
        __transformed: null,
        __parent: null,
        __scope: null,
        __target: null,
        layout: null,
        elementName: null,
        attributes: null,
        children: null,
    };
    clone.__instance = element.__instance;
    clone.__handlers = element.__handlers;
    clone.__transformed = element.__transformed;
    clone.__parent = element.__parent;
    clone.__scope = element.__scope;
    clone.__target = element.__target;
    clone.layout = element.layout;
    clone.elementName = element.elementName;
    clone.attributes = {};
    for (var key in element.attributes) {
        clone.attributes[key] = element.attributes[key];
    }
    clone.children = [];
    return clone;
}
var CSS_QUERY_MAPPING = {
    name: 'elementName',
    attributes: 'attributes',
    children: 'children',
};
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 applyHandlerToElement(match, name, fn) {
    if (!match.__handlers) {
        match.__handlers = {};
    }
    match.__handlers[name] = fn;
    return match;
}
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'] = 'top left';
    }
    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;
    }
}
//# sourceMappingURL=HaikuComponent.js.map