vendor: 10,728 bytes, lines 1-304
1/** 2 * Applies the :focus-visible polyfill at the given scope. 3 * A scope in this case is either the top-level Document or a Shadow Root. 4 * 5 * @param {(Document|ShadowRoot)} scope 6 * @see https://github.com/WICG/focus-visible 7 */ 8function applyFocusVisiblePolyfill(scope) { 9 var hadKeyboardEvent = true; 10 var hadFocusVisibleRecently = false; 11 var hadFocusVisibleRecentlyTimeout = null; 12 13 var inputTypesAllowlist = { 14 text: true, 15 search: true, 16 url: true, 17 tel: true, 18 email: true, 19 password: true, 20 number: true, 21 date: true, 22 month: true, 23 week: true, 24 time: true, 25 datetime: true, 26 'datetime-local': true 27 }; 28 29 /** 30 * Helper function for legacy browsers and iframes which sometimes focus 31 * elements like document, body, and non-interactive SVG. 32 * @param {Element} el 33 */ 34 function isValidFocusTarget(el) { 35 if ( 36 el && 37 el !== document && 38 el.nodeName !== 'HTML' && 39 el.nodeName !== 'BODY' && 40 'classList' in el && 41 'contains' in el.classList 42 ) { 43 return true; 44 } 45 return false; 46 } 47 48 /** 49 * Computes whether the given element should automatically trigger the 50 * `focus-visible` class being added, i.e. whether it should always match 51 * `:focus-visible` when focused. 52 * @param {Element} el 53 * @return {boolean} 54 */ 55 function focusTriggersKeyboardModality(el) { 56 var type = el.type; 57 var tagName = el.tagName; 58 59 if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) { 60 return true; 61 } 62 63 if (tagName === 'TEXTAREA' && !el.readOnly) { 64 return true; 65 } 66 67 if (el.isContentEditable) { 68 return true; 69 } 70 71 return false; 72 } 73 74 /** 75 * Add the `focus-visible` class to the given element if it was not added by 76 * the author. 77 * @param {Element} el 78 */ 79 function addFocusVisibleClass(el) { 80 if (el.classList.contains('focus-visible')) { 81 return; 82 } 83 el.classList.add('focus-visible'); 84 el.setAttribute('data-focus-visible-added', ''); 85 } 86 87 /** 88 * Remove the `focus-visible` class from the given element if it was not 89 * originally added by the author. 90 * @param {Element} el 91 */ 92 function removeFocusVisibleClass(el) { 93 if (!el.hasAttribute('data-focus-visible-added')) { 94 return; 95 } 96 el.classList.remove('focus-visible'); 97 el.removeAttribute('data-focus-visible-added'); 98 } 99 100 /** 101 * If the most recent user interaction was via the keyboard; 102 * and the key press did not include a meta, alt/option, or control key; 103 * then the modality is keyboard. Otherwise, the modality is not keyboard. 104 * Apply `focus-visible` to any current active element and keep track 105 * of our keyboard modality state with `hadKeyboardEvent`. 106 * @param {KeyboardEvent} e 107 */ 108 function onKeyDown(e) { 109 if (e.metaKey || e.altKey || e.ctrlKey) { 110 return; 111 } 112 113 if (isValidFocusTarget(scope.activeElement)) { 114 addFocusVisibleClass(scope.activeElement); 115 } 116 117 hadKeyboardEvent = true; 118 } 119 120 /** 121 * If at any point a user clicks with a pointing device, ensure that we change 122 * the modality away from keyboard. 123 * This avoids the situation where a user presses a key on an already focused 124 * element, and then clicks on a different element, focusing it with a 125 * pointing device, while we still think we're in keyboard modality. 126 * @param {Event} e 127 */ 128 function onPointerDown(e) { 129 hadKeyboardEvent = false; 130 } 131 132 /** 133 * On `focus`, add the `focus-visible` class to the target if: 134 * - the target received focus as a result of keyboard navigation, or 135 * - the event target is an element that will likely require interaction 136 * via the keyboard (e.g. a text box) 137 * @param {Event} e 138 */ 139 function onFocus(e) { 140 // Prevent IE from focusing the document or HTML element. 141 if (!isValidFocusTarget(e.target)) { 142 return; 143 } 144 145 if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) { 146 addFocusVisibleClass(e.target); 147 } 148 } 149 150 /** 151 * On `blur`, remove the `focus-visible` class from the target. 152 * @param {Event} e 153 */ 154 function onBlur(e) { 155 if (!isValidFocusTarget(e.target)) { 156 return; 157 } 158 159 if ( 160 e.target.classList.contains('focus-visible') || 161 e.target.hasAttribute('data-focus-visible-added') 162 ) { 163 // To detect a tab/window switch, we look for a blur event followed 164 // rapidly by a visibility change. 165 // If we don't see a visibility change within 100ms, it's probably a 166 // regular focus change. 167 hadFocusVisibleRecently = true; 168 window.clearTimeout(hadFocusVisibleRecentlyTimeout); 169 hadFocusVisibleRecentlyTimeout = window.setTimeout(function() { 170 hadFocusVisibleRecently = false; 171 }, 100); 172 removeFocusVisibleClass(e.target); 173 } 174 } 175 176 /** 177 * If the user changes tabs, keep track of whether or not the previously 178 * focused element had .focus-visible. 179 * @param {Event} e 180 */ 181 function onVisibilityChange(e) { 182 if (document.visibilityState === 'hidden') { 183 // If the tab becomes active again, the browser will handle calling focus 184 // on the element (Safari actually calls it twice). 185 // If this tab change caused a blur on an element with focus-visible, 186 // re-apply the class when the user switches back to the tab. 187 if (hadFocusVisibleRecently) { 188 hadKeyboardEvent = true; 189 } 190 addInitialPointerMoveListeners(); 191 } 192 } 193 194 /** 195 * Add a group of listeners to detect usage of any pointing devices. 196 * These listeners will be added when the polyfill first loads, and anytime 197 * the window is blurred, so that they are active when the window regains 198 * focus. 199 */ 200 function addInitialPointerMoveListeners() { 201 document.addEventListener('mousemove', onInitialPointerMove); 202 document.addEventListener('mousedown', onInitialPointerMove); 203 document.addEventListener('mouseup', onInitialPointerMove); 204 document.addEventListener('pointermove', onInitialPointerMove); 205 document.addEventListener('pointerdown', onInitialPointerMove); 206 document.addEventListener('pointerup', onInitialPointerMove); 207 document.addEventListener('touchmove', onInitialPointerMove); 208 document.addEventListener('touchstart', onInitialPointerMove); 209 document.addEventListener('touchend', onInitialPointerMove); 210 } 211 212 function removeInitialPointerMoveListeners() { 213 document.removeEventListener('mousemove', onInitialPointerMove); 214 document.removeEventListener('mousedown', onInitialPointerMove); 215 document.removeEventListener('mouseup', onInitialPointerMove); 216 document.removeEventListener('pointermove', onInitialPointerMove); 217 document.removeEventListener('pointerdown', onInitialPointerMove); 218 document.removeEventListener('pointerup', onInitialPointerMove); 219 document.removeEventListener('touchmove', onInitialPointerMove); 220 document.removeEventListener('touchstart', onInitialPointerMove); 221 document.removeEventListener('touchend', onInitialPointerMove); 222 } 223 224 /** 225 * When the polfyill first loads, assume the user is in keyboard modality. 226 * If any event is received from a pointing device (e.g. mouse, pointer, 227 * touch), turn off keyboard modality. 228 * This accounts for situations where focus enters the page from the URL bar. 229 * @param {Event} e 230 */ 231 function onInitialPointerMove(e) { 232 // Work around a Safari quirk that fires a mousemove on <html> whenever the 233 // window blurs, even if you're tabbing out of the page. ¯\_(ã)_/¯ 234 if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') { 235 return; 236 } 237 238 hadKeyboardEvent = false; 239 removeInitialPointerMoveListeners(); 240 } 241 242 // For some kinds of state, we are interested in changes at the global scope 243 // only. For example, global pointer input, global key presses and global 244 // visibility change should affect the state at every scope: 245 document.addEventListener('keydown', onKeyDown, true); 246 document.addEventListener('mousedown', onPointerDown, true); 247 document.addEventListener('pointerdown', onPointerDown, true); 248 document.addEventListener('touchstart', onPointerDown, true); 249 document.addEventListener('visibilitychange', onVisibilityChange, true); 250 251 addInitialPointerMoveListeners(); 252 253 // For focus and blur, we specifically care about state changes in the local 254 // scope. This is because focus / blur events that originate from within a 255 // shadow root are not re-dispatched from the host element if it was already 256 // the active element in its own scope: 257 scope.addEventListener('focus', onFocus, true); 258 scope.addEventListener('blur', onBlur, true); 259 260 // We detect that a node is a ShadowRoot by ensuring that it is a 261 // DocumentFragment and also has a host property. This check covers native 262 // implementation and polyfill implementation transparently. If we only cared 263 // about the native implementation, we could just check if the scope was 264 // an instance of a ShadowRoot. 265 if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) { 266 // Since a ShadowRoot is a special kind of DocumentFragment, it does not 267 // have a root element to add a class to. So, we add this attribute to the 268 // host element instead: 269 scope.host.setAttribute('data-js-focus-visible', ''); 270 } else if (scope.nodeType === Node.DOCUMENT_NODE) { 271 document.documentElement.classList.add('js-focus-visible'); 272 document.documentElement.setAttribute('data-js-focus-visible', ''); 273 } 274} 275 276// It is important to wrap all references to global window and document in 277// these checks to support server-side rendering use cases 278// @see https://github.com/WICG/focus-visible/issues/199 279if (typeof window !== 'undefined' && typeof document !== 'undefined') { 280 // Make the polyfill helper globally available. This can be used as a signal 281 // to interested libraries that wish to coordinate with the polyfill for e.g., 282 // applying the polyfill to a shadow root: 283 window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill; 284 285 // Notify interested libraries of the polyfill's presence, in case the 286 // polyfill was loaded lazily: 287 var event; 288 289 try { 290 event = new CustomEvent('focus-visible-polyfill-ready'); 291 } catch (error) { 292 // IE11 does not support using CustomEvent as a constructor directly: 293 event = document.createEvent('CustomEvent'); 294 event.initCustomEvent('focus-visible-polyfill-ready', false, false, {}); 295 } 296 297 window.dispatchEvent(event); 298} 299 300if (typeof document !== 'undefined') { 301 // Apply the polyfill to the global document, so that no JavaScript 302 // coordination is required to use the polyfill in the top-level document: 303 applyFocusVisiblePolyfill(document); 304}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.