export const transitionEndAsync = (el, expectedDuration = 0) => {
    return new Promise((resolve) => {
        transitionEnd(el, expectedDuration, resolve);
    });
};
const transitionEnd = (el, expectedDuration = 0, callback) => {
    let unRegTrans;
    let animationTimeout;
    const opts = { passive: true };
    const ANIMATION_FALLBACK_TIMEOUT = 500;
    const unregister = () => {
        if (unRegTrans) {
            unRegTrans();
        }
    };
    const onTransitionEnd = (ev) => {
        if (ev === undefined || el === ev.target) {
            unregister();
            callback(ev);
        }
    };
    if (el) {
        el.addEventListener('webkitTransitionEnd', onTransitionEnd, opts);
        el.addEventListener('transitionend', onTransitionEnd, opts);
        animationTimeout = setTimeout(onTransitionEnd, expectedDuration + ANIMATION_FALLBACK_TIMEOUT);
        unRegTrans = () => {
            if (animationTimeout !== undefined) {
                clearTimeout(animationTimeout);
                animationTimeout = undefined;
            }
            el.removeEventListener('webkitTransitionEnd', onTransitionEnd, opts);
            el.removeEventListener('transitionend', onTransitionEnd, opts);
        };
    }
    return unregister;
};
export const componentOnReady = (el, callback) => {
    if (el.componentOnReady) {
        el.componentOnReady().then((resolvedEl) => callback(resolvedEl));
    }
    else {
        raf(() => callback(el));
    }
};
export const hasLazyBuild = (stencilEl) => {
    return stencilEl.componentOnReady !== undefined;
};
export const inheritAttributes = (el, attributes = []) => {
    const attributeObject = {};
    attributes.forEach((attr) => {
        if (el.hasAttribute(attr)) {
            const value = el.getAttribute(attr);
            if (value !== null) {
                attributeObject[attr] = el.getAttribute(attr);
            }
            el.removeAttribute(attr);
        }
    });
    return attributeObject;
};
const ariaAttributes = [
    'role',
    'aria-activedescendant',
    'aria-atomic',
    'aria-autocomplete',
    'aria-braillelabel',
    'aria-brailleroledescription',
    'aria-busy',
    'aria-checked',
    'aria-colcount',
    'aria-colindex',
    'aria-colindextext',
    'aria-colspan',
    'aria-controls',
    'aria-current',
    'aria-describedby',
    'aria-description',
    'aria-details',
    'aria-disabled',
    'aria-errormessage',
    'aria-expanded',
    'aria-flowto',
    'aria-haspopup',
    'aria-hidden',
    'aria-invalid',
    'aria-keyshortcuts',
    'aria-label',
    'aria-labelledby',
    'aria-level',
    'aria-live',
    'aria-multiline',
    'aria-multiselectable',
    'aria-orientation',
    'aria-owns',
    'aria-placeholder',
    'aria-posinset',
    'aria-pressed',
    'aria-readonly',
    'aria-relevant',
    'aria-required',
    'aria-roledescription',
    'aria-rowcount',
    'aria-rowindex',
    'aria-rowindextext',
    'aria-rowspan',
    'aria-selected',
    'aria-setsize',
    'aria-sort',
    'aria-valuemax',
    'aria-valuemin',
    'aria-valuenow',
    'aria-valuetext',
];
export const inheritAriaAttributes = (el, ignoreList) => {
    let attributesToInherit = ariaAttributes;
    if (ignoreList && ignoreList.length > 0) {
        attributesToInherit = attributesToInherit.filter((attr) => !ignoreList.includes(attr));
    }
    return inheritAttributes(el, attributesToInherit);
};
export const addEventListener = (el, eventName, callback, opts) => {
    return el.addEventListener(eventName, callback, opts);
};
export const removeEventListener = (el, eventName, callback, opts) => {
    return el.removeEventListener(eventName, callback, opts);
};
export const getElementRoot = (el, fallback = el) => {
    return el.shadowRoot || fallback;
};
export const raf = (h) => {
    if (typeof __zone_symbol__requestAnimationFrame === 'function') {
        return __zone_symbol__requestAnimationFrame(h);
    }
    if (typeof requestAnimationFrame === 'function') {
        return requestAnimationFrame(h);
    }
    return setTimeout(h);
};
export const hasShadowDom = (el) => {
    return !!el.shadowRoot && !!el.attachShadow;
};
export const focusVisibleElement = (el) => {
    el.focus();
};
export const renderHiddenInput = (always, container, name, value, disabled) => {
    if (always || hasShadowDom(container)) {
        let input = container.querySelector('input.aux-input');
        if (!input) {
            input = container.ownerDocument.createElement('input');
            input.type = 'hidden';
            input.classList.add('aux-input');
            container.appendChild(input);
        }
        input.disabled = disabled;
        input.name = name;
        input.value = value || '';
    }
};
export const clamp = (min, n, max) => {
    return Math.max(min, Math.min(n, max));
};
export const assert = (actual, reason) => {
    if (!actual) {
        const message = 'ASSERT: ' + reason;
        console.error(message);
        debugger;
        throw new Error(message);
    }
};
export const now = (ev) => {
    return ev.timeStamp || Date.now();
};
export const pointerCoord = (ev) => {
    if (ev) {
        const changedTouches = ev.changedTouches;
        if (changedTouches && changedTouches.length > 0) {
            const touch = changedTouches[0];
            return { x: touch.clientX, y: touch.clientY };
        }
        if (ev.pageX !== undefined) {
            return { x: ev.pageX, y: ev.pageY };
        }
    }
    return { x: 0, y: 0 };
};
export const deferEvent = (event) => {
    return debounceEvent(event, 0);
};
export const debounceEvent = (event, wait) => {
    const original = event._original || event;
    return {
        _original: event,
        emit: debounce(original.emit.bind(original), wait),
    };
};
export const debounce = (func, wait = 0) => {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(func, wait, ...args);
    };
};
export const shallowEqualStringMap = (map1, map2) => {
    map1 ??= {};
    map2 ??= {};
    if (map1 === map2) {
        return true;
    }
    const keys1 = Object.keys(map1);
    if (keys1.length !== Object.keys(map2).length) {
        return false;
    }
    for (const k1 of keys1) {
        if (!(k1 in map2)) {
            return false;
        }
        if (map1[k1] !== map2[k1]) {
            return false;
        }
    }
    return true;
};
export const isSafeNumber = (input) => {
    return typeof input === 'number' && !isNaN(input) && isFinite(input);
};
export function getDeepActiveElement(root = document) {
    let activeElement = root.activeElement;
    if (!activeElement) {
        return null;
    }
    while (activeElement?.shadowRoot) {
        const next = activeElement.shadowRoot.activeElement;
        if (!next) {
            break;
        }
        activeElement = next;
    }
    return activeElement;
}
export function canReceiveFocus(el) {
    if (!el)
        return false;
    return (el instanceof HTMLElement && el.tabIndex >= 0 && !el.hasAttribute('disabled') && el.getAttribute('aria-hidden') !== 'true' && el.checkVisibility() && el.offsetParent !== null);
}
export function findFormByRef(form, el) {
    if (form instanceof HTMLFormElement) {
        return form;
    }
    if (typeof form === 'string') {
        const el = document.getElementById(form);
        if (el) {
            if (el instanceof HTMLFormElement) {
                return el;
            }
            else {
                console.warn(`No Form found with selector: "#${form}". Verify that the element with id exists and is actually a <form> element.`, el);
                return null;
            }
        }
        else {
            console.warn(`No Form found with selector: "#${form}". Verify that the id is correct and the form is rendered in the DOM.`, el);
            return null;
        }
    }
    if (form !== undefined) {
        console.warn(`The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, el);
        return null;
    }
    return el.closest('form');
}
