define(["require", "exports"], function (require, exports) {
    "use strict";
    /**
     * Sets the virtual parent of an element.
     * Pass `undefined` as the `parent` to clear the virtual parent.
     *
     * @export
     * @param {HTMLElement} child
     * @param {HTMLElement} parent
     */
    function setVirtualParent(child, parent) {
        var virtualChild = child;
        var virtualParent = parent;
        if (!virtualChild._virtual) {
            virtualChild._virtual = {
                children: []
            };
        }
        var oldParent = virtualChild._virtual.parent;
        if (oldParent && oldParent !== parent) {
            // Remove the child from its old parent.
            var index = oldParent._virtual.children.indexOf(virtualChild);
            if (index > -1) {
                oldParent._virtual.children.splice(index, 1);
            }
        }
        virtualChild._virtual.parent = virtualParent || undefined;
        if (virtualParent) {
            if (!virtualParent._virtual) {
                virtualParent._virtual = {
                    children: []
                };
            }
            virtualParent._virtual.children.push(virtualChild);
        }
    }
    exports.setVirtualParent = setVirtualParent;
    function getVirtualParent(child) {
        var parent;
        if (child && isVirtualElement(child)) {
            parent = child._virtual.parent;
        }
        return parent;
    }
    exports.getVirtualParent = getVirtualParent;
    /**
     * Gets the element which is the parent of a given element.
     * If `allowVirtuaParents` is `true`, this method prefers the virtual parent over
     * real DOM parent when present.
     *
     * @export
     * @param {HTMLElement} child
     * @param {boolean} [allowVirtualParents=true]
     * @returns {HTMLElement}
     */
    function getParent(child, allowVirtualParents) {
        if (allowVirtualParents === void 0) { allowVirtualParents = true; }
        return child && (allowVirtualParents && getVirtualParent(child) ||
            child.parentNode && child.parentNode);
    }
    exports.getParent = getParent;
    /**
     * Determines whether or not a parent element contains a given child element.
     * If `allowVirtualParents` is true, this method may return `true` if the child
     * has the parent in its virtual element hierarchy.
     *
     * @export
     * @param {HTMLElement} parent
     * @param {HTMLElement} child
     * @param {boolean} [allowVirtualParents=true]
     * @returns {boolean}
     */
    function elementContains(parent, child, allowVirtualParents) {
        if (allowVirtualParents === void 0) { allowVirtualParents = true; }
        var isContained = false;
        if (parent && child) {
            if (allowVirtualParents) {
                isContained = false;
                while (child) {
                    var nextParent = getParent(child);
                    if (nextParent === parent) {
                        isContained = true;
                        break;
                    }
                    child = nextParent;
                }
            }
            else if (parent.contains) {
                isContained = parent.contains(child);
            }
        }
        return isContained;
    }
    exports.elementContains = elementContains;
    var _isSSR = false;
    /** Helper to set ssr mode to simulate no window object returned from getWindow helper. */
    function setSSR(isEnabled) {
        _isSSR = isEnabled;
    }
    exports.setSSR = setSSR;
    /** Helper to get the window object. */
    function getWindow() {
        return _isSSR ? undefined : window;
    }
    exports.getWindow = getWindow;
    /** Helper to get the document object. */
    function getDocument() {
        return _isSSR ? undefined : document;
    }
    exports.getDocument = getDocument;
    /** Helper to get bounding client rect, works with window. */
    function getRect(element) {
        var rect;
        if (element) {
            if (element === window) {
                rect = {
                    left: 0,
                    top: 0,
                    width: window.innerWidth,
                    height: window.innerHeight,
                    right: window.innerWidth,
                    bottom: window.innerHeight
                };
            }
            else if (element.getBoundingClientRect) {
                rect = element.getBoundingClientRect();
            }
        }
        return rect;
    }
    exports.getRect = getRect;
    /**
     * Determines whether or not an element has the virtual hierarchy extension.
     *
     * @param {(HTMLElement | IVirtualElement)} element
     * @returns {element is IVirtualElement}
     */
    function isVirtualElement(element) {
        return element && !!element._virtual;
    }
});
