weekly on-sync release 5.0dev
[moodle.git] / theme / boost / amd / build / aria.min.js.map
blob4f4e21fdd9d87a44c9f3dfba4fd238705343cfbd
1 {"version":3,"file":"aria.min.js","sources":["../src/aria.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Enhancements to Bootstrap components for accessibility.\n *\n * @module     theme_boost/aria\n * @copyright  2018 Damyon Wiese <damyon@moodle.com>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Pending from 'core/pending';\nimport * as FocusLockManager from 'core/local/aria/focuslock';\n\n/**\n * Drop downs from bootstrap don't support keyboard accessibility by default.\n */\nconst dropdownFix = () => {\n    let focusEnd = false;\n    const setFocusEnd = (end = true) => {\n        focusEnd = end;\n    };\n    const getFocusEnd = () => {\n        const result = focusEnd;\n        focusEnd = false;\n        return result;\n    };\n\n    // Special handling for navigation keys when menu is open.\n    const shiftFocus = (element, focusCheck = null) => {\n        const pendingPromise = new Pending('core/aria:delayed-focus');\n        setTimeout(() => {\n            if (!focusCheck || focusCheck()) {\n                element.focus();\n            }\n\n            pendingPromise.resolve();\n        }, 50);\n    };\n\n    // Event handling for the dropdown menu button.\n    const handleMenuButton = e => {\n        const trigger = e.key;\n        let fixFocus = false;\n\n        // Space key or Enter key opens the menu.\n        if (trigger === ' ' || trigger === 'Enter') {\n            fixFocus = true;\n            // Cancel random scroll.\n            e.preventDefault();\n            // Open the menu instead.\n            e.target.click();\n        }\n\n        // Up and Down keys also open the menu.\n        if (trigger === 'ArrowUp' || trigger === 'ArrowDown') {\n            fixFocus = true;\n        }\n\n        if (!fixFocus) {\n            // No need to fix the focus. Return early.\n            return;\n        }\n\n        // Fix the focus on the menu items when the menu is opened.\n        const menu = e.target.parentElement.querySelector('[role=\"menu\"]');\n        let menuItems = false;\n        let foundMenuItem = false;\n\n        if (menu) {\n            menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n        }\n        if (menuItems && menuItems.length > 0) {\n            // Up key opens the menu at the end.\n            if (trigger === 'ArrowUp') {\n                setFocusEnd();\n            } else {\n                setFocusEnd(false);\n            }\n\n            if (getFocusEnd()) {\n                foundMenuItem = menuItems[menuItems.length - 1];\n            } else {\n                // The first menu entry, pretty reasonable.\n                foundMenuItem = menuItems[0];\n            }\n        }\n\n        if (foundMenuItem) {\n            shiftFocus(foundMenuItem);\n        }\n    };\n\n    // Search for menu items by finding the first item that has\n    // text starting with the typed character (case insensitive).\n    document.addEventListener('keypress', e => {\n        if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n            const menu = e.target.closest('[role=\"menu\"]');\n            if (!menu) {\n                return;\n            }\n            const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n            if (!menuItems) {\n                return;\n            }\n\n            const trigger = e.key.toLowerCase();\n\n            for (let i = 0; i < menuItems.length; i++) {\n                const item = menuItems[i];\n                const itemText = item.text.trim().toLowerCase();\n                if (itemText.indexOf(trigger) == 0) {\n                    shiftFocus(item);\n                    break;\n                }\n            }\n        }\n    });\n\n    // Keyboard navigation for arrow keys, home and end keys.\n    document.addEventListener('keydown', e => {\n\n        // We only want to set focus when users access the dropdown via keyboard as per\n        // guidelines defined in w3 aria practices 1.1 menu-button.\n        if (e.target.matches('[data-toggle=\"dropdown\"]')) {\n            handleMenuButton(e);\n        }\n\n        if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n            const trigger = e.key;\n            let next = false;\n            const menu = e.target.closest('[role=\"menu\"]');\n\n            if (!menu) {\n                return;\n            }\n            const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n            if (!menuItems) {\n                return;\n            }\n            // Down key.\n            if (trigger == 'ArrowDown') {\n                for (let i = 0; i < menuItems.length - 1; i++) {\n                    if (menuItems[i] == e.target) {\n                        next = menuItems[i + 1];\n                        break;\n                    }\n                }\n                if (!next) {\n                    // Wrap to first item.\n                    next = menuItems[0];\n                }\n            } else if (trigger == 'ArrowUp') {\n                // Up key.\n                for (let i = 1; i < menuItems.length; i++) {\n                    if (menuItems[i] == e.target) {\n                        next = menuItems[i - 1];\n                        break;\n                    }\n                }\n                if (!next) {\n                    // Wrap to last item.\n                    next = menuItems[menuItems.length - 1];\n                }\n            } else if (trigger == 'Home') {\n                // Home key.\n                next = menuItems[0];\n\n            } else if (trigger == 'End') {\n                // End key.\n                next = menuItems[menuItems.length - 1];\n            }\n\n            // Variable next is set if we do want to act on the keypress.\n            if (next) {\n                e.preventDefault();\n                shiftFocus(next);\n            }\n            return;\n        }\n    });\n\n    // Trap focus if the dropdown is a dialog.\n    $(document).on('shown.bs.dropdown', e => {\n        const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n        if (dialog) {\n            // Use setTimeout to make sure the dialog is positioned correctly to prevent random scrolling.\n            setTimeout(() => {\n                FocusLockManager.trapFocus(dialog);\n            });\n        }\n    });\n\n    // Untrap focus when the dialog dropdown is closed.\n    $(document).on('hidden.bs.dropdown', e => {\n        const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n        if (dialog) {\n            FocusLockManager.untrapFocus();\n        }\n\n        // We need to focus on the menu trigger.\n        const trigger = e.target.querySelector('[data-toggle=\"dropdown\"]');\n        // If it's a click event, then no element is focused because the clicked element is inside a closed dropdown.\n        const focused = e.clickEvent?.target || (document.activeElement !== document.body ? document.activeElement : null);\n        if (trigger && focused && e.target.contains(focused)) {\n            shiftFocus(trigger, () => {\n                if (document.activeElement === document.body) {\n                    // If the focus is currently on the body, then we can safely assume that the focus needs to be updated.\n                    return true;\n                }\n\n                // If the focus is on a child of the clicked element still, then update the focus.\n                return e.target.contains(document.activeElement);\n            });\n        }\n    });\n};\n\n/**\n * A lot of Bootstrap's out of the box features don't work if dropdown items are not focusable.\n */\nconst comboboxFix = () => {\n    $(document).on('show.bs.dropdown', e => {\n        if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n            const combobox = e.relatedTarget;\n            const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n            if (listbox) {\n                const selectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n                // To make sure ArrowDown doesn't move the active option afterwards.\n                setTimeout(() => {\n                    if (selectedOption) {\n                        selectedOption.classList.add('active');\n                        combobox.setAttribute('aria-activedescendant', selectedOption.id);\n                    } else {\n                        const firstOption = listbox.querySelector('[role=\"option\"]');\n                        firstOption.setAttribute('aria-selected', 'true');\n                        firstOption.classList.add('active');\n                        combobox.setAttribute('aria-activedescendant', firstOption.id);\n                    }\n                }, 0);\n            }\n        }\n    });\n\n    $(document).on('hidden.bs.dropdown', e => {\n        if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n            const combobox = e.relatedTarget;\n            const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n            combobox.removeAttribute('aria-activedescendant');\n\n            if (listbox) {\n                setTimeout(() => {\n                    // Undo all previously highlighted options.\n                    listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n                        option.classList.remove('active');\n                    });\n                }, 0);\n            }\n        }\n    });\n\n    // Handling keyboard events for both navigating through and selecting options.\n    document.addEventListener('keydown', e => {\n        if (e.target.matches('[role=\"combobox\"][aria-controls]:not([aria-haspopup=dialog])')) {\n            const combobox = e.target;\n            const trigger = e.key;\n            let next = null;\n            const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n            const options = listbox.querySelectorAll('[role=\"option\"]');\n            const activeOption = listbox.querySelector('.active[role=\"option\"]');\n            const editable = combobox.hasAttribute('aria-autocomplete');\n\n            // Under the special case that the dropdown menu is being shown as a result of the key press (like when the user\n            // presses ArrowDown or Enter or ... to open the dropdown menu), activeOption is not set yet.\n            // It's because of a race condition with show.bs.dropdown event handler.\n            if (options && (activeOption || editable)) {\n                if (trigger == 'ArrowDown') {\n                    for (let i = 0; i < options.length - 1; i++) {\n                        if (options[i] == activeOption) {\n                            next = options[i + 1];\n                            break;\n                        }\n                    }\n                    if (editable && !next) {\n                        next = options[0];\n                    }\n                } if (trigger == 'ArrowUp') {\n                    for (let i = 1; i < options.length; i++) {\n                        if (options[i] == activeOption) {\n                            next = options[i - 1];\n                            break;\n                        }\n                    }\n                    if (editable && !next) {\n                        next = options[options.length - 1];\n                    }\n                } else if (trigger == 'Home' && !editable) {\n                    next = options[0];\n                } else if (trigger == 'End' && !editable) {\n                    next = options[options.length - 1];\n                } else if ((trigger == ' ' && !editable) || trigger == 'Enter') {\n                    e.preventDefault();\n                    selectOption(combobox, activeOption);\n                } else if (!editable) {\n                    // Search for options by finding the first option that has\n                    // text starting with the typed character (case insensitive).\n                    for (let i = 0; i < options.length; i++) {\n                        const option = options[i];\n                        const optionText = option.textContent.trim().toLowerCase();\n                        const keyPressed = e.key.toLowerCase();\n                        if (optionText.indexOf(keyPressed) == 0) {\n                            next = option;\n                            break;\n                        }\n                    }\n                }\n\n                // Variable next is set if we do want to act on the keypress.\n                if (next) {\n                    e.preventDefault();\n                    if (activeOption) {\n                        activeOption.classList.remove('active');\n                    }\n                    next.classList.add('active');\n                    combobox.setAttribute('aria-activedescendant', next.id);\n                    next.scrollIntoView({block: 'nearest'});\n                }\n            }\n        }\n    });\n\n    document.addEventListener('click', e => {\n        const option = e.target.closest('[role=\"listbox\"] [role=\"option\"]');\n        if (option) {\n            const listbox = option.closest('[role=\"listbox\"]');\n            const combobox = document.querySelector(`[role=\"combobox\"][aria-controls=\"${listbox.id}\"]`);\n            if (combobox) {\n                selectOption(combobox, option);\n            }\n        }\n    });\n\n    // In case some code somewhere else changes the value of the combobox.\n    document.addEventListener('change', e => {\n        if (e.target.matches('input[type=\"hidden\"][id]')) {\n            const combobox = document.querySelector(`[role=\"combobox\"][data-input-element=\"${e.target.id}\"]`);\n            const option = e.target.parentElement.querySelector(`[role=\"option\"][data-value=\"${e.target.value}\"]`);\n\n            if (combobox && option) {\n                selectOption(combobox, option);\n            }\n        }\n    });\n\n    const selectOption = (combobox, option) => {\n        const listbox = option.closest('[role=\"listbox\"]');\n        const oldSelectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n        if (oldSelectedOption != option) {\n            if (oldSelectedOption) {\n                oldSelectedOption.removeAttribute('aria-selected');\n            }\n            option.setAttribute('aria-selected', 'true');\n        }\n\n        if (combobox.hasAttribute('value')) {\n            combobox.value = option.dataset.shortText || option.textContent.replace(/[\\n\\r]+|[\\s]{2,}/g, ' ').trim();\n        } else {\n            const selectedOptionContainer = combobox.querySelector('[data-selected-option]');\n            if (selectedOptionContainer) {\n                selectedOptionContainer.textContent = option.dataset.shortText || option.textContent;\n            } else {\n                combobox.textContent = option.dataset.shortText || option.textContent;\n            }\n        }\n\n        if (combobox.dataset.inputElement) {\n            const inputElement = document.getElementById(combobox.dataset.inputElement);\n            if (inputElement && (inputElement.value != option.dataset.value)) {\n                inputElement.value = option.dataset.value;\n                inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n            }\n        }\n    };\n};\n\n/**\n * After page load, focus on any element with special autofocus attribute.\n */\nconst autoFocus = () => {\n    window.addEventListener(\"load\", () => {\n        const alerts = document.querySelectorAll('[data-aria-autofocus=\"true\"][role=\"alert\"]');\n        Array.prototype.forEach.call(alerts, autofocusElement => {\n            // According to the specification an role=\"alert\" region is only read out on change to the content\n            // of that region.\n            autofocusElement.innerHTML += ' ';\n            autofocusElement.removeAttribute('data-aria-autofocus');\n        });\n    });\n};\n\n/**\n * Changes the focus to the correct element based on the key that is pressed.\n * @param {NodeList} elements A NodeList of focusable elements to navigate between.\n * @param {KeyboardEvent} e The keyboard event that triggers the roving focus.\n * @param {boolean} vertical Whether the navigation is vertical.\n * @param {boolean} updateTabIndex Whether to update the tabIndex of the elements.\n */\nconst rovingFocus = (elements, e, vertical, updateTabIndex) => {\n    const rtl = window.right_to_left();\n    const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');\n    const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');\n    const keys = [arrowNext, arrowPrevious, 'Home', 'End'];\n\n    if (!keys.includes(e.key)) {\n        return;\n    }\n\n    const focusElement = index => {\n        elements[index].focus();\n        if (updateTabIndex) {\n            elements.forEach((element, i) => element.setAttribute('tabindex', i === index ? '0' : '-1'));\n        }\n    };\n\n    const currentIndex = Array.prototype.indexOf.call(elements, e.target);\n    let nextIndex;\n\n    switch (e.key) {\n        case arrowNext:\n            e.preventDefault();\n            nextIndex = (currentIndex + 1 < elements.length) ? currentIndex + 1 : 0;\n            focusElement(nextIndex);\n            break;\n        case arrowPrevious:\n            e.preventDefault();\n            nextIndex = (currentIndex - 1 >= 0) ? currentIndex - 1 : elements.length - 1;\n            focusElement(nextIndex);\n            break;\n        case 'Home':\n            e.preventDefault();\n            focusElement(0);\n            break;\n        case 'End':\n            e.preventDefault();\n            focusElement(elements.length - 1);\n    }\n};\n\n/**\n * Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.\n */\nconst tabElementFix = () => {\n    document.addEventListener('keydown', e => {\n        if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n            if (e.target.matches('[role=\"tablist\"] [role=\"tab\"]')) {\n                const tabList = e.target.closest('[role=\"tablist\"]');\n                const tabs = Array.prototype.filter.call(\n                    tabList.querySelectorAll('[role=\"tab\"]'),\n                    tab => !!tab.offsetHeight\n                ); // We only work with the visible tabs.\n                const vertical = tabList.getAttribute('aria-orientation') == 'vertical';\n\n                rovingFocus(tabs, e, vertical, false);\n            }\n        }\n    });\n\n    document.addEventListener('click', e => {\n        if (e.target.matches('[role=\"tablist\"] [data-toggle=\"tab\"], [role=\"tablist\"] [data-toggle=\"pill\"]')) {\n            const tabs = e.target.closest('[role=\"tablist\"]').querySelectorAll('[data-toggle=\"tab\"], [data-toggle=\"pill\"]');\n            e.preventDefault();\n            $(e.target).tab('show');\n            tabs.forEach(tab => {\n                tab.tabIndex = -1;\n            });\n            e.target.tabIndex = 0;\n        }\n    });\n};\n\n/**\n * Fix keyboard interaction with Bootstrap Collapse elements.\n *\n * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure|WAI-ARIA Authoring Practices 1.1 - Disclosure (Show/Hide)}\n */\nconst collapseFix = () => {\n    document.addEventListener('keydown', e => {\n        if (e.target.matches('[data-toggle=\"collapse\"]')) {\n            // Pressing space should toggle expand/collapse.\n            if (e.key === ' ') {\n                e.preventDefault();\n                e.target.click();\n            }\n        }\n    });\n};\n\n/**\n * Fix accessibility issues\n */\nconst toolbarFix = () => {\n    document.addEventListener('keydown', e => {\n        if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n            if (e.target.matches('[role=\"toolbar\"] button')) {\n                const buttons = e.target.closest('[role=\"toolbar\"]').querySelectorAll('button');\n                rovingFocus(buttons, e, false, true);\n            }\n        }\n    });\n};\n\nexport const init = () => {\n    dropdownFix();\n    comboboxFix();\n    autoFocus();\n    tabElementFix();\n    collapseFix();\n    toolbarFix();\n};\n"],"names":["dropdownFix","focusEnd","setFocusEnd","end","shiftFocus","element","focusCheck","pendingPromise","Pending","setTimeout","focus","resolve","handleMenuButton","e","trigger","key","fixFocus","preventDefault","target","click","menu","parentElement","querySelector","menuItems","foundMenuItem","querySelectorAll","length","result","getFocusEnd","document","addEventListener","matches","closest","toLowerCase","i","item","text","trim","indexOf","next","on","dialog","FocusLockManager","trapFocus","untrapFocus","focused","clickEvent","activeElement","body","contains","rovingFocus","elements","vertical","updateTabIndex","rtl","window","right_to_left","arrowNext","arrowPrevious","includes","focusElement","index","forEach","setAttribute","currentIndex","Array","prototype","call","nextIndex","relatedTarget","combobox","listbox","getAttribute","selectedOption","classList","add","id","firstOption","removeAttribute","option","remove","options","activeOption","editable","hasAttribute","selectOption","optionText","textContent","keyPressed","scrollIntoView","block","value","oldSelectedOption","dataset","shortText","replace","selectedOptionContainer","inputElement","getElementById","dispatchEvent","Event","bubbles","comboboxFix","alerts","autofocusElement","innerHTML","tabList","tabs","filter","tab","offsetHeight","tabIndex","buttons"],"mappings":"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"}