"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hideNonTargetElements = hideNonTargetElements;
/**
 * Modified version of `aria-hidden`-package.
 * - Removed "inert"-functionality.
 * - Removed flexibility for different data-attributes.
 * https://github.com/theKashey/aria-hidden/blob/720e8a8e1cfa047bd299a929d95d47ac860a5c1a/src/index.ts
 */
const owner_1 = require("./owner");
let ariaHiddenCounter = new WeakMap();
let markerCounter = new WeakMap();
let uncontrolledElementsSet = new WeakSet();
let lockCount = 0;
const controlAttribute = "aria-hidden";
const markerName = "data-aksel-hidden";
/**
 * Unwraps a Shadow DOM host to find the actual Element in the light DOM.
 */
function unwrapHost(node) {
    return (node &&
        (node.host || unwrapHost(node.parentNode)));
}
/**
 * Corrects the target elements by unwrapping Shadow DOM hosts if necessary.
 *
 * @param parent - The parent HTMLElement to check containment against.
 * @param targets - An array of target Elements to correct.
 * @returns An array of corrected Elements that are contained within the parent.
 */
function correctElements(parent, targets) {
    return targets
        .map((target) => {
        if (parent.contains(target)) {
            return target;
        }
        const correctedTarget = unwrapHost(target);
        if (parent.contains(correctedTarget)) {
            return correctedTarget;
        }
        return null;
    })
        .filter((x) => x !== null);
}
/**
 * Applies the aria-hidden attribute to all elements in the body except the specified avoid elements.
 */
function applyAttributeToOthers(uncorrectedAvoidElements, body) {
    const avoidElements = correctElements(body, uncorrectedAvoidElements);
    const elementsToAvoidWithParents = new Set();
    const elementsToAvoidUpdating = new Set(avoidElements);
    const hiddenElements = [];
    avoidElements.forEach(addToAvoidList);
    applyAttributes(body);
    elementsToAvoidWithParents.clear();
    function addToAvoidList(el) {
        if (!el || elementsToAvoidWithParents.has(el)) {
            return;
        }
        elementsToAvoidWithParents.add(el);
        if (el.parentNode) {
            addToAvoidList(el.parentNode);
        }
    }
    function applyAttributes(parent) {
        if (!parent || elementsToAvoidUpdating.has(parent)) {
            return;
        }
        const parentChildren = parent.children;
        for (let index = 0; index < parentChildren.length; index += 1) {
            const node = parentChildren[index];
            if (elementsToAvoidWithParents.has(node)) {
                applyAttributes(node);
            }
            else {
                const attr = node.getAttribute(controlAttribute);
                /*
                 * We only check for falsy values here since since arbitrary values
                 * (e.g. "true", "foo", "") are all valid for indicating that the element is already hidden.
                 */
                const alreadyHidden = attr !== null && attr !== "false";
                const counterValue = (ariaHiddenCounter.get(node) || 0) + 1;
                const markerValue = (markerCounter.get(node) || 0) + 1;
                ariaHiddenCounter.set(node, counterValue);
                markerCounter.set(node, markerValue);
                hiddenElements.push(node);
                if (counterValue === 1 && alreadyHidden) {
                    uncontrolledElementsSet.add(node);
                }
                if (markerValue === 1) {
                    node.setAttribute(markerName, "");
                }
                if (!alreadyHidden) {
                    node.setAttribute(controlAttribute, "true");
                }
            }
        }
    }
    lockCount += 1;
    /* Cleanup */
    return () => {
        for (const element of hiddenElements) {
            const currentCounterValue = ariaHiddenCounter.get(element) || 0;
            const counterValue = currentCounterValue - 1;
            const markerValue = (markerCounter.get(element) || 0) - 1;
            ariaHiddenCounter.set(element, counterValue);
            markerCounter.set(element, markerValue);
            if (!counterValue) {
                if (!uncontrolledElementsSet.has(element)) {
                    element.removeAttribute(controlAttribute);
                }
                uncontrolledElementsSet.delete(element);
            }
            if (!markerValue) {
                element.removeAttribute(markerName);
            }
        }
        lockCount -= 1;
        /* Reset */
        if (!lockCount) {
            ariaHiddenCounter = new WeakMap();
            uncontrolledElementsSet = new WeakSet();
            markerCounter = new WeakMap();
        }
    };
}
/**
 * Hides all elements in the document body for assertive technologies except the specified elements with `aria-hidden`.
 * @param avoidElements - An array of elements to avoid hiding.
 * @returns A function that, when called, will undo the hiding of elements.
 */
function hideNonTargetElements(avoidElements) {
    const body = (0, owner_1.ownerDocument)(avoidElements[0]).body;
    /**
     * Assume that elements with `aria-live` or `script` tags should not be hidden.
     * This ensures that live regions and scripts continue to function properly.
     */
    const ignoredElements = Array.from(body.querySelectorAll("[aria-live], script"));
    return applyAttributeToOthers(avoidElements.concat(ignoredElements), body);
}
//# sourceMappingURL=hideNonTargetElements.js.map