(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.Inferno = global.Inferno || {})));
}(this, (function (exports) { 'use strict';

    var NO_OP = '$NO_OP';
    var ERROR_MSG = 'a runtime error occured! Use Inferno in development environment to find the error.';
    // This should be boolean and not reference to window.document
    var isBrowser = !!(typeof window !== 'undefined' && window.document);
    // this is MUCH faster than .constructor === Array and instanceof Array
    // in Node 7 and the later versions of V8, slower in older versions though
    var isArray = Array.isArray;
    function isStringOrNumber(o) {
        var type = typeof o;
        return type === 'string' || type === 'number';
    }
    function isNullOrUndef(o) {
        return isUndefined(o) || isNull(o);
    }
    function isInvalid(o) {
        return isNull(o) || o === false || isTrue(o) || isUndefined(o);
    }
    function isFunction(o) {
        return typeof o === 'function';
    }
    function isString(o) {
        return typeof o === 'string';
    }
    function isNumber(o) {
        return typeof o === 'number';
    }
    function isNull(o) {
        return o === null;
    }
    function isTrue(o) {
        return o === true;
    }
    function isUndefined(o) {
        return o === void 0;
    }
    function isDefined(o) {
        return o !== void 0;
    }
    function isObject(o) {
        return typeof o === 'object';
    }
    function throwError(message) {
        if (!message) {
            message = ERROR_MSG;
        }
        throw new Error(("Inferno Error: " + message));
    }
    function warning(message) {
        // tslint:disable-next-line:no-console
        console.error(message);
    }
    function combineFrom(first, second) {
        var out = {};
        if (first) {
            for (var key in first) {
                out[key] = first[key];
            }
        }
        if (second) {
            for (var key$1 in second) {
                out[key$1] = second[key$1];
            }
        }
        return out;
    }

    function getTagName(input) {
        var tagName;
        if (isArray(input)) {
            var arrayText = input.length > 3 ? input.slice(0, 3).toString() + ',...' : input.toString();
            tagName = 'Array(' + arrayText + ')';
        }
        else if (isStringOrNumber(input)) {
            tagName = 'Text(' + input + ')';
        }
        else if (isInvalid(input)) {
            tagName = 'InvalidVNode(' + input + ')';
        }
        else {
            var flags = input.flags;
            if (flags & 481 /* Element */) {
                tagName = "<" + (input.type) + (input.className ? ' class="' + input.className + '"' : '') + ">";
            }
            else if (flags & 16 /* Text */) {
                tagName = "Text(" + (input.children) + ")";
            }
            else if (flags & 1024 /* Portal */) {
                tagName = "Portal*";
            }
            else {
                var type = input.type;
                // Fallback for IE
                var componentName = type.name || type.displayName || type.constructor.name || (type.toString().match(/^function\s*([^\s(]+)/) || [])[1];
                tagName = "<" + componentName + " />";
            }
        }
        return '>> ' + tagName + '\n';
    }
    function DEV_ValidateKeys(vNodeTree, vNode, forceKeyed) {
        var foundKeys = [];
        for (var i = 0, len = vNodeTree.length; i < len; i++) {
            var childNode = vNodeTree[i];
            if (isArray(childNode)) {
                return 'Encountered ARRAY in mount, array must be flattened, or normalize used. Location: \n' + getTagName(childNode);
            }
            if (isInvalid(childNode)) {
                if (forceKeyed) {
                    return 'Encountered invalid node when preparing to keyed algorithm. Location: \n' + getTagName(childNode);
                }
                else if (foundKeys.length !== 0) {
                    return 'Encountered invalid node with mixed keys. Location: \n' + getTagName(childNode);
                }
                continue;
            }
            if (typeof childNode === 'object') {
                childNode.isValidated = true;
            }
            var key = childNode.key;
            if (!isNullOrUndef(key) && !isStringOrNumber(key)) {
                return 'Encountered child vNode where key property is not string or number. Location: \n' + getTagName(childNode);
            }
            var children = childNode.children;
            var childFlags = childNode.childFlags;
            if (!isInvalid(children)) {
                var val = (void 0);
                if (childFlags & 12 /* MultipleChildren */) {
                    val = DEV_ValidateKeys(children, childNode, childNode.childFlags & 8 /* HasKeyedChildren */);
                }
                else if (childFlags === 2 /* HasVNodeChildren */) {
                    val = DEV_ValidateKeys([children], childNode, childNode.childFlags & 8 /* HasKeyedChildren */);
                }
                if (val) {
                    val += getTagName(childNode);
                    return val;
                }
            }
            if (forceKeyed && isNullOrUndef(key)) {
                return ('Encountered child without key during keyed algorithm. If this error points to Array make sure children is flat list. Location: \n' +
                    getTagName(childNode));
            }
            else if (!forceKeyed && isNullOrUndef(key)) {
                if (foundKeys.length !== 0) {
                    return 'Encountered children with key missing. Location: \n' + getTagName(childNode);
                }
                continue;
            }
            if (foundKeys.indexOf(key) > -1) {
                return 'Encountered two children with same key: {' + key + '}. Location: \n' + getTagName(childNode);
            }
            foundKeys.push(key);
        }
    }
    function validateVNodeElementChildren(vNode) {
        {
            if (vNode.childFlags & 1 /* HasInvalidChildren */) {
                return;
            }
            if (vNode.flags & 64 /* InputElement */) {
                throwError("input elements can't have children.");
            }
            if (vNode.flags & 128 /* TextareaElement */) {
                throwError("textarea elements can't have children.");
            }
            if (vNode.flags & 481 /* Element */) {
                var voidTypes = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
                var tag = vNode.type.toLowerCase();
                if (tag === 'media') {
                    throwError("media elements can't have children.");
                }
                var idx = voidTypes.indexOf(tag);
                if (idx !== -1) {
                    throwError(((voidTypes[idx]) + " elements can't have children."));
                }
            }
        }
    }
    function validateKeys(vNode) {
        {
            // Checks if there is any key missing or duplicate keys
            if (vNode.isValidated === false && vNode.children && vNode.flags & 481 /* Element */) {
                var error = DEV_ValidateKeys(Array.isArray(vNode.children) ? vNode.children : [vNode.children], vNode, (vNode.childFlags & 8 /* HasKeyedChildren */) > 0);
                if (error) {
                    throwError(error + getTagName(vNode));
                }
            }
            vNode.isValidated = true;
        }
    }

    var keyPrefix = '$';
    function getVNode(childFlags, children, className, flags, key, props, ref, type) {
        return {
            childFlags: childFlags,
            children: children,
            className: className,
            dom: null,
            flags: flags,
            isValidated: false,
            key: key === void 0 ? null : key,
            parentVNode: null,
            props: props === void 0 ? null : props,
            ref: ref === void 0 ? null : ref,
            type: type
        };
    }
    function createVNode(flags, type, className, children, childFlags, props, key, ref) {
        {
            if (flags & 14 /* Component */) {
                throwError('Creating Component vNodes using createVNode is not allowed. Use Inferno.createComponentVNode method.');
            }
        }
        var childFlag = childFlags === void 0 ? 1 /* HasInvalidChildren */ : childFlags;
        var vNode = getVNode(childFlag, children, className, flags, key, props, ref, type);
        var optsVNode = options.createVNode;
        if (typeof optsVNode === 'function') {
            optsVNode(vNode);
        }
        if (childFlag === 0 /* UnknownChildren */) {
            normalizeChildren(vNode, vNode.children);
        }
        {
            validateVNodeElementChildren(vNode);
        }
        return vNode;
    }
    function createComponentVNode(flags, type, props, key, ref) {
        {
            if (flags & 1 /* HtmlElement */) {
                throwError('Creating element vNodes using createComponentVNode is not allowed. Use Inferno.createVNode method.');
            }
        }
        if ((flags & 2 /* ComponentUnknown */) > 0) {
            flags = isDefined(type.prototype) && isFunction(type.prototype.render) ? 4 /* ComponentClass */ : 8 /* ComponentFunction */;
        }
        // set default props
        var defaultProps = type.defaultProps;
        if (!isNullOrUndef(defaultProps)) {
            if (!props) {
                props = {}; // Props can be referenced and modified at application level so always create new object
            }
            for (var prop in defaultProps) {
                if (isUndefined(props[prop])) {
                    props[prop] = defaultProps[prop];
                }
            }
        }
        if ((flags & 8 /* ComponentFunction */) > 0) {
            var defaultHooks = type.defaultHooks;
            if (!isNullOrUndef(defaultHooks)) {
                if (!ref) {
                    // As ref cannot be referenced from application level, we can use the same refs object
                    ref = defaultHooks;
                }
                else {
                    for (var prop$1 in defaultHooks) {
                        if (isUndefined(ref[prop$1])) {
                            ref[prop$1] = defaultHooks[prop$1];
                        }
                    }
                }
            }
        }
        var vNode = getVNode(1 /* HasInvalidChildren */, null, null, flags, key, props, ref, type);
        var optsVNode = options.createVNode;
        if (isFunction(optsVNode)) {
            optsVNode(vNode);
        }
        return vNode;
    }
    function createTextVNode(text, key) {
        return getVNode(1 /* HasInvalidChildren */, isNullOrUndef(text) ? '' : text, null, 16 /* Text */, key, null, null, null);
    }
    function normalizeProps(vNode) {
        var props = vNode.props;
        if (props) {
            if (vNode.flags & 481 /* Element */) {
                if (isDefined(props.children) && isNullOrUndef(vNode.children)) {
                    normalizeChildren(vNode, props.children);
                }
                if (isDefined(props.className)) {
                    vNode.className = props.className || null;
                    props.className = undefined;
                }
            }
            if (isDefined(props.key)) {
                vNode.key = props.key;
                props.key = undefined;
            }
            if (isDefined(props.ref)) {
                vNode.ref = props.ref;
                props.ref = undefined;
            }
        }
        return vNode;
    }
    function directClone(vNodeToClone) {
        var newVNode;
        var flags = vNodeToClone.flags;
        if (flags & 14 /* Component */) {
            var props;
            var propsToClone = vNodeToClone.props;
            if (!isNull(propsToClone)) {
                props = {};
                for (var key in propsToClone) {
                    props[key] = propsToClone[key];
                }
            }
            newVNode = createComponentVNode(flags, vNodeToClone.type, props, vNodeToClone.key, vNodeToClone.ref);
        }
        else if (flags & 481 /* Element */) {
            var children = vNodeToClone.children;
            newVNode = createVNode(flags, vNodeToClone.type, vNodeToClone.className, children, 0 /* UnknownChildren */, vNodeToClone.props, vNodeToClone.key, vNodeToClone.ref);
        }
        else if (flags & 16 /* Text */) {
            newVNode = createTextVNode(vNodeToClone.children, vNodeToClone.key);
        }
        else if (flags & 1024 /* Portal */) {
            newVNode = vNodeToClone;
        }
        return newVNode;
    }
    function createVoidVNode() {
        return createTextVNode('', null);
    }
    function _normalizeVNodes(nodes, result, index, currentKey) {
        for (var len = nodes.length; index < len; index++) {
            var n = nodes[index];
            if (!isInvalid(n)) {
                var newKey = currentKey + keyPrefix + index;
                if (isArray(n)) {
                    _normalizeVNodes(n, result, 0, newKey);
                }
                else {
                    if (isStringOrNumber(n)) {
                        n = createTextVNode(n, newKey);
                    }
                    else {
                        var oldKey = n.key;
                        var isPrefixedKey = isString(oldKey) && oldKey[0] === keyPrefix;
                        if (!isNull(n.dom) || isPrefixedKey) {
                            n = directClone(n);
                        }
                        if (isNull(oldKey) || isPrefixedKey) {
                            n.key = newKey;
                        }
                        else {
                            n.key = currentKey + oldKey;
                        }
                    }
                    result.push(n);
                }
            }
        }
    }
    function getFlagsForElementVnode(type) {
        if (type === 'svg') {
            return 32 /* SvgElement */;
        }
        if (type === 'input') {
            return 64 /* InputElement */;
        }
        if (type === 'select') {
            return 256 /* SelectElement */;
        }
        if (type === 'textarea') {
            return 128 /* TextareaElement */;
        }
        return 1 /* HtmlElement */;
    }
    function normalizeChildren(vNode, children) {
        var newChildren;
        var newChildFlags;
        // Don't change children to match strict equal (===) true in patching
        if (isInvalid(children)) {
            newChildFlags = 1 /* HasInvalidChildren */;
            newChildren = children;
        }
        else if (isString(children)) {
            newChildFlags = 2 /* HasVNodeChildren */;
            newChildren = createTextVNode(children);
        }
        else if (isNumber(children)) {
            newChildFlags = 2 /* HasVNodeChildren */;
            newChildren = createTextVNode(children + '');
        }
        else if (isArray(children)) {
            var len = children.length;
            if (len === 0) {
                newChildren = null;
                newChildFlags = 1 /* HasInvalidChildren */;
            }
            else {
                // we assign $ which basically means we've flagged this array for future note
                // if it comes back again, we need to clone it, as people are using it
                // in an immutable way
                // tslint:disable-next-line
                if (Object.isFrozen(children) || children['$'] === true) {
                    children = children.slice();
                }
                newChildFlags = 8 /* HasKeyedChildren */;
                for (var i = 0; i < len; i++) {
                    var n = children[i];
                    if (isInvalid(n) || isArray(n)) {
                        newChildren = newChildren || children.slice(0, i);
                        _normalizeVNodes(children, newChildren, i, '');
                        break;
                    }
                    else if (isStringOrNumber(n)) {
                        newChildren = newChildren || children.slice(0, i);
                        newChildren.push(createTextVNode(n, keyPrefix + i));
                    }
                    else {
                        var key = n.key;
                        var isNullDom = isNull(n.dom);
                        var isNullKey = isNull(key);
                        var isPrefixed = !isNullKey && key[0] === keyPrefix;
                        if (!isNullDom || isNullKey || isPrefixed) {
                            newChildren = newChildren || children.slice(0, i);
                            if (!isNullDom || isPrefixed) {
                                n = directClone(n);
                            }
                            if (isNullKey || isPrefixed) {
                                n.key = keyPrefix + i;
                            }
                            newChildren.push(n);
                        }
                        else if (newChildren) {
                            newChildren.push(n);
                        }
                    }
                }
                newChildren = newChildren || children;
                newChildren.$ = true;
            }
        }
        else {
            newChildren = children;
            if (!isNull(children.dom)) {
                newChildren = directClone(children);
            }
            newChildFlags = 2 /* HasVNodeChildren */;
        }
        vNode.children = newChildren;
        vNode.childFlags = newChildFlags;
        {
            validateVNodeElementChildren(vNode);
        }
        return vNode;
    }
    var options = {
        afterMount: null,
        afterRender: null,
        afterUpdate: null,
        beforeRender: null,
        beforeUnmount: null,
        createVNode: null,
        roots: []
    };

    /**
     * Links given data to event as first parameter
     * @param {*} data data to be linked, it will be available in function as first parameter
     * @param {Function} event Function to be called when event occurs
     * @returns {{data: *, event: Function}}
     */
    function linkEvent(data, event) {
        if (isFunction(event)) {
            return { data: data, event: event };
        }
        return null; // Return null when event is invalid, to avoid creating unnecessary event handlers
    }

    var xlinkNS = 'http://www.w3.org/1999/xlink';
    var xmlNS = 'http://www.w3.org/XML/1998/namespace';
    var svgNS = 'http://www.w3.org/2000/svg';
    var namespaces = {
        'xlink:actuate': xlinkNS,
        'xlink:arcrole': xlinkNS,
        'xlink:href': xlinkNS,
        'xlink:role': xlinkNS,
        'xlink:show': xlinkNS,
        'xlink:title': xlinkNS,
        'xlink:type': xlinkNS,
        'xml:base': xmlNS,
        'xml:lang': xmlNS,
        'xml:space': xmlNS
    };

    // We need EMPTY_OBJ defined in one place.
    // Its used for comparison so we cant inline it into shared
    var EMPTY_OBJ = {};
    var LIFECYCLE = [];
    {
        Object.freeze(EMPTY_OBJ);
    }
    function appendChild(parentDom, dom) {
        parentDom.appendChild(dom);
    }
    function insertOrAppend(parentDom, newNode, nextNode) {
        if (isNullOrUndef(nextNode)) {
            appendChild(parentDom, newNode);
        }
        else {
            parentDom.insertBefore(newNode, nextNode);
        }
    }
    function documentCreateElement(tag, isSVG) {
        if (isSVG === true) {
            return document.createElementNS(svgNS, tag);
        }
        return document.createElement(tag);
    }
    function replaceChild(parentDom, newDom, lastDom) {
        parentDom.replaceChild(newDom, lastDom);
    }
    function removeChild(parentDom, dom) {
        parentDom.removeChild(dom);
    }
    function callAll(arrayFn) {
        var listener;
        while ((listener = arrayFn.shift()) !== undefined) {
            listener();
        }
    }

    var attachedEventCounts = {};
    var attachedEvents = {};
    function handleEvent(name, nextEvent, dom) {
        var eventsLeft = attachedEventCounts[name];
        var eventsObject = dom.$EV;
        if (nextEvent) {
            if (!eventsLeft) {
                attachedEvents[name] = attachEventToDocument(name);
                attachedEventCounts[name] = 0;
            }
            if (!eventsObject) {
                eventsObject = dom.$EV = {};
            }
            if (!eventsObject[name]) {
                attachedEventCounts[name]++;
            }
            eventsObject[name] = nextEvent;
        }
        else if (eventsObject && eventsObject[name]) {
            attachedEventCounts[name]--;
            if (eventsLeft === 1) {
                document.removeEventListener(normalizeEventName(name), attachedEvents[name]);
                attachedEvents[name] = null;
            }
            eventsObject[name] = nextEvent;
        }
    }
    function dispatchEvents(event, target, isClick, name, eventData) {
        var dom = target;
        while (!isNull(dom)) {
            // Html Nodes can be nested fe: span inside button in that scenario browser does not handle disabled attribute on parent,
            // because the event listener is on document.body
            // Don't process clicks on disabled elements
            if (isClick && dom.disabled) {
                return;
            }
            var eventsObject = dom.$EV;
            if (eventsObject) {
                var currentEvent = eventsObject[name];
                if (currentEvent) {
                    // linkEvent object
                    eventData.dom = dom;
                    if (currentEvent.event) {
                        currentEvent.event(currentEvent.data, event);
                    }
                    else {
                        currentEvent(event);
                    }
                    if (event.cancelBubble) {
                        return;
                    }
                }
            }
            dom = dom.parentNode;
        }
    }
    function normalizeEventName(name) {
        return name.substr(2).toLowerCase();
    }
    function stopPropagation() {
        this.cancelBubble = true;
        this.stopImmediatePropagation();
    }
    function attachEventToDocument(name) {
        var docEvent = function (event) {
            var type = event.type;
            var isClick = type === 'click' || type === 'dblclick';
            if (isClick && event.button !== 0) {
                // Firefox incorrectly triggers click event for mid/right mouse buttons.
                // This bug has been active for 12 years.
                // https://bugzilla.mozilla.org/show_bug.cgi?id=184051
                event.preventDefault();
                event.stopPropagation();
                return false;
            }
            event.stopPropagation = stopPropagation;
            // Event data needs to be object to save reference to currentTarget getter
            var eventData = {
                dom: document
            };
            try {
                Object.defineProperty(event, 'currentTarget', {
                    configurable: true,
                    get: function get() {
                        return eventData.dom;
                    }
                });
            }
            catch (e) {
                /* safari7 and phantomJS will crash */
            }
            dispatchEvents(event, event.target, isClick, name, eventData);
        };
        document.addEventListener(normalizeEventName(name), docEvent);
        return docEvent;
    }

    function isSameInnerHTML(dom, innerHTML) {
        var tempdom = document.createElement('i');
        tempdom.innerHTML = innerHTML;
        return tempdom.innerHTML === dom.innerHTML;
    }
    function isSamePropsInnerHTML(dom, props) {
        return Boolean(props && props.dangerouslySetInnerHTML && props.dangerouslySetInnerHTML.__html && isSameInnerHTML(dom, props.dangerouslySetInnerHTML.__html));
    }

    function triggerEventListener(props, methodName, e) {
        if (props[methodName]) {
            var listener = props[methodName];
            if (listener.event) {
                listener.event(listener.data, e);
            }
            else {
                listener(e);
            }
        }
        else {
            var nativeListenerName = methodName.toLowerCase();
            if (props[nativeListenerName]) {
                props[nativeListenerName](e);
            }
        }
    }
    function createWrappedFunction(methodName, applyValue) {
        var fnMethod = function (e) {
            e.stopPropagation();
            var vNode = this.$V;
            // If vNode is gone by the time event fires, no-op
            if (!vNode) {
                return;
            }
            var props = vNode.props || EMPTY_OBJ;
            var dom = vNode.dom;
            if (isString(methodName)) {
                triggerEventListener(props, methodName, e);
            }
            else {
                for (var i = 0; i < methodName.length; i++) {
                    triggerEventListener(props, methodName[i], e);
                }
            }
            if (isFunction(applyValue)) {
                var newVNode = this.$V;
                var newProps = newVNode.props || EMPTY_OBJ;
                applyValue(newProps, dom, false, newVNode);
            }
        };
        Object.defineProperty(fnMethod, 'wrapped', {
            configurable: false,
            enumerable: false,
            value: true,
            writable: false
        });
        return fnMethod;
    }

    function isCheckedType(type) {
        return type === 'checkbox' || type === 'radio';
    }
    var onTextInputChange = createWrappedFunction('onInput', applyValueInput);
    var wrappedOnChange = createWrappedFunction(['onClick', 'onChange'], applyValueInput);
    /* tslint:disable-next-line:no-empty */
    function emptywrapper(event) {
        event.stopPropagation();
    }
    emptywrapper.wrapped = true;
    function inputEvents(dom, nextPropsOrEmpty) {
        if (isCheckedType(nextPropsOrEmpty.type)) {
            dom.onchange = wrappedOnChange;
            dom.onclick = emptywrapper;
        }
        else {
            dom.oninput = onTextInputChange;
        }
    }
    function applyValueInput(nextPropsOrEmpty, dom) {
        var type = nextPropsOrEmpty.type;
        var value = nextPropsOrEmpty.value;
        var checked = nextPropsOrEmpty.checked;
        var multiple = nextPropsOrEmpty.multiple;
        var defaultValue = nextPropsOrEmpty.defaultValue;
        var hasValue = !isNullOrUndef(value);
        if (type && type !== dom.type) {
            dom.setAttribute('type', type);
        }
        if (!isNullOrUndef(multiple) && multiple !== dom.multiple) {
            dom.multiple = multiple;
        }
        if (!isNullOrUndef(defaultValue) && !hasValue) {
            dom.defaultValue = defaultValue + '';
        }
        if (isCheckedType(type)) {
            if (hasValue) {
                dom.value = value;
            }
            if (!isNullOrUndef(checked)) {
                dom.checked = checked;
            }
        }
        else {
            if (hasValue && dom.value !== value) {
                dom.defaultValue = value;
                dom.value = value;
            }
            else if (!isNullOrUndef(checked)) {
                dom.checked = checked;
            }
        }
    }

    function updateChildOptionGroup(vNode, value) {
        var type = vNode.type;
        if (type === 'optgroup') {
            var children = vNode.children;
            var childFlags = vNode.childFlags;
            if (childFlags & 12 /* MultipleChildren */) {
                for (var i = 0, len = children.length; i < len; i++) {
                    updateChildOption(children[i], value);
                }
            }
            else if (childFlags === 2 /* HasVNodeChildren */) {
                updateChildOption(children, value);
            }
        }
        else {
            updateChildOption(vNode, value);
        }
    }
    function updateChildOption(vNode, value) {
        var props = vNode.props || EMPTY_OBJ;
        var dom = vNode.dom;
        // we do this as multiple may have changed
        dom.value = props.value;
        if ((isArray(value) && value.indexOf(props.value) !== -1) || props.value === value) {
            dom.selected = true;
        }
        else if (!isNullOrUndef(value) || !isNullOrUndef(props.selected)) {
            dom.selected = props.selected || false;
        }
    }
    var onSelectChange = createWrappedFunction('onChange', applyValueSelect);
    function selectEvents(dom) {
        dom.onchange = onSelectChange;
    }
    function applyValueSelect(nextPropsOrEmpty, dom, mounting, vNode) {
        var multiplePropInBoolean = Boolean(nextPropsOrEmpty.multiple);
        if (!isNullOrUndef(nextPropsOrEmpty.multiple) && multiplePropInBoolean !== dom.multiple) {
            dom.multiple = multiplePropInBoolean;
        }
        var childFlags = vNode.childFlags;
        if ((childFlags & 1 /* HasInvalidChildren */) === 0) {
            var children = vNode.children;
            var value = nextPropsOrEmpty.value;
            if (mounting && isNullOrUndef(value)) {
                value = nextPropsOrEmpty.defaultValue;
            }
            if (childFlags & 12 /* MultipleChildren */) {
                for (var i = 0, len = children.length; i < len; i++) {
                    updateChildOptionGroup(children[i], value);
                }
            }
            else if (childFlags === 2 /* HasVNodeChildren */) {
                updateChildOptionGroup(children, value);
            }
        }
    }

    var onTextareaInputChange = createWrappedFunction('onInput', applyValueTextArea);
    var wrappedOnChange$1 = createWrappedFunction('onChange');
    function textAreaEvents(dom, nextPropsOrEmpty) {
        dom.oninput = onTextareaInputChange;
        if (nextPropsOrEmpty.onChange) {
            dom.onchange = wrappedOnChange$1;
        }
    }
    function applyValueTextArea(nextPropsOrEmpty, dom, mounting) {
        var value = nextPropsOrEmpty.value;
        var domValue = dom.value;
        if (isNullOrUndef(value)) {
            if (mounting) {
                var defaultValue = nextPropsOrEmpty.defaultValue;
                if (!isNullOrUndef(defaultValue) && defaultValue !== domValue) {
                    dom.defaultValue = defaultValue;
                    dom.value = defaultValue;
                }
            }
        }
        else if (domValue !== value) {
            /* There is value so keep it controlled */
            dom.defaultValue = value;
            dom.value = value;
        }
    }

    /**
     * There is currently no support for switching same input between controlled and nonControlled
     * If that ever becomes a real issue, then re design controlled elements
     * Currently user must choose either controlled or non-controlled and stick with that
     */
    function processElement(flags, vNode, dom, nextPropsOrEmpty, mounting, isControlled) {
        if (flags & 64 /* InputElement */) {
            applyValueInput(nextPropsOrEmpty, dom);
        }
        else if (flags & 256 /* SelectElement */) {
            applyValueSelect(nextPropsOrEmpty, dom, mounting, vNode);
        }
        else if (flags & 128 /* TextareaElement */) {
            applyValueTextArea(nextPropsOrEmpty, dom, mounting);
        }
        if (isControlled) {
            dom.$V = vNode;
        }
    }
    function addFormElementEventHandlers(flags, dom, nextPropsOrEmpty) {
        if (flags & 64 /* InputElement */) {
            inputEvents(dom, nextPropsOrEmpty);
        }
        else if (flags & 256 /* SelectElement */) {
            selectEvents(dom);
        }
        else if (flags & 128 /* TextareaElement */) {
            textAreaEvents(dom, nextPropsOrEmpty);
        }
    }
    function isControlledFormElement(nextPropsOrEmpty) {
        return nextPropsOrEmpty.type && isCheckedType(nextPropsOrEmpty.type) ? !isNullOrUndef(nextPropsOrEmpty.checked) : !isNullOrUndef(nextPropsOrEmpty.value);
    }

    function remove(vNode, parentDom) {
        unmount(vNode);
        if (!isNull(parentDom)) {
            removeChild(parentDom, vNode.dom);
            // Let carbage collector free memory
            vNode.dom = null;
        }
    }
    function unmount(vNode) {
        var flags = vNode.flags;
        if (flags & 481 /* Element */) {
            var ref = vNode.ref;
            var props = vNode.props;
            if (isFunction(ref)) {
                ref(null);
            }
            var children = vNode.children;
            var childFlags = vNode.childFlags;
            if (childFlags & 12 /* MultipleChildren */) {
                unmountAllChildren(children);
            }
            else if (childFlags === 2 /* HasVNodeChildren */) {
                unmount(children);
            }
            if (!isNull(props)) {
                for (var name in props) {
                    switch (name) {
                        case 'onClick':
                        case 'onDblClick':
                        case 'onFocusIn':
                        case 'onFocusOut':
                        case 'onKeyDown':
                        case 'onKeyPress':
                        case 'onKeyUp':
                        case 'onMouseDown':
                        case 'onMouseMove':
                        case 'onMouseUp':
                        case 'onSubmit':
                        case 'onTouchEnd':
                        case 'onTouchMove':
                        case 'onTouchStart':
                            handleEvent(name, null, vNode.dom);
                            break;
                        default:
                            break;
                    }
                }
            }
        }
        else if (flags & 14 /* Component */) {
            var instance = vNode.children;
            var ref$1 = vNode.ref;
            if (flags & 4 /* ComponentClass */) {
                if (isFunction(options.beforeUnmount)) {
                    options.beforeUnmount(vNode);
                }
                if (isFunction(instance.componentWillUnmount)) {
                    instance.componentWillUnmount();
                }
                if (isFunction(ref$1)) {
                    ref$1(null);
                }
                instance.$UN = true;
                unmount(instance.$LI);
            }
            else {
                if (!isNullOrUndef(ref$1) && isFunction(ref$1.onComponentWillUnmount)) {
                    ref$1.onComponentWillUnmount(vNode.dom, vNode.props || EMPTY_OBJ);
                }
                unmount(instance);
            }
        }
        else if (flags & 1024 /* Portal */) {
            var children$1 = vNode.children;
            if (!isNull(children$1) && isObject(children$1)) {
                remove(children$1, vNode.type);
            }
        }
    }
    function unmountAllChildren(children) {
        for (var i = 0, len = children.length; i < len; i++) {
            unmount(children[i]);
        }
    }
    function removeAllChildren(dom, children) {
        unmountAllChildren(children);
        dom.textContent = '';
    }

    function createLinkEvent(linkEvent, nextValue) {
        return function (e) {
            linkEvent(nextValue.data, e);
        };
    }
    function patchEvent(name, lastValue, nextValue, dom) {
        var nameLowerCase = name.toLowerCase();
        if (!isFunction(nextValue) && !isNullOrUndef(nextValue)) {
            var linkEvent = nextValue.event;
            if (linkEvent && isFunction(linkEvent)) {
                dom[nameLowerCase] = createLinkEvent(linkEvent, nextValue);
            }
            else {
                // Development warning
                {
                    throwError(("an event on a VNode \"" + name + "\". was not a function or a valid linkEvent."));
                }
            }
        }
        else {
            var domEvent = dom[nameLowerCase];
            // if the function is wrapped, that means it's been controlled by a wrapper
            if (!domEvent || !domEvent.wrapped) {
                dom[nameLowerCase] = nextValue;
            }
        }
    }
    function getNumberStyleValue(style, value) {
        switch (style) {
            case 'animationIterationCount':
            case 'borderImageOutset':
            case 'borderImageSlice':
            case 'borderImageWidth':
            case 'boxFlex':
            case 'boxFlexGroup':
            case 'boxOrdinalGroup':
            case 'columnCount':
            case 'fillOpacity':
            case 'flex':
            case 'flexGrow':
            case 'flexNegative':
            case 'flexOrder':
            case 'flexPositive':
            case 'flexShrink':
            case 'floodOpacity':
            case 'fontWeight':
            case 'gridColumn':
            case 'gridRow':
            case 'lineClamp':
            case 'lineHeight':
            case 'opacity':
            case 'order':
            case 'orphans':
            case 'stopOpacity':
            case 'strokeDasharray':
            case 'strokeDashoffset':
            case 'strokeMiterlimit':
            case 'strokeOpacity':
            case 'strokeWidth':
            case 'tabSize':
            case 'widows':
            case 'zIndex':
            case 'zoom':
                return value;
            default:
                return value + 'px';
        }
    }
    // We are assuming here that we come from patchProp routine
    // -nextAttrValue cannot be null or undefined
    function patchStyle(lastAttrValue, nextAttrValue, dom) {
        var domStyle = dom.style;
        var style;
        var value;
        if (isString(nextAttrValue)) {
            domStyle.cssText = nextAttrValue;
            return;
        }
        if (!isNullOrUndef(lastAttrValue) && !isString(lastAttrValue)) {
            for (style in nextAttrValue) {
                // do not add a hasOwnProperty check here, it affects performance
                value = nextAttrValue[style];
                if (value !== lastAttrValue[style]) {
                    domStyle[style] = isNumber(value) ? getNumberStyleValue(style, value) : value;
                }
            }
            for (style in lastAttrValue) {
                if (isNullOrUndef(nextAttrValue[style])) {
                    domStyle[style] = '';
                }
            }
        }
        else {
            for (style in nextAttrValue) {
                value = nextAttrValue[style];
                domStyle[style] = isNumber(value) ? getNumberStyleValue(style, value) : value;
            }
        }
    }
    function patchProp(prop, lastValue, nextValue, dom, isSVG, hasControlledValue, lastVNode) {
        switch (prop) {
            case 'onClick':
            case 'onDblClick':
            case 'onFocusIn':
            case 'onFocusOut':
            case 'onKeyDown':
            case 'onKeyPress':
            case 'onKeyUp':
            case 'onMouseDown':
            case 'onMouseMove':
            case 'onMouseUp':
            case 'onSubmit':
            case 'onTouchEnd':
            case 'onTouchMove':
            case 'onTouchStart':
                handleEvent(prop, nextValue, dom);
                break;
            case 'children':
            case 'childrenType':
            case 'className':
            case 'defaultValue':
            case 'key':
            case 'multiple':
            case 'ref':
                return;
            case 'allowfullscreen':
            case 'autoFocus':
            case 'autoplay':
            case 'capture':
            case 'checked':
            case 'controls':
            case 'default':
            case 'disabled':
            case 'hidden':
            case 'indeterminate':
            case 'loop':
            case 'muted':
            case 'novalidate':
            case 'open':
            case 'readOnly':
            case 'required':
            case 'reversed':
            case 'scoped':
            case 'seamless':
            case 'selected':
                prop = prop === 'autoFocus' ? prop.toLowerCase() : prop;
                dom[prop] = !!nextValue;
                break;
            case 'defaultChecked':
            case 'value':
            case 'volume':
                if (hasControlledValue && prop === 'value') {
                    return;
                }
                var value = isNullOrUndef(nextValue) ? '' : nextValue;
                if (dom[prop] !== value) {
                    dom[prop] = value;
                }
                break;
            case 'dangerouslySetInnerHTML':
                var lastHtml = (lastValue && lastValue.__html) || '';
                var nextHtml = (nextValue && nextValue.__html) || '';
                if (lastHtml !== nextHtml) {
                    if (!isNullOrUndef(nextHtml) && !isSameInnerHTML(dom, nextHtml)) {
                        if (!isNull(lastVNode)) {
                            if (lastVNode.childFlags & 12 /* MultipleChildren */) {
                                unmountAllChildren(lastVNode.children);
                            }
                            else if (lastVNode.childFlags === 2 /* HasVNodeChildren */) {
                                unmount(lastVNode.children);
                            }
                            lastVNode.children = null;
                            lastVNode.childFlags = 1 /* HasInvalidChildren */;
                        }
                        dom.innerHTML = nextHtml;
                    }
                }
                break;
            default:
                if (prop[0] === 'o' && prop[1] === 'n') {
                    patchEvent(prop, lastValue, nextValue, dom);
                }
                else if (isNullOrUndef(nextValue)) {
                    dom.removeAttribute(prop);
                }
                else if (prop === 'style') {
                    patchStyle(lastValue, nextValue, dom);
                }
                else if (isSVG && namespaces[prop]) {
                    // We optimize for NS being boolean. Its 99.9% time false
                    // If we end up in this path we can read property again
                    dom.setAttributeNS(namespaces[prop], prop, nextValue);
                }
                else {
                    dom.setAttribute(prop, nextValue);
                }
                break;
        }
    }
    function mountProps(vNode, flags, props, dom, isSVG) {
        var hasControlledValue = false;
        var isFormElement = (flags & 448 /* FormElement */) > 0;
        if (isFormElement) {
            hasControlledValue = isControlledFormElement(props);
            if (hasControlledValue) {
                addFormElementEventHandlers(flags, dom, props);
            }
        }
        for (var prop in props) {
            // do not add a hasOwnProperty check here, it affects performance
            patchProp(prop, null, props[prop], dom, isSVG, hasControlledValue, null);
        }
        if (isFormElement) {
            processElement(flags, vNode, dom, props, true, hasControlledValue);
        }
    }

    function createClassComponentInstance(vNode, Component, props, context) {
        var instance = new Component(props, context);
        vNode.children = instance;
        instance.$V = vNode;
        instance.$BS = false;
        instance.context = context;
        if (instance.props === EMPTY_OBJ) {
            instance.props = props;
        }
        instance.$UN = false;
        if (isFunction(instance.componentWillMount)) {
            instance.$BR = true;
            instance.componentWillMount();
            if (instance.$PSS) {
                var state = instance.state;
                var pending = instance.$PS;
                if (isNull(state)) {
                    instance.state = pending;
                }
                else {
                    for (var key in pending) {
                        state[key] = pending[key];
                    }
                }
                instance.$PSS = false;
                instance.$PS = null;
            }
            instance.$BR = false;
        }
        if (isFunction(options.beforeRender)) {
            options.beforeRender(instance);
        }
        var input = handleComponentInput(instance.render(props, instance.state, context), vNode);
        var childContext;
        if (isFunction(instance.getChildContext)) {
            childContext = instance.getChildContext();
        }
        if (isNullOrUndef(childContext)) {
            instance.$CX = context;
        }
        else {
            instance.$CX = combineFrom(context, childContext);
        }
        if (isFunction(options.afterRender)) {
            options.afterRender(instance);
        }
        instance.$LI = input;
        return instance;
    }
    function handleComponentInput(input, componentVNode) {
        // Development validation
        {
            if (isArray(input)) {
                throwError('a valid Inferno VNode (or null) must be returned from a component render. You may have returned an array or an invalid object.');
            }
        }
        if (isInvalid(input)) {
            input = createVoidVNode();
        }
        else if (isStringOrNumber(input)) {
            input = createTextVNode(input, null);
        }
        else {
            if (input.dom) {
                input = directClone(input);
            }
            if (input.flags & 14 /* Component */) {
                // if we have an input that is also a component, we run into a tricky situation
                // where the root vNode needs to always have the correct DOM entry
                // we can optimise this in the future, but this gets us out of a lot of issues
                input.parentVNode = componentVNode;
            }
        }
        return input;
    }

    function mount(vNode, parentDom, lifecycle, context, isSVG) {
        var flags = vNode.flags;
        if (flags & 481 /* Element */) {
            return mountElement(vNode, parentDom, lifecycle, context, isSVG);
        }
        if (flags & 14 /* Component */) {
            return mountComponent(vNode, parentDom, lifecycle, context, isSVG, (flags & 4 /* ComponentClass */) > 0);
        }
        if (flags & 512 /* Void */ || flags & 16 /* Text */) {
            return mountText(vNode, parentDom);
        }
        if (flags & 1024 /* Portal */) {
            mount(vNode.children, vNode.type, lifecycle, context, false);
            return (vNode.dom = mountText(createVoidVNode(), parentDom));
        }
        // Development validation, in production we don't need to throw because it crashes anyway
        {
            if (typeof vNode === 'object') {
                throwError(("mount() received an object that's not a valid VNode, you should stringify it first, fix createVNode flags or call normalizeChildren. Object: \"" + (JSON.stringify(vNode)) + "\"."));
            }
            else {
                throwError(("mount() expects a valid VNode, instead it received an object with the type \"" + (typeof vNode) + "\"."));
            }
        }
    }
    function mountText(vNode, parentDom) {
        var dom = (vNode.dom = document.createTextNode(vNode.children));
        if (!isNull(parentDom)) {
            appendChild(parentDom, dom);
        }
        return dom;
    }
    function mountElement(vNode, parentDom, lifecycle, context, isSVG) {
        var flags = vNode.flags;
        var children = vNode.children;
        var props = vNode.props;
        var className = vNode.className;
        var ref = vNode.ref;
        var childFlags = vNode.childFlags;
        isSVG = isSVG || (flags & 32 /* SvgElement */) > 0;
        var dom = documentCreateElement(vNode.type, isSVG);
        vNode.dom = dom;
        if (!isNullOrUndef(className) && className !== '') {
            if (isSVG) {
                dom.setAttribute('class', className);
            }
            else {
                dom.className = className;
            }
        }
        {
            validateKeys(vNode);
        }
        if (!isNull(parentDom)) {
            appendChild(parentDom, dom);
        }
        if ((childFlags & 1 /* HasInvalidChildren */) === 0) {
            var childrenIsSVG = isSVG === true && vNode.type !== 'foreignObject';
            if (childFlags === 2 /* HasVNodeChildren */) {
                mount(children, dom, lifecycle, context, childrenIsSVG);
            }
            else if (childFlags & 12 /* MultipleChildren */) {
                mountArrayChildren(children, dom, lifecycle, context, childrenIsSVG);
            }
        }
        if (!isNull(props)) {
            mountProps(vNode, flags, props, dom, isSVG);
        }
        {
            if (isString(ref)) {
                throwError('string "refs" are not supported in Inferno 1.0. Use callback "refs" instead.');
            }
        }
        if (isFunction(ref)) {
            mountRef(dom, ref, lifecycle);
        }
        return dom;
    }
    function mountArrayChildren(children, dom, lifecycle, context, isSVG) {
        for (var i = 0, len = children.length; i < len; i++) {
            var child = children[i];
            if (!isNull(child.dom)) {
                children[i] = child = directClone(child);
            }
            mount(child, dom, lifecycle, context, isSVG);
        }
    }
    function mountComponent(vNode, parentDom, lifecycle, context, isSVG, isClass) {
        var dom;
        var type = vNode.type;
        var props = vNode.props || EMPTY_OBJ;
        var ref = vNode.ref;
        if (isClass) {
            var instance = createClassComponentInstance(vNode, type, props, context);
            vNode.dom = dom = mount(instance.$LI, null, lifecycle, instance.$CX, isSVG);
            mountClassComponentCallbacks(vNode, ref, instance, lifecycle);
            instance.$UPD = false;
        }
        else {
            var input = handleComponentInput(type(props, context), vNode);
            vNode.children = input;
            vNode.dom = dom = mount(input, null, lifecycle, context, isSVG);
            mountFunctionalComponentCallbacks(props, ref, dom, lifecycle);
        }
        if (!isNull(parentDom)) {
            appendChild(parentDom, dom);
        }
        return dom;
    }
    function createClassMountCallback(instance, hasAfterMount, afterMount, vNode, hasDidMount) {
        return function () {
            instance.$UPD = true;
            if (hasAfterMount) {
                afterMount(vNode);
            }
            if (hasDidMount) {
                instance.componentDidMount();
            }
            instance.$UPD = false;
        };
    }
    function mountClassComponentCallbacks(vNode, ref, instance, lifecycle) {
        if (isFunction(ref)) {
            ref(instance);
        }
        else {
            {
                if (isStringOrNumber(ref)) {
                    throwError('string "refs" are not supported in Inferno 1.0. Use callback "refs" instead.');
                }
                else if (!isNullOrUndef(ref) && isObject(ref) && vNode.flags & 4 /* ComponentClass */) {
                    throwError('functional component lifecycle events are not supported on ES2015 class components.');
                }
            }
        }
        var hasDidMount = isFunction(instance.componentDidMount);
        var afterMount = options.afterMount;
        var hasAfterMount = isFunction(afterMount);
        if (hasDidMount || hasAfterMount) {
            lifecycle.push(createClassMountCallback(instance, hasAfterMount, afterMount, vNode, hasDidMount));
        }
    }
    // Create did mount callback lazily to avoid creating function context if not needed
    function createOnMountCallback(ref, dom, props) {
        return function () { return ref.onComponentDidMount(dom, props); };
    }
    function mountFunctionalComponentCallbacks(props, ref, dom, lifecycle) {
        if (!isNullOrUndef(ref)) {
            if (isFunction(ref.onComponentWillMount)) {
                ref.onComponentWillMount(props);
            }
            if (isFunction(ref.onComponentDidMount)) {
                lifecycle.push(createOnMountCallback(ref, dom, props));
            }
        }
    }
    function mountRef(dom, value, lifecycle) {
        lifecycle.push(function () { return value(dom); });
    }

    function hydrateComponent(vNode, dom, lifecycle, context, isSVG, isClass) {
        var type = vNode.type;
        var ref = vNode.ref;
        var props = vNode.props || EMPTY_OBJ;
        if (isClass) {
            var instance = createClassComponentInstance(vNode, type, props, context);
            var input = instance.$LI;
            hydrateVNode(input, dom, lifecycle, instance.$CX, isSVG);
            vNode.dom = input.dom;
            mountClassComponentCallbacks(vNode, ref, instance, lifecycle);
            instance.$UPD = false; // Mount finished allow going sync
        }
        else {
            var input$1 = handleComponentInput(type(props, context), vNode);
            hydrateVNode(input$1, dom, lifecycle, context, isSVG);
            vNode.children = input$1;
            vNode.dom = input$1.dom;
            mountFunctionalComponentCallbacks(props, ref, dom, lifecycle);
        }
    }
    function hydrateElement(vNode, dom, lifecycle, context, isSVG) {
        var children = vNode.children;
        var props = vNode.props;
        var className = vNode.className;
        var flags = vNode.flags;
        var ref = vNode.ref;
        isSVG = isSVG || (flags & 32 /* SvgElement */) > 0;
        if (dom.nodeType !== 1 || dom.tagName.toLowerCase() !== vNode.type) {
            {
                warning("Inferno hydration: Server-side markup doesn't match client-side markup or Initial render target is not empty");
            }
            var newDom = mountElement(vNode, null, lifecycle, context, isSVG);
            vNode.dom = newDom;
            replaceChild(dom.parentNode, newDom, dom);
        }
        else {
            vNode.dom = dom;
            var childNode = dom.firstChild;
            var childFlags = vNode.childFlags;
            if ((childFlags & 1 /* HasInvalidChildren */) === 0) {
                var nextSibling = null;
                while (childNode) {
                    nextSibling = childNode.nextSibling;
                    if (childNode.nodeType === 8) {
                        if (childNode.data === '!') {
                            dom.replaceChild(document.createTextNode(''), childNode);
                        }
                        else {
                            dom.removeChild(childNode);
                        }
                    }
                    childNode = nextSibling;
                }
                childNode = dom.firstChild;
                if (childFlags === 2 /* HasVNodeChildren */) {
                    if (isNull(childNode)) {
                        mount(children, dom, lifecycle, context, isSVG);
                    }
                    else {
                        nextSibling = childNode.nextSibling;
                        hydrateVNode(children, childNode, lifecycle, context, isSVG);
                        childNode = nextSibling;
                    }
                }
                else if (childFlags & 12 /* MultipleChildren */) {
                    for (var i = 0, len = children.length; i < len; i++) {
                        var child = children[i];
                        if (isNull(childNode)) {
                            mount(child, dom, lifecycle, context, isSVG);
                        }
                        else {
                            nextSibling = childNode.nextSibling;
                            hydrateVNode(child, childNode, lifecycle, context, isSVG);
                            childNode = nextSibling;
                        }
                    }
                }
                // clear any other DOM nodes, there should be only a single entry for the root
                while (childNode) {
                    nextSibling = childNode.nextSibling;
                    dom.removeChild(childNode);
                    childNode = nextSibling;
                }
            }
            else if (!isNull(dom.firstChild) && !isSamePropsInnerHTML(dom, props)) {
                dom.textContent = ''; // dom has content, but VNode has no children remove everything from DOM
                if (flags & 448 /* FormElement */) {
                    // If element is form element, we need to clear defaultValue also
                    dom.defaultValue = '';
                }
            }
            if (!isNull(props)) {
                mountProps(vNode, flags, props, dom, isSVG);
            }
            if (isNullOrUndef(className)) {
                if (dom.className !== '') {
                    dom.removeAttribute('class');
                }
            }
            else if (isSVG) {
                dom.setAttribute('class', className);
            }
            else {
                dom.className = className;
            }
            if (isFunction(ref)) {
                mountRef(dom, ref, lifecycle);
            }
            else {
                {
                    if (isString(ref)) {
                        throwError('string "refs" are not supported in Inferno 1.0. Use callback "refs" instead.');
                    }
                }
            }
        }
    }
    function hydrateText(vNode, dom) {
        if (dom.nodeType !== 3) {
            var newDom = mountText(vNode, null);
            vNode.dom = newDom;
            replaceChild(dom.parentNode, newDom, dom);
        }
        else {
            var text = vNode.children;
            if (dom.nodeValue !== text) {
                dom.nodeValue = text;
            }
            vNode.dom = dom;
        }
    }
    function hydrateVNode(vNode, dom, lifecycle, context, isSVG) {
        var flags = vNode.flags;
        if (flags & 14 /* Component */) {
            hydrateComponent(vNode, dom, lifecycle, context, isSVG, (flags & 4 /* ComponentClass */) > 0);
        }
        else if (flags & 481 /* Element */) {
            hydrateElement(vNode, dom, lifecycle, context, isSVG);
        }
        else if (flags & 16 /* Text */) {
            hydrateText(vNode, dom);
        }
        else if (flags & 512 /* Void */) {
            vNode.dom = dom;
        }
        else {
            {
                throwError(("hydrate() expects a valid VNode, instead it received an object with the type \"" + (typeof vNode) + "\"."));
            }
            throwError();
        }
    }
    function hydrate(input, parentDom, callback) {
        var dom = parentDom.firstChild;
        if (!isNull(dom)) {
            if (!isInvalid(input)) {
                hydrateVNode(input, dom, LIFECYCLE, EMPTY_OBJ, false);
            }
            dom = parentDom.firstChild;
            // clear any other DOM nodes, there should be only a single entry for the root
            while ((dom = dom.nextSibling)) {
                parentDom.removeChild(dom);
            }
        }
        if (LIFECYCLE.length > 0) {
            callAll(LIFECYCLE);
        }
        if (!parentDom.$V) {
            options.roots.push(parentDom);
        }
        parentDom.$V = input;
        if (isFunction(callback)) {
            callback();
        }
    }

    function replaceWithNewNode(lastNode, nextNode, parentDom, lifecycle, context, isSVG) {
        unmount(lastNode);
        replaceChild(parentDom, mount(nextNode, null, lifecycle, context, isSVG), lastNode.dom);
    }
    function patch(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG) {
        if (lastVNode !== nextVNode) {
            var nextFlags = nextVNode.flags;
            if (lastVNode.flags !== nextFlags || nextFlags & 2048 /* ReCreate */) {
                replaceWithNewNode(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG);
            }
            else if (nextFlags & 481 /* Element */) {
                patchElement(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG);
            }
            else if (nextFlags & 14 /* Component */) {
                patchComponent(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG, (nextFlags & 4 /* ComponentClass */) > 0);
            }
            else if (nextFlags & 16 /* Text */) {
                patchText(lastVNode, nextVNode, parentDom);
            }
            else if (nextFlags & 512 /* Void */) {
                nextVNode.dom = lastVNode.dom;
            }
            else {
                // Portal
                patchPortal(lastVNode, nextVNode, lifecycle, context);
            }
        }
    }
    function patchPortal(lastVNode, nextVNode, lifecycle, context) {
        var lastContainer = lastVNode.type;
        var nextContainer = nextVNode.type;
        var nextChildren = nextVNode.children;
        patchChildren(lastVNode.childFlags, nextVNode.childFlags, lastVNode.children, nextChildren, lastContainer, lifecycle, context, false);
        nextVNode.dom = lastVNode.dom;
        if (lastContainer !== nextContainer && !isInvalid(nextChildren)) {
            var node = nextChildren.dom;
            lastContainer.removeChild(node);
            nextContainer.appendChild(node);
        }
    }
    function patchElement(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG) {
        var nextTag = nextVNode.type;
        if (lastVNode.type !== nextTag) {
            replaceWithNewNode(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG);
        }
        else {
            var dom = lastVNode.dom;
            var nextFlags = nextVNode.flags;
            var lastProps = lastVNode.props;
            var nextProps = nextVNode.props;
            var isFormElement = false;
            var hasControlledValue = false;
            var nextPropsOrEmpty;
            nextVNode.dom = dom;
            isSVG = isSVG || (nextFlags & 32 /* SvgElement */) > 0;
            // inlined patchProps  -- starts --
            if (lastProps !== nextProps) {
                var lastPropsOrEmpty = lastProps || EMPTY_OBJ;
                nextPropsOrEmpty = nextProps || EMPTY_OBJ;
                if (nextPropsOrEmpty !== EMPTY_OBJ) {
                    isFormElement = (nextFlags & 448 /* FormElement */) > 0;
                    if (isFormElement) {
                        hasControlledValue = isControlledFormElement(nextPropsOrEmpty);
                    }
                    for (var prop in nextPropsOrEmpty) {
                        var lastValue = lastPropsOrEmpty[prop];
                        var nextValue = nextPropsOrEmpty[prop];
                        if (lastValue !== nextValue) {
                            patchProp(prop, lastValue, nextValue, dom, isSVG, hasControlledValue, lastVNode);
                        }
                    }
                }
                if (lastPropsOrEmpty !== EMPTY_OBJ) {
                    for (var prop$1 in lastPropsOrEmpty) {
                        // do not add a hasOwnProperty check here, it affects performance
                        if (!nextPropsOrEmpty.hasOwnProperty(prop$1) && !isNullOrUndef(lastPropsOrEmpty[prop$1])) {
                            patchProp(prop$1, lastPropsOrEmpty[prop$1], null, dom, isSVG, hasControlledValue, lastVNode);
                        }
                    }
                }
            }
            var lastChildren = lastVNode.children;
            var nextChildren = nextVNode.children;
            var nextRef = nextVNode.ref;
            var lastClassName = lastVNode.className;
            var nextClassName = nextVNode.className;
            if (lastChildren !== nextChildren) {
                {
                    validateKeys(nextVNode);
                }
                patchChildren(lastVNode.childFlags, nextVNode.childFlags, lastChildren, nextChildren, dom, lifecycle, context, isSVG && nextTag !== 'foreignObject');
            }
            if (isFormElement) {
                processElement(nextFlags, nextVNode, dom, nextPropsOrEmpty, false, hasControlledValue);
            }
            // inlined patchProps  -- ends --
            if (lastClassName !== nextClassName) {
                if (isNullOrUndef(nextClassName)) {
                    dom.removeAttribute('class');
                }
                else if (isSVG) {
                    dom.setAttribute('class', nextClassName);
                }
                else {
                    dom.className = nextClassName;
                }
            }
            if (isFunction(nextRef) && lastVNode.ref !== nextRef) {
                mountRef(dom, nextRef, lifecycle);
            }
            else {
                {
                    if (isString(nextRef)) {
                        throwError('string "refs" are not supported in Inferno 1.0. Use callback "refs" instead.');
                    }
                }
            }
        }
    }
    function patchChildren(lastChildFlags, nextChildFlags, lastChildren, nextChildren, parentDOM, lifecycle, context, isSVG) {
        switch (lastChildFlags) {
            case 2 /* HasVNodeChildren */:
                switch (nextChildFlags) {
                    case 2 /* HasVNodeChildren */:
                        patch(lastChildren, nextChildren, parentDOM, lifecycle, context, isSVG);
                        break;
                    case 1 /* HasInvalidChildren */:
                        remove(lastChildren, parentDOM);
                        break;
                    default:
                        remove(lastChildren, parentDOM);
                        mountArrayChildren(nextChildren, parentDOM, lifecycle, context, isSVG);
                        break;
                }
                break;
            case 1 /* HasInvalidChildren */:
                switch (nextChildFlags) {
                    case 2 /* HasVNodeChildren */:
                        mount(nextChildren, parentDOM, lifecycle, context, isSVG);
                        break;
                    case 1 /* HasInvalidChildren */:
                        break;
                    default:
                        mountArrayChildren(nextChildren, parentDOM, lifecycle, context, isSVG);
                        break;
                }
                break;
            default:
                if (nextChildFlags & 12 /* MultipleChildren */) {
                    var lastLength = lastChildren.length;
                    var nextLength = nextChildren.length;
                    // Fast path's for both algorithms
                    if (lastLength === 0) {
                        if (nextLength > 0) {
                            mountArrayChildren(nextChildren, parentDOM, lifecycle, context, isSVG);
                        }
                    }
                    else if (nextLength === 0) {
                        removeAllChildren(parentDOM, lastChildren);
                    }
                    else if (nextChildFlags === 8 /* HasKeyedChildren */ && lastChildFlags === 8 /* HasKeyedChildren */) {
                        patchKeyedChildren(lastChildren, nextChildren, parentDOM, lifecycle, context, isSVG, lastLength, nextLength);
                    }
                    else {
                        patchNonKeyedChildren(lastChildren, nextChildren, parentDOM, lifecycle, context, isSVG, lastLength, nextLength);
                    }
                }
                else if (nextChildFlags === 1 /* HasInvalidChildren */) {
                    removeAllChildren(parentDOM, lastChildren);
                }
                else {
                    removeAllChildren(parentDOM, lastChildren);
                    mount(nextChildren, parentDOM, lifecycle, context, isSVG);
                }
                break;
        }
    }
    function updateClassComponent(instance, nextState, nextVNode, nextProps, parentDom, lifecycle, context, isSVG, force, fromSetState) {
        var lastState = instance.state;
        var lastProps = instance.props;
        nextVNode.children = instance;
        var lastInput = instance.$LI;
        var renderOutput;
        if (instance.$UN) {
            {
                throwError('Inferno Error: Can only update a mounted or mounting component. This usually means you called setState() or forceUpdate() on an unmounted component. This is a no-op.');
            }
            return;
        }
        if (lastProps !== nextProps || nextProps === EMPTY_OBJ) {
            if (!fromSetState && isFunction(instance.componentWillReceiveProps)) {
                instance.$BR = true;
                instance.componentWillReceiveProps(nextProps, context);
                // If instance component was removed during its own update do nothing...
                if (instance.$UN) {
                    return;
                }
                instance.$BR = false;
            }
            if (instance.$PSS) {
                nextState = combineFrom(nextState, instance.$PS);
                instance.$PSS = false;
                instance.$PS = null;
            }
        }
        /* Update if scu is not defined, or it returns truthy value or force */
        var hasSCU = isFunction(instance.shouldComponentUpdate);
        if (force || !hasSCU || (hasSCU && instance.shouldComponentUpdate(nextProps, nextState, context))) {
            if (isFunction(instance.componentWillUpdate)) {
                instance.$BS = true;
                instance.componentWillUpdate(nextProps, nextState, context);
                instance.$BS = false;
            }
            instance.props = nextProps;
            instance.state = nextState;
            instance.context = context;
            if (isFunction(options.beforeRender)) {
                options.beforeRender(instance);
            }
            renderOutput = instance.render(nextProps, nextState, context);
            if (isFunction(options.afterRender)) {
                options.afterRender(instance);
            }
            var didUpdate = renderOutput !== NO_OP;
            var childContext;
            if (isFunction(instance.getChildContext)) {
                childContext = instance.getChildContext();
            }
            if (isNullOrUndef(childContext)) {
                childContext = context;
            }
            else {
                childContext = combineFrom(context, childContext);
            }
            instance.$CX = childContext;
            if (didUpdate) {
                var nextInput = (instance.$LI = handleComponentInput(renderOutput, nextVNode));
                patch(lastInput, nextInput, parentDom, lifecycle, childContext, isSVG);
                if (isFunction(instance.componentDidUpdate)) {
                    instance.componentDidUpdate(lastProps, lastState);
                }
                if (isFunction(options.afterUpdate)) {
                    options.afterUpdate(nextVNode);
                }
            }
        }
        else {
            instance.props = nextProps;
            instance.state = nextState;
            instance.context = context;
        }
        nextVNode.dom = instance.$LI.dom;
    }
    function patchComponent(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG, isClass) {
        var nextType = nextVNode.type;
        var lastKey = lastVNode.key;
        var nextKey = nextVNode.key;
        if (lastVNode.type !== nextType || lastKey !== nextKey) {
            replaceWithNewNode(lastVNode, nextVNode, parentDom, lifecycle, context, isSVG);
        }
        else {
            var nextProps = nextVNode.props || EMPTY_OBJ;
            if (isClass) {
                var instance = lastVNode.children;
                instance.$UPD = true;
                updateClassComponent(instance, instance.state, nextVNode, nextProps, parentDom, lifecycle, context, isSVG, false, false);
                instance.$V = nextVNode;
                instance.$UPD = false;
            }
            else {
                var shouldUpdate = true;
                var lastProps = lastVNode.props;
                var nextHooks = nextVNode.ref;
                var nextHooksDefined = !isNullOrUndef(nextHooks);
                var lastInput = lastVNode.children;
                nextVNode.dom = lastVNode.dom;
                nextVNode.children = lastInput;
                if (nextHooksDefined && isFunction(nextHooks.onComponentShouldUpdate)) {
                    shouldUpdate = nextHooks.onComponentShouldUpdate(lastProps, nextProps);
                }
                if (shouldUpdate !== false) {
                    if (nextHooksDefined && isFunction(nextHooks.onComponentWillUpdate)) {
                        nextHooks.onComponentWillUpdate(lastProps, nextProps);
                    }
                    var nextInput = nextType(nextProps, context);
                    if (nextInput !== NO_OP) {
                        nextInput = handleComponentInput(nextInput, nextVNode);
                        patch(lastInput, nextInput, parentDom, lifecycle, context, isSVG);
                        nextVNode.children = nextInput;
                        nextVNode.dom = nextInput.dom;
                        if (nextHooksDefined && isFunction(nextHooks.onComponentDidUpdate)) {
                            nextHooks.onComponentDidUpdate(lastProps, nextProps);
                        }
                    }
                }
                else if (lastInput.flags & 14 /* Component */) {
                    lastInput.parentVNode = nextVNode;
                }
            }
        }
    }
    function patchText(lastVNode, nextVNode, parentDom) {
        var nextText = nextVNode.children;
        var textNode = parentDom.firstChild;
        var dom;
        // Guard against external change on DOM node.
        if (isNull(textNode)) {
            parentDom.textContent = nextText;
            dom = parentDom.firstChild;
        }
        else {
            dom = lastVNode.dom;
            if (nextText !== lastVNode.children) {
                dom.nodeValue = nextText;
            }
        }
        nextVNode.dom = dom;
    }
    function patchNonKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, isSVG, lastChildrenLength, nextChildrenLength) {
        var commonLength = lastChildrenLength > nextChildrenLength ? nextChildrenLength : lastChildrenLength;
        var i = 0;
        for (; i < commonLength; i++) {
            var nextChild = nextChildren[i];
            if (nextChild.dom) {
                nextChild = nextChildren[i] = directClone(nextChild);
            }
            patch(lastChildren[i], nextChild, dom, lifecycle, context, isSVG);
        }
        if (lastChildrenLength < nextChildrenLength) {
            for (i = commonLength; i < nextChildrenLength; i++) {
                var nextChild$1 = nextChildren[i];
                if (nextChild$1.dom) {
                    nextChild$1 = nextChildren[i] = directClone(nextChild$1);
                }
                mount(nextChild$1, dom, lifecycle, context, isSVG);
            }
        }
        else if (lastChildrenLength > nextChildrenLength) {
            for (i = commonLength; i < lastChildrenLength; i++) {
                remove(lastChildren[i], dom);
            }
        }
    }
    function patchKeyedChildren(a, b, dom, lifecycle, context, isSVG, aLength, bLength) {
        var aEnd = aLength - 1;
        var bEnd = bLength - 1;
        var aStart = 0;
        var bStart = 0;
        var i;
        var j;
        var aNode;
        var bNode;
        var nextNode;
        var nextPos;
        var node;
        var aStartNode = a[aStart];
        var bStartNode = b[bStart];
        var aEndNode = a[aEnd];
        var bEndNode = b[bEnd];
        if (bStartNode.dom) {
            b[bStart] = bStartNode = directClone(bStartNode);
        }
        if (bEndNode.dom) {
            b[bEnd] = bEndNode = directClone(bEndNode);
        }
        // Step 1
        // tslint:disable-next-line
        outer: {
            // Sync nodes with the same key at the beginning.
            while (aStartNode.key === bStartNode.key) {
                patch(aStartNode, bStartNode, dom, lifecycle, context, isSVG);
                aStart++;
                bStart++;
                if (aStart > aEnd || bStart > bEnd) {
                    break outer;
                }
                aStartNode = a[aStart];
                bStartNode = b[bStart];
                if (bStartNode.dom) {
                    b[bStart] = bStartNode = directClone(bStartNode);
                }
            }
            // Sync nodes with the same key at the end.
            while (aEndNode.key === bEndNode.key) {
                patch(aEndNode, bEndNode, dom, lifecycle, context, isSVG);
                aEnd--;
                bEnd--;
                if (aStart > aEnd || bStart > bEnd) {
                    break outer;
                }
                aEndNode = a[aEnd];
                bEndNode = b[bEnd];
                if (bEndNode.dom) {
                    b[bEnd] = bEndNode = directClone(bEndNode);
                }
            }
        }
        if (aStart > aEnd) {
            if (bStart <= bEnd) {
                nextPos = bEnd + 1;
                nextNode = nextPos < bLength ? b[nextPos].dom : null;
                while (bStart <= bEnd) {
                    node = b[bStart];
                    if (node.dom) {
                        b[bStart] = node = directClone(node);
                    }
                    bStart++;
                    insertOrAppend(dom, mount(node, null, lifecycle, context, isSVG), nextNode);
                }
            }
        }
        else if (bStart > bEnd) {
            while (aStart <= aEnd) {
                remove(a[aStart++], dom);
            }
        }
        else {
            var aLeft = aEnd - aStart + 1;
            var bLeft = bEnd - bStart + 1;
            var sources = new Array(bLeft);
            for (i = 0; i < bLeft; i++) {
                sources[i] = -1;
            }
            var moved = false;
            var pos = 0;
            var patched = 0;
            // When sizes are small, just loop them through
            if (bLeft <= 4 || aLeft * bLeft <= 16) {
                for (i = aStart; i <= aEnd; i++) {
                    aNode = a[i];
                    if (patched < bLeft) {
                        for (j = bStart; j <= bEnd; j++) {
                            bNode = b[j];
                            if (aNode.key === bNode.key) {
                                sources[j - bStart] = i;
                                if (pos > j) {
                                    moved = true;
                                }
                                else {
                                    pos = j;
                                }
                                if (bNode.dom) {
                                    b[j] = bNode = directClone(bNode);
                                }
                                patch(aNode, bNode, dom, lifecycle, context, isSVG);
                                patched++;
                                a[i] = null;
                                break;
                            }
                        }
                    }
                }
            }
            else {
                var keyIndex = {};
                // Map keys by their index in array
                for (i = bStart; i <= bEnd; i++) {
                    keyIndex[b[i].key] = i;
                }
                // Try to patch same keys
                for (i = aStart; i <= aEnd; i++) {
                    aNode = a[i];
                    if (patched < bLeft) {
                        j = keyIndex[aNode.key];
                        if (isDefined(j)) {
                            bNode = b[j];
                            sources[j - bStart] = i;
                            if (pos > j) {
                                moved = true;
                            }
                            else {
                                pos = j;
                            }
                            if (bNode.dom) {
                                b[j] = bNode = directClone(bNode);
                            }
                            patch(aNode, bNode, dom, lifecycle, context, isSVG);
                            patched++;
                            a[i] = null;
                        }
                    }
                }
            }
            // fast-path: if nothing patched remove all old and add all new
            if (aLeft === aLength && patched === 0) {
                removeAllChildren(dom, a);
                mountArrayChildren(b, dom, lifecycle, context, isSVG);
            }
            else {
                i = aLeft - patched;
                while (i > 0) {
                    aNode = a[aStart++];
                    if (!isNull(aNode)) {
                        remove(aNode, dom);
                        i--;
                    }
                }
                if (moved) {
                    var seq = lis_algorithm(sources);
                    j = seq.length - 1;
                    for (i = bLeft - 1; i >= 0; i--) {
                        if (sources[i] === -1) {
                            pos = i + bStart;
                            node = b[pos];
                            if (node.dom) {
                                b[pos] = node = directClone(node);
                            }
                            nextPos = pos + 1;
                            insertOrAppend(dom, mount(node, null, lifecycle, context, isSVG), nextPos < bLength ? b[nextPos].dom : null);
                        }
                        else if (j < 0 || i !== seq[j]) {
                            pos = i + bStart;
                            node = b[pos];
                            nextPos = pos + 1;
                            insertOrAppend(dom, node.dom, nextPos < bLength ? b[nextPos].dom : null);
                        }
                        else {
                            j--;
                        }
                    }
                }
                else if (patched !== bLeft) {
                    // when patched count doesn't match b length we need to insert those new ones
                    // loop backwards so we can use insertBefore
                    for (i = bLeft - 1; i >= 0; i--) {
                        if (sources[i] === -1) {
                            pos = i + bStart;
                            node = b[pos];
                            if (node.dom) {
                                b[pos] = node = directClone(node);
                            }
                            nextPos = pos + 1;
                            insertOrAppend(dom, mount(node, null, lifecycle, context, isSVG), nextPos < bLength ? b[nextPos].dom : null);
                        }
                    }
                }
            }
        }
    }
    // // https://en.wikipedia.org/wiki/Longest_increasing_subsequence
    function lis_algorithm(arr) {
        var p = arr.slice();
        var result = [0];
        var i;
        var j;
        var u;
        var v;
        var c;
        var len = arr.length;
        for (i = 0; i < len; i++) {
            var arrI = arr[i];
            if (arrI !== -1) {
                j = result[result.length - 1];
                if (arr[j] < arrI) {
                    p[i] = j;
                    result.push(i);
                    continue;
                }
                u = 0;
                v = result.length - 1;
                while (u < v) {
                    c = ((u + v) / 2) | 0;
                    if (arr[result[c]] < arrI) {
                        u = c + 1;
                    }
                    else {
                        v = c;
                    }
                }
                if (arrI < arr[result[u]]) {
                    if (u > 0) {
                        p[i] = result[u - 1];
                    }
                    result[u] = i;
                }
            }
        }
        u = result.length;
        v = result[u - 1];
        while (u-- > 0) {
            result[u] = v;
            v = p[v];
        }
        return result;
    }

    var roots = options.roots;
    {
        if (isBrowser && document.body === null) {
            warning('Inferno warning: you cannot initialize inferno without "document.body". Wait on "DOMContentLoaded" event, add script to bottom of body, or use async/defer attributes on script tag.');
        }
    }
    var documentBody = isBrowser ? document.body : null;
    function render(input, parentDom, callback) {
        // Development warning
        {
            if (documentBody === parentDom) {
                throwError('you cannot render() to the "document.body". Use an empty element as a container instead.');
            }
        }
        if (input === NO_OP) {
            return;
        }
        var rootLen = roots.length;
        var rootInput;
        var index;
        for (index = 0; index < rootLen; index++) {
            if (roots[index] === parentDom) {
                rootInput = parentDom.$V;
                break;
            }
        }
        if (isUndefined(rootInput)) {
            if (!isInvalid(input)) {
                if (input.dom) {
                    input = directClone(input);
                }
                if (isNull(parentDom.firstChild)) {
                    mount(input, parentDom, LIFECYCLE, EMPTY_OBJ, false);
                    parentDom.$V = input;
                    roots.push(parentDom);
                }
                else {
                    hydrate(input, parentDom);
                }
                rootInput = input;
            }
        }
        else {
            if (isNullOrUndef(input)) {
                remove(rootInput, parentDom);
                roots.splice(index, 1);
            }
            else {
                if (input.dom) {
                    input = directClone(input);
                }
                patch(rootInput, input, parentDom, LIFECYCLE, EMPTY_OBJ, false);
                rootInput = parentDom.$V = input;
            }
        }
        if (LIFECYCLE.length > 0) {
            callAll(LIFECYCLE);
        }
        if (isFunction(callback)) {
            callback();
        }
        if (rootInput && rootInput.flags & 14 /* Component */) {
            return rootInput.children;
        }
    }
    function createRenderer(parentDom) {
        return function renderer(lastInput, nextInput) {
            if (!parentDom) {
                parentDom = lastInput;
            }
            render(nextInput, parentDom);
        };
    }
    function createPortal(children, container) {
        return createVNode(1024 /* Portal */, container, null, children, 0 /* UnknownChildren */, null, isInvalid(children) ? null : children.key, null);
    }

    var resolvedPromise = typeof Promise === 'undefined' ? null : Promise.resolve();
    var fallbackMethod = typeof requestAnimationFrame === 'undefined' ? setTimeout : requestAnimationFrame;
    function nextTick(fn) {
        if (resolvedPromise) {
            return resolvedPromise.then(fn);
        }
        return fallbackMethod(fn);
    }
    function queueStateChanges(component, newState, callback) {
        if (isFunction(newState)) {
            newState = newState(component.state, component.props, component.context);
        }
        var pending = component.$PS;
        if (isNullOrUndef(pending)) {
            component.$PS = newState;
        }
        else {
            for (var stateKey in newState) {
                pending[stateKey] = newState[stateKey];
            }
        }
        if (!component.$PSS && !component.$BR) {
            if (!component.$UPD) {
                component.$PSS = true;
                component.$UPD = true;
                applyState(component, false, callback);
                component.$UPD = false;
            }
            else {
                // Async
                var queue = component.$QU;
                if (isNull(queue)) {
                    queue = component.$QU = [];
                    nextTick(promiseCallback(component, queue));
                }
                if (isFunction(callback)) {
                    queue.push(callback);
                }
            }
        }
        else {
            component.$PSS = true;
            if (component.$BR && isFunction(callback)) {
                LIFECYCLE.push(callback.bind(component));
            }
        }
    }
    function promiseCallback(component, queue) {
        return function () {
            component.$QU = null;
            component.$UPD = true;
            applyState(component, false, function () {
                for (var i = 0, len = queue.length; i < len; i++) {
                    queue[i].call(component);
                }
            });
            component.$UPD = false;
        };
    }
    function applyState(component, force, callback) {
        if (component.$UN) {
            return;
        }
        if (force || !component.$BR) {
            component.$PSS = false;
            var pendingState = component.$PS;
            var prevState = component.state;
            var nextState = combineFrom(prevState, pendingState);
            var props = component.props;
            var context = component.context;
            component.$PS = null;
            var vNode = component.$V;
            var lastInput = component.$LI;
            var parentDom = lastInput.dom && lastInput.dom.parentNode;
            updateClassComponent(component, nextState, vNode, props, parentDom, LIFECYCLE, context, (vNode.flags & 32 /* SvgElement */) > 0, force, true);
            if (component.$UN) {
                return;
            }
            if ((component.$LI.flags & 1024 /* Portal */) === 0) {
                var dom = component.$LI.dom;
                while (!isNull((vNode = vNode.parentVNode))) {
                    if ((vNode.flags & 14 /* Component */) > 0) {
                        vNode.dom = dom;
                    }
                }
            }
            if (LIFECYCLE.length > 0) {
                callAll(LIFECYCLE);
            }
        }
        else {
            component.state = component.$PS;
            component.$PS = null;
        }
        if (isFunction(callback)) {
            callback.call(component);
        }
    }
    var Component = function Component(props, context) {
        this.state = null;
        // Internal properties
        this.$BR = false; // BLOCK RENDER
        this.$BS = true; // BLOCK STATE
        this.$PSS = false; // PENDING SET STATE
        this.$PS = null; // PENDING STATE (PARTIAL or FULL)
        this.$LI = null; // LAST INPUT
        this.$V = null; // VNODE
        this.$UN = false; // UNMOUNTED
        this.$CX = null; // CHILDCONTEXT
        this.$UPD = true; // UPDATING
        this.$QU = null; // QUEUE
        /** @type {object} */
        this.props = props || EMPTY_OBJ;
        /** @type {object} */
        this.context = context || EMPTY_OBJ; // context should not be mutable
    };
    Component.prototype.forceUpdate = function forceUpdate (callback) {
        if (this.$UN) {
            return;
        }
        applyState(this, true, callback);
    };
    Component.prototype.setState = function setState (newState, callback) {
        if (this.$UN) {
            return;
        }
        if (!this.$BS) {
            queueStateChanges(this, newState, callback);
        }
        else {
            // Development warning
            {
                throwError('cannot update state via setState() in componentWillUpdate() or constructor.');
            }
            return;
        }
    };
    // tslint:disable-next-line:no-empty
    Component.prototype.render = function render (nextProps, nextState, nextContext) { };
    // Public
    Component.defaultProps = null;

    {
        /* tslint:disable-next-line:no-empty */
        var testFunc = function testFn() { };
        if ((testFunc.name || testFunc.toString()).indexOf('testFn') === -1) {
            warning("It looks like you're using a minified copy of the development build " +
                'of Inferno. When deploying Inferno apps to production, make sure to use ' +
                'the production build which skips development warnings and is faster. ' +
                'See http://infernojs.org for more details.');
        }
    }
    var version = "4.0.4";

    exports.Component = Component;
    exports.EMPTY_OBJ = EMPTY_OBJ;
    exports.NO_OP = NO_OP;
    exports.createComponentVNode = createComponentVNode;
    exports.createPortal = createPortal;
    exports.createRenderer = createRenderer;
    exports.createTextVNode = createTextVNode;
    exports.createVNode = createVNode;
    exports.directClone = directClone;
    exports.getFlagsForElementVnode = getFlagsForElementVnode;
    exports.getNumberStyleValue = getNumberStyleValue;
    exports.hydrate = hydrate;
    exports.linkEvent = linkEvent;
    exports.normalizeProps = normalizeProps;
    exports.options = options;
    exports.render = render;
    exports.version = version;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
