/*!
 * (C) Ionic http://ionicframework.com - MIT License
 */
'use strict';

const ION_FOCUSED = 'ion-focused';
const ION_FOCUSABLE = 'ion-focusable';
const FOCUS_KEYS = [
    'Tab',
    'ArrowDown',
    'Space',
    'Escape',
    ' ',
    'Shift',
    'Enter',
    'ArrowLeft',
    'ArrowRight',
    'ArrowUp',
    'Home',
    'End',
];
let focusVisibleUtility = null;
const getOrInitFocusVisibleUtility = () => {
    if (!focusVisibleUtility) {
        focusVisibleUtility = startFocusVisible();
    }
    return focusVisibleUtility;
};
/**
 * Used to set focus on an element that uses `ion-focusable`.
 * Do not use this if focusing the element as a result of a keyboard
 * event as the focus utility should handle this for us. This method
 * should be used when we want to programmatically focus an element as
 * a result of another user action. (Ex: We focus the first element
 * inside of a popover when the user presents it, but the popover is not always
 * presented as a result of keyboard action.)
 *
 * @param elements - The elements to set focus on.
 */
const focusElements = (elements) => {
    const focusVisible = getOrInitFocusVisibleUtility();
    focusVisible.setFocus(elements);
};
const startFocusVisible = (rootEl) => {
    let currentFocus = [];
    let keyboardMode = true;
    const ref = rootEl ? rootEl.shadowRoot : document;
    const root = rootEl ? rootEl : document.body;
    const setFocus = (elements) => {
        currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
        elements.forEach((el) => el.classList.add(ION_FOCUSED));
        currentFocus = elements;
    };
    const pointerDown = () => {
        keyboardMode = false;
        setFocus([]);
    };
    const onKeydown = (ev) => {
        keyboardMode = FOCUS_KEYS.includes(ev.key);
        if (!keyboardMode) {
            setFocus([]);
        }
    };
    const onFocusin = (ev) => {
        if (keyboardMode && ev.composedPath !== undefined) {
            const toFocus = ev.composedPath().filter((el) => {
                // TODO(FW-2832): type
                if (el.classList) {
                    return el.classList.contains(ION_FOCUSABLE);
                }
                return false;
            });
            setFocus(toFocus);
        }
    };
    const onFocusout = () => {
        if (ref.activeElement === root) {
            setFocus([]);
        }
    };
    ref.addEventListener('keydown', onKeydown);
    ref.addEventListener('focusin', onFocusin);
    ref.addEventListener('focusout', onFocusout);
    ref.addEventListener('touchstart', pointerDown, { passive: true });
    ref.addEventListener('mousedown', pointerDown);
    const destroy = () => {
        ref.removeEventListener('keydown', onKeydown);
        ref.removeEventListener('focusin', onFocusin);
        ref.removeEventListener('focusout', onFocusout);
        ref.removeEventListener('touchstart', pointerDown);
        ref.removeEventListener('mousedown', pointerDown);
    };
    return {
        destroy,
        setFocus,
    };
};

exports.focusElements = focusElements;
exports.getOrInitFocusVisibleUtility = getOrInitFocusVisibleUtility;
exports.startFocusVisible = startFocusVisible;
