{"version":3,"file":"focus-visible.min.js","sources":["../src/focus-visible.js"],"sourcesContent":["/**\n * https://github.com/WICG/focus-visible\n */\nfunction init() {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesWhitelist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName == 'INPUT' && inputTypesWhitelist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName == 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * Treat `keydown` as a signal that the user is in keyboard modality.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {Event} e\n */\n function onKeyDown(e) {\n if (isValidFocusTarget(document.activeElement)) {\n addFocusVisibleClass(document.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState == 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. ¯\\_(ツ)_/¯\n if (e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('focus', onFocus, true);\n document.addEventListener('blur', onBlur, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n addInitialPointerMoveListeners();\n\n document.body.classList.add('js-focus-visible');\n}\n\n/**\n * Subscription when the DOM is ready\n * @param {Function} callback\n */\nfunction onDOMReady(callback) {\n var loaded;\n\n /**\n * Callback wrapper for check loaded state\n */\n function load() {\n if (!loaded) {\n loaded = true;\n\n callback();\n }\n }\n\n if (['interactive', 'complete'].indexOf(document.readyState) >= 0) {\n callback();\n } else {\n loaded = false;\n document.addEventListener('DOMContentLoaded', load, false);\n window.addEventListener('load', load, false);\n }\n}\n\nif (typeof document !== 'undefined') {\n onDOMReady(init);\n}\n"],"names":["document","callback","loaded","load","indexOf","readyState","addEventListener","window","onDOMReady","hadKeyboardEvent","hadFocusVisibleRecently","hadFocusVisibleRecentlyTimeout","inputTypesWhitelist","text","search","url","tel","email","password","number","date","month","week","time","datetime","datetime-local","isValidFocusTarget","el","nodeName","classList","addFocusVisibleClass","contains","add","setAttribute","onPointerDown","e","addInitialPointerMoveListeners","onInitialPointerMove","target","toLowerCase","removeEventListener","activeElement","type","tagName","readOnly","isContentEditable","hasAttribute","clearTimeout","setTimeout","remove","removeAttribute","visibilityState","body"],"mappings":"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"}