UNPKG

gatsby

Version:
1,414 lines (1,335 loc) 100 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = _default; var _platform2 = _interopRequireDefault(require("platform")); var _css = _interopRequireDefault(require("css.escape")); /* eslint-disable */ // Adapted from https://github.com/medialize/ally.js // License: MIT // Copyright (c) 2015 Rodney Rehm // // Entrypoint: ally.js/maintain/tab-focus // input may be undefined, selector-tring, Node, NodeList, HTMLCollection, array of Nodes // yes, to some extent this is a bad replica of jQuery's constructor function function nodeArray(input) { if (!input) { return []; } if (Array.isArray(input)) { return input; } // instanceof Node - does not work with iframes if (input.nodeType !== undefined) { return [input]; } if (typeof input === 'string') { input = document.querySelectorAll(input); } if (input.length !== undefined) { return [].slice.call(input, 0); } throw new TypeError('unexpected input ' + String(input)); } function contextToElement(_ref) { var context = _ref.context, _ref$label = _ref.label, label = _ref$label === undefined ? 'context-to-element' : _ref$label, resolveDocument = _ref.resolveDocument, defaultToDocument = _ref.defaultToDocument; var element = nodeArray(context)[0]; if (resolveDocument && element && element.nodeType === Node.DOCUMENT_NODE) { element = element.documentElement; } if (!element && defaultToDocument) { return document.documentElement; } if (!element) { throw new TypeError(label + ' requires valid options.context'); } if (element.nodeType !== Node.ELEMENT_NODE && element.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { throw new TypeError(label + ' requires options.context to be an Element'); } return element; } function getShadowHost() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, context = _ref.context; var element = contextToElement({ label: 'get/shadow-host', context: context }); // walk up to the root var container = null; while (element) { container = element; element = element.parentNode; } // https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeType // NOTE: Firefox 34 does not expose ShadowRoot.host (but 37 does) if (container.nodeType === container.DOCUMENT_FRAGMENT_NODE && container.host) { // the root is attached to a fragment node that has a host return container.host; } return null; } function getDocument(node) { if (!node) { return document; } if (node.nodeType === Node.DOCUMENT_NODE) { return node; } return node.ownerDocument || document; } function isActiveElement(context) { var element = contextToElement({ label: 'is/active-element', resolveDocument: true, context: context }); var _document = getDocument(element); if (_document.activeElement === element) { return true; } var shadowHost = getShadowHost({ context: element }); if (shadowHost && shadowHost.shadowRoot.activeElement === element) { return true; } return false; } // [elem, elem.parent, elem.parent.parent, …, html] // will not contain the shadowRoot (DOCUMENT_FRAGMENT_NODE) and shadowHost function getParents() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, context = _ref.context; var list = []; var element = contextToElement({ label: 'get/parents', context: context }); while (element) { list.push(element); // IE does know support parentElement on SVGElement element = element.parentNode; if (element && element.nodeType !== Node.ELEMENT_NODE) { element = null; } } return list; } // Element.prototype.matches may be available at a different name // https://developer.mozilla.org/en/docs/Web/API/Element/matches var names = ['matches', 'webkitMatchesSelector', 'mozMatchesSelector', 'msMatchesSelector']; var name = null; function findMethodName(element) { names.some(function (_name) { if (!element[_name]) { return false; } name = _name; return true; }); } function elementMatches(element, selector) { if (!name) { findMethodName(element); } return element[name](selector); } // deep clone of original platform var platform = JSON.parse(JSON.stringify(_platform2.default)); // operating system var os = platform.os.family || ''; var ANDROID = os === 'Android'; var WINDOWS = os.slice(0, 7) === 'Windows'; var OSX = os === 'OS X'; var IOS = os === 'iOS'; // layout var BLINK = platform.layout === 'Blink'; var GECKO = platform.layout === 'Gecko'; var TRIDENT = platform.layout === 'Trident'; var EDGE = platform.layout === 'EdgeHTML'; var WEBKIT = platform.layout === 'WebKit'; // browser version (not layout engine version!) var version = parseFloat(platform.version); var majorVersion = Math.floor(version); platform.majorVersion = majorVersion; platform.is = { // operating system ANDROID: ANDROID, WINDOWS: WINDOWS, OSX: OSX, IOS: IOS, // layout BLINK: BLINK, // "Chrome", "Chrome Mobile", "Opera" GECKO: GECKO, // "Firefox" TRIDENT: TRIDENT, // "Internet Explorer" EDGE: EDGE, // "Microsoft Edge" WEBKIT: WEBKIT, // "Safari" // INTERNET EXPLORERS IE9: TRIDENT && majorVersion === 9, IE10: TRIDENT && majorVersion === 10, IE11: TRIDENT && majorVersion === 11 }; function before() { var data = { // remember what had focus to restore after test activeElement: document.activeElement, // remember scroll positions to restore after test windowScrollTop: window.scrollTop, windowScrollLeft: window.scrollLeft, bodyScrollTop: document.body.scrollTop, bodyScrollLeft: document.body.scrollLeft }; // wrap tests in an element hidden from screen readers to prevent them // from announcing focus, which can be quite irritating to the user var iframe = document.createElement('iframe'); iframe.setAttribute('style', 'position:absolute; position:fixed; top:0; left:-2px; width:1px; height:1px; overflow:hidden;'); iframe.setAttribute('aria-live', 'off'); iframe.setAttribute('aria-busy', 'true'); iframe.setAttribute('aria-hidden', 'true'); document.body.appendChild(iframe); var _window = iframe.contentWindow; var _document = _window.document; _document.open(); _document.close(); var wrapper = _document.createElement('div'); _document.body.appendChild(wrapper); data.iframe = iframe; data.wrapper = wrapper; data.window = _window; data.document = _document; return data; } // options.element: // {string} element name // {function} callback(wrapper, document) to generate an element // options.mutate: (optional) // {function} callback(element, wrapper, document) to manipulate element prior to focus-test. // Can return DOMElement to define focus target (default: element) // options.validate: (optional) // {function} callback(element, focusTarget, document) to manipulate test-result function test(data, options) { // make sure we operate on a clean slate data.wrapper.innerHTML = ''; // create dummy element to test focusability of var element = typeof options.element === 'string' ? data.document.createElement(options.element) : options.element(data.wrapper, data.document); // allow callback to further specify dummy element // and optionally define element to focus var focus = options.mutate && options.mutate(element, data.wrapper, data.document); if (!focus && focus !== false) { focus = element; } // element needs to be part of the DOM to be focusable !element.parentNode && data.wrapper.appendChild(element); // test if the element with invalid tabindex can be focused focus && focus.focus && focus.focus(); // validate test's result return options.validate ? options.validate(element, focus, data.document) : data.document.activeElement === focus; } function after(data) { // restore focus to what it was before test and cleanup if (data.activeElement === document.body) { document.activeElement && document.activeElement.blur && document.activeElement.blur(); if (platform.is.IE10) { // IE10 does not redirect focus to <body> when the activeElement is removed document.body.focus(); } } else { data.activeElement && data.activeElement.focus && data.activeElement.focus(); } document.body.removeChild(data.iframe); // restore scroll position window.scrollTop = data.windowScrollTop; window.scrollLeft = data.windowScrollLeft; document.body.scrollTop = data.bodyScrollTop; document.body.scrollLeft = data.bodyScrollLeft; } function detectFocus(tests) { var data = before(); var results = {}; Object.keys(tests).map(function (key) { results[key] = test(data, tests[key]); }); after(data); return results; } // this file is overwritten by `npm run build:pre` var version$1 = '1.4.1'; /* Facility to cache test results in localStorage. USAGE: cache.get('key'); cache.set('key', 'value'); */ function readLocalStorage(key) { // allow reading from storage to retrieve previous support results // even while the document does not have focus var data = void 0; try { data = window.localStorage && window.localStorage.getItem(key); data = data ? JSON.parse(data) : {}; } catch (e) { data = {}; } return data; } function writeLocalStorage(key, value) { if (!document.hasFocus()) { // if the document does not have focus when tests are executed, focus() may // not be handled properly and events may not be dispatched immediately. // This can happen when a document is reloaded while Developer Tools have focus. try { window.localStorage && window.localStorage.removeItem(key); } catch (e) { // ignore } return; } try { window.localStorage && window.localStorage.setItem(key, JSON.stringify(value)); } catch (e) { // ignore } } var userAgent = typeof window !== 'undefined' && window.navigator.userAgent || ''; var cacheKey = 'ally-supports-cache'; var cache = readLocalStorage(cacheKey); // update the cache if ally or the user agent changed (newer version, etc) if (cache.userAgent !== userAgent || cache.version !== version$1) { cache = {}; } cache.userAgent = userAgent; cache.version = version$1; var cache$1 = { get: function get() { return cache; }, set: function set(values) { Object.keys(values).forEach(function (key) { cache[key] = values[key]; }); cache.time = new Date().toISOString(); writeLocalStorage(cacheKey, cache); } }; function cssShadowPiercingDeepCombinator() { var combinator = void 0; // see https://dev.w3.org/csswg/css-scoping-1/#deep-combinator // https://bugzilla.mozilla.org/show_bug.cgi?id=1117572 // https://code.google.com/p/chromium/issues/detail?id=446051 try { document.querySelector('html >>> :first-child'); combinator = '>>>'; } catch (noArrowArrowArrow) { try { // old syntax supported at least up to Chrome 41 // https://code.google.com/p/chromium/issues/detail?id=446051 document.querySelector('html /deep/ :first-child'); combinator = '/deep/'; } catch (noDeep) { combinator = ''; } } return combinator; } var gif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap var focusAreaImgTabindex = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="image-map-tabindex-test">' + '<area shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#image-map-tabindex-test" tabindex="-1" alt="" src="' + gif + '">'; return element.querySelector('area'); } }; // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap var focusAreaTabindex = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="image-map-tabindex-test">' + '<area href="#void" tabindex="-1" shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#image-map-tabindex-test" alt="" src="' + gif + '">'; return false; }, validate: function validate(element, focusTarget, _document) { if (platform.is.GECKO) { // fixes https://github.com/medialize/ally.js/issues/35 // Firefox loads the DataURI asynchronously, causing a false-negative return true; } var focus = element.querySelector('area'); focus.focus(); return _document.activeElement === focus; } }; // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap var focusAreaWithoutHref = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="image-map-area-href-test">' + '<area shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#image-map-area-href-test" alt="" src="' + gif + '">'; return element.querySelector('area'); }, validate: function validate(element, focusTarget, _document) { if (platform.is.GECKO) { // fixes https://github.com/medialize/ally.js/issues/35 // Firefox loads the DataURI asynchronously, causing a false-negative return true; } return _document.activeElement === focusTarget; } }; var focusAudioWithoutControls = { name: 'can-focus-audio-without-controls', element: 'audio', mutate: function mutate(element) { try { // invalid media file can trigger warning in console, data-uri to prevent HTTP request element.setAttribute('src', gif); } catch (e) { // IE9 may throw "Error: Not implemented" } } }; var invalidGif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'; // NOTE: https://github.com/medialize/ally.js/issues/35 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap var focusBrokenImageMap = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="broken-image-map-test"><area href="#void" shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#broken-image-map-test" alt="" src="' + invalidGif + '">'; return element.querySelector('area'); } }; // Children of focusable elements with display:flex are focusable in IE10-11 var focusChildrenOfFocusableFlexbox = { element: 'div', mutate: function mutate(element) { element.setAttribute('tabindex', '-1'); element.setAttribute('style', 'display: -webkit-flex; display: -ms-flexbox; display: flex;'); element.innerHTML = '<span style="display: block;">hello</span>'; return element.querySelector('span'); } }; // fieldset[tabindex=0][disabled] should not be focusable, but Blink and WebKit disagree // @specification https://www.w3.org/TR/html5/disabled-elements.html#concept-element-disabled // @browser-issue Chromium https://crbug.com/453847 // @browser-issue WebKit https://bugs.webkit.org/show_bug.cgi?id=141086 var focusFieldsetDisabled = { element: 'fieldset', mutate: function mutate(element) { element.setAttribute('tabindex', 0); element.setAttribute('disabled', 'disabled'); } }; var focusFieldset = { element: 'fieldset', mutate: function mutate(element) { element.innerHTML = '<legend>legend</legend><p>content</p>'; } }; // elements with display:flex are focusable in IE10-11 var focusFlexboxContainer = { element: 'span', mutate: function mutate(element) { element.setAttribute('style', 'display: -webkit-flex; display: -ms-flexbox; display: flex;'); element.innerHTML = '<span style="display: block;">hello</span>'; } }; // form[tabindex=0][disabled] should be focusable as the // specification doesn't know the disabled attribute on the form element // @specification https://www.w3.org/TR/html5/forms.html#the-form-element var focusFormDisabled = { element: 'form', mutate: function mutate(element) { element.setAttribute('tabindex', 0); element.setAttribute('disabled', 'disabled'); } }; // NOTE: https://github.com/medialize/ally.js/issues/35 // fixes https://github.com/medialize/ally.js/issues/20 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-ismap var focusImgIsmap = { element: 'a', mutate: function mutate(element) { element.href = '#void'; element.innerHTML = '<img ismap src="' + gif + '" alt="">'; return element.querySelector('img'); } }; // NOTE: https://github.com/medialize/ally.js/issues/35 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap var focusImgUsemapTabindex = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="image-map-tabindex-test"><area href="#void" shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#image-map-tabindex-test" tabindex="-1" alt="" ' + 'src="' + gif + '">'; return element.querySelector('img'); } }; var focusInHiddenIframe = { element: function element(wrapper, _document) { var iframe = _document.createElement('iframe'); // iframe must be part of the DOM before accessing the contentWindow is possible wrapper.appendChild(iframe); // create the iframe's default document (<html><head></head><body></body></html>) var iframeDocument = iframe.contentWindow.document; iframeDocument.open(); iframeDocument.close(); return iframe; }, mutate: function mutate(iframe) { iframe.style.visibility = 'hidden'; var iframeDocument = iframe.contentWindow.document; var input = iframeDocument.createElement('input'); iframeDocument.body.appendChild(input); return input; }, validate: function validate(iframe) { var iframeDocument = iframe.contentWindow.document; var focus = iframeDocument.querySelector('input'); return iframeDocument.activeElement === focus; } }; var result = !platform.is.WEBKIT; function focusInZeroDimensionObject() { return result; } // Firefox allows *any* value and treats invalid values like tabindex="-1" // @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 var focusInvalidTabindex = { element: 'div', mutate: function mutate(element) { element.setAttribute('tabindex', 'invalid-value'); } }; var focusLabelTabindex = { element: 'label', mutate: function mutate(element) { element.setAttribute('tabindex', '-1'); }, validate: function validate(element, focusTarget, _document) { // force layout in Chrome 49, otherwise the element won't be focusable /* eslint-disable no-unused-vars */ var variableToPreventDeadCodeElimination = element.offsetHeight; /* eslint-enable no-unused-vars */ element.focus(); return _document.activeElement === element; } }; var svg = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtb' + 'G5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBpZD0ic3ZnIj48dGV4dCB4PSIxMCIgeT0iMjAiIGlkPSJ' + 'zdmctbGluay10ZXh0Ij50ZXh0PC90ZXh0Pjwvc3ZnPg=='; // Note: IE10 on BrowserStack does not like this test var focusObjectSvgHidden = { element: 'object', mutate: function mutate(element) { element.setAttribute('type', 'image/svg+xml'); element.setAttribute('data', svg); element.setAttribute('width', '200'); element.setAttribute('height', '50'); element.style.visibility = 'hidden'; } }; // Note: IE10 on BrowserStack does not like this test var focusObjectSvg = { name: 'can-focus-object-svg', element: 'object', mutate: function mutate(element) { element.setAttribute('type', 'image/svg+xml'); element.setAttribute('data', svg); element.setAttribute('width', '200'); element.setAttribute('height', '50'); }, validate: function validate(element, focusTarget, _document) { if (platform.is.GECKO) { // Firefox seems to be handling the object creation asynchronously and thereby produces a false negative test result. // Because we know Firefox is able to focus object elements referencing SVGs, we simply cheat by sniffing the user agent string return true; } return _document.activeElement === element; } }; // Every Environment except IE9 considers SWF objects focusable var result$1 = !platform.is.IE9; function focusObjectSwf() { return result$1; } var focusRedirectImgUsemap = { element: 'div', mutate: function mutate(element) { element.innerHTML = '<map name="focus-redirect-img-usemap"><area href="#void" shape="rect" coords="63,19,144,45"></map>' + '<img usemap="#focus-redirect-img-usemap" alt="" ' + 'src="' + gif + '">'; // focus the <img>, not the <div> return element.querySelector('img'); }, validate: function validate(element, focusTarget, _document) { var target = element.querySelector('area'); return _document.activeElement === target; } }; // see https://jsbin.com/nenirisage/edit?html,js,console,output var focusRedirectLegend = { element: 'fieldset', mutate: function mutate(element) { element.innerHTML = '<legend>legend</legend><input tabindex="-1"><input tabindex="0">'; // take care of focus in validate(); return false; }, validate: function validate(element, focusTarget, _document) { var focusable = element.querySelector('input[tabindex="-1"]'); var tabbable = element.querySelector('input[tabindex="0"]'); // Firefox requires this test to focus the <fieldset> first, while this is not necessary in // https://jsbin.com/nenirisage/edit?html,js,console,output element.focus(); element.querySelector('legend').focus(); return _document.activeElement === focusable && 'focusable' || _document.activeElement === tabbable && 'tabbable' || ''; } }; // https://github.com/medialize/ally.js/issues/21 var focusScrollBody = { element: 'div', mutate: function mutate(element) { element.setAttribute('style', 'width: 100px; height: 50px; overflow: auto;'); element.innerHTML = '<div style="width: 500px; height: 40px;">scrollable content</div>'; return element.querySelector('div'); } }; // https://github.com/medialize/ally.js/issues/21 var focusScrollContainerWithoutOverflow = { element: 'div', mutate: function mutate(element) { element.setAttribute('style', 'width: 100px; height: 50px;'); element.innerHTML = '<div style="width: 500px; height: 40px;">scrollable content</div>'; } }; // https://github.com/medialize/ally.js/issues/21 var focusScrollContainer = { element: 'div', mutate: function mutate(element) { element.setAttribute('style', 'width: 100px; height: 50px; overflow: auto;'); element.innerHTML = '<div style="width: 500px; height: 40px;">scrollable content</div>'; } }; var focusSummary = { element: 'details', mutate: function mutate(element) { element.innerHTML = '<summary>foo</summary><p>content</p>'; return element.firstElementChild; } }; function makeFocusableForeignObject() { var fragment = document.createElement('div'); fragment.innerHTML = '<svg><foreignObject width="30" height="30">\n <input type="text"/>\n </foreignObject></svg>'; return fragment.firstChild.firstChild; } function focusSvgForeignObjectHack(element) { // Edge13, Edge14: foreignObject focus hack // https://jsbin.com/kunehinugi/edit?html,js,output // https://jsbin.com/fajagi/3/edit?html,js,output var isSvgElement = element.ownerSVGElement || element.nodeName.toLowerCase() === 'svg'; if (!isSvgElement) { return false; } // inject and focus an <input> element into the SVG element to receive focus var foreignObject = makeFocusableForeignObject(); element.appendChild(foreignObject); var input = foreignObject.querySelector('input'); input.focus(); // upon disabling the activeElement, IE and Edge // will not shift focus to <body> like all the other // browsers, but instead find the first focusable // ancestor and shift focus to that input.disabled = true; // clean up element.removeChild(foreignObject); return true; } function generate(element) { return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' + element + '</svg>'; } function focus(element) { if (element.focus) { return; } try { HTMLElement.prototype.focus.call(element); } catch (e) { focusSvgForeignObjectHack(element); } } function validate(element, focusTarget, _document) { focus(focusTarget); return _document.activeElement === focusTarget; } var focusSvgFocusableAttribute = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate('<text focusable="true">a</text>'); return element.querySelector('text'); }, validate: validate }; var focusSvgTabindexAttribute = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate('<text tabindex="0">a</text>'); return element.querySelector('text'); }, validate: validate }; var focusSvgNegativeTabindexAttribute = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate('<text tabindex="-1">a</text>'); return element.querySelector('text'); }, validate: validate }; var focusSvgUseTabindex = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate(['<g id="ally-test-target"><a xlink:href="#void"><text>link</text></a></g>', '<use xlink:href="#ally-test-target" x="0" y="0" tabindex="-1" />'].join('')); return element.querySelector('use'); }, validate: validate }; var focusSvgForeignobjectTabindex = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate('<foreignObject tabindex="-1"><input type="text" /></foreignObject>'); // Safari 8's quersSelector() can't identify foreignObject, but getElementyByTagName() can return element.querySelector('foreignObject') || element.getElementsByTagName('foreignObject')[0]; }, validate: validate }; // Firefox seems to be handling the SVG-document-in-iframe creation asynchronously // and thereby produces a false negative test result. Thus the test is pointless // and we resort to UA sniffing once again. // see http://jsbin.com/vunadohoko/1/edit?js,console,output var result$2 = Boolean(platform.is.GECKO && typeof SVGElement !== 'undefined' && SVGElement.prototype.focus); function focusSvgInIframe() { return result$2; } var focusSvg = { element: 'div', mutate: function mutate(element) { element.innerHTML = generate(''); return element.firstChild; }, validate: validate }; // Firefox allows *any* value and treats invalid values like tabindex="-1" // @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 var focusTabindexTrailingCharacters = { element: 'div', mutate: function mutate(element) { element.setAttribute('tabindex', '3x'); } }; var focusTable = { element: 'table', mutate: function mutate(element, wrapper, _document) { // IE9 has a problem replacing TBODY contents with innerHTML. // https://stackoverflow.com/a/8097055/515124 // element.innerHTML = '<tr><td>cell</td></tr>'; var fragment = _document.createDocumentFragment(); fragment.innerHTML = '<tr><td>cell</td></tr>'; element.appendChild(fragment); } }; var focusVideoWithoutControls = { element: 'video', mutate: function mutate(element) { try { // invalid media file can trigger warning in console, data-uri to prevent HTTP request element.setAttribute('src', gif); } catch (e) { // IE9 may throw "Error: Not implemented" } } }; // https://jsbin.com/vafaba/3/edit?html,js,console,output var result$3 = platform.is.GECKO || platform.is.TRIDENT || platform.is.EDGE; function tabsequenceAreaAtImgPosition() { return result$3; } var testCallbacks = { cssShadowPiercingDeepCombinator: cssShadowPiercingDeepCombinator, focusInZeroDimensionObject: focusInZeroDimensionObject, focusObjectSwf: focusObjectSwf, focusSvgInIframe: focusSvgInIframe, tabsequenceAreaAtImgPosition: tabsequenceAreaAtImgPosition }; var testDescriptions = { focusAreaImgTabindex: focusAreaImgTabindex, focusAreaTabindex: focusAreaTabindex, focusAreaWithoutHref: focusAreaWithoutHref, focusAudioWithoutControls: focusAudioWithoutControls, focusBrokenImageMap: focusBrokenImageMap, focusChildrenOfFocusableFlexbox: focusChildrenOfFocusableFlexbox, focusFieldsetDisabled: focusFieldsetDisabled, focusFieldset: focusFieldset, focusFlexboxContainer: focusFlexboxContainer, focusFormDisabled: focusFormDisabled, focusImgIsmap: focusImgIsmap, focusImgUsemapTabindex: focusImgUsemapTabindex, focusInHiddenIframe: focusInHiddenIframe, focusInvalidTabindex: focusInvalidTabindex, focusLabelTabindex: focusLabelTabindex, focusObjectSvg: focusObjectSvg, focusObjectSvgHidden: focusObjectSvgHidden, focusRedirectImgUsemap: focusRedirectImgUsemap, focusRedirectLegend: focusRedirectLegend, focusScrollBody: focusScrollBody, focusScrollContainerWithoutOverflow: focusScrollContainerWithoutOverflow, focusScrollContainer: focusScrollContainer, focusSummary: focusSummary, focusSvgFocusableAttribute: focusSvgFocusableAttribute, focusSvgTabindexAttribute: focusSvgTabindexAttribute, focusSvgNegativeTabindexAttribute: focusSvgNegativeTabindexAttribute, focusSvgUseTabindex: focusSvgUseTabindex, focusSvgForeignobjectTabindex: focusSvgForeignobjectTabindex, focusSvg: focusSvg, focusTabindexTrailingCharacters: focusTabindexTrailingCharacters, focusTable: focusTable, focusVideoWithoutControls: focusVideoWithoutControls }; function executeTests() { var results = detectFocus(testDescriptions); Object.keys(testCallbacks).forEach(function (key) { results[key] = testCallbacks[key](); }); return results; } var supportsCache = null; function _supports() { if (supportsCache) { return supportsCache; } supportsCache = cache$1.get(); if (!supportsCache.time) { cache$1.set(executeTests()); supportsCache = cache$1.get(); } return supportsCache; } var supports = void 0; // https://www.w3.org/TR/html5/infrastructure.html#rules-for-parsing-integers // NOTE: all browsers agree to allow trailing spaces as well var validIntegerPatternNoTrailing = /^\s*(-|\+)?[0-9]+\s*$/; var validIntegerPatternWithTrailing = /^\s*(-|\+)?[0-9]+.*$/; function isValidTabindex(context) { if (!supports) { supports = _supports(); } var validIntegerPattern = supports.focusTabindexTrailingCharacters ? validIntegerPatternWithTrailing : validIntegerPatternNoTrailing; var element = contextToElement({ label: 'is/valid-tabindex', resolveDocument: true, context: context }); // Edge 14 has a capitalization problem on SVG elements, // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9282058/ var hasTabindex = element.hasAttribute('tabindex'); var hasTabIndex = element.hasAttribute('tabIndex'); if (!hasTabindex && !hasTabIndex) { return false; } // older Firefox and Internet Explorer don't support tabindex on SVG elements var isSvgElement = element.ownerSVGElement || element.nodeName.toLowerCase() === 'svg'; if (isSvgElement && !supports.focusSvgTabindexAttribute) { return false; } // @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 if (supports.focusInvalidTabindex) { return true; } // an element matches the tabindex selector even if its value is invalid var tabindex = element.getAttribute(hasTabindex ? 'tabindex' : 'tabIndex'); // IE11 parses tabindex="" as the value "-32768" // @browser-issue Trident https://connect.microsoft.com/IE/feedback/details/1072965 if (tabindex === '-32768') { return false; } return Boolean(tabindex && validIntegerPattern.test(tabindex)); } function tabindexValue(element) { if (!isValidTabindex(element)) { return null; } // Edge 14 has a capitalization problem on SVG elements, // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9282058/ var hasTabindex = element.hasAttribute('tabindex'); var attributeName = hasTabindex ? 'tabindex' : 'tabIndex'; // @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 var tabindex = parseInt(element.getAttribute(attributeName), 10); return isNaN(tabindex) ? -1 : tabindex; } // this is a shared utility file for focus-relevant.js and tabbable.js // separate testing of this file's functions is not necessary, // as they're implicitly tested by way of the consumers function isUserModifyWritable(style) { // https://www.w3.org/TR/1999/WD-css3-userint-19990916#user-modify // https://github.com/medialize/ally.js/issues/17 var userModify = style.webkitUserModify || ''; return Boolean(userModify && userModify.indexOf('write') !== -1); } function hasCssOverflowScroll(style) { return [style.getPropertyValue('overflow'), style.getPropertyValue('overflow-x'), style.getPropertyValue('overflow-y')].some(function (overflow) { return overflow === 'auto' || overflow === 'scroll'; }); } function hasCssDisplayFlex(style) { return style.display.indexOf('flex') > -1; } function isScrollableContainer(element, nodeName, parentNodeName, parentStyle) { if (nodeName !== 'div' && nodeName !== 'span') { // Internet Explorer advances scrollable containers and bodies to focusable // only if the scrollable container is <div> or <span> - this does *not* // happen for <section>, <article>, … return false; } if (parentNodeName && parentNodeName !== 'div' && parentNodeName !== 'span' && !hasCssOverflowScroll(parentStyle)) { return false; } return element.offsetHeight < element.scrollHeight || element.offsetWidth < element.scrollWidth; } var supports$1 = void 0; function isFocusRelevantRules() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, context = _ref.context, _ref$except = _ref.except, except = _ref$except === undefined ? { flexbox: false, scrollable: false, shadow: false } : _ref$except; if (!supports$1) { supports$1 = _supports(); } var element = contextToElement({ label: 'is/focus-relevant', resolveDocument: true, context: context }); if (!except.shadow && element.shadowRoot) { // a ShadowDOM host receives focus when the focus moves to its content return true; } var nodeName = element.nodeName.toLowerCase(); if (nodeName === 'input' && element.type === 'hidden') { // input[type="hidden"] supports.cannot be focused return false; } if (nodeName === 'input' || nodeName === 'select' || nodeName === 'button' || nodeName === 'textarea') { return true; } if (nodeName === 'legend' && supports$1.focusRedirectLegend) { // specifics filtered in is/focusable return true; } if (nodeName === 'label') { // specifics filtered in is/focusable return true; } if (nodeName === 'area') { // specifics filtered in is/focusable return true; } if (nodeName === 'a' && element.hasAttribute('href')) { return true; } if (nodeName === 'object' && element.hasAttribute('usemap')) { // object[usemap] is not focusable in any browser return false; } if (nodeName === 'object') { var svgType = element.getAttribute('type'); if (!supports$1.focusObjectSvg && svgType === 'image/svg+xml') { // object[type="image/svg+xml"] is not focusable in Internet Explorer return false; } else if (!supports$1.focusObjectSwf && svgType === 'application/x-shockwave-flash') { // object[type="application/x-shockwave-flash"] is not focusable in Internet Explorer 9 return false; } } if (nodeName === 'iframe' || nodeName === 'object') { // browsing context containers return true; } if (nodeName === 'embed' || nodeName === 'keygen') { // embed is considered focus-relevant but not focusable // see https://github.com/medialize/ally.js/issues/82 return true; } if (element.hasAttribute('contenteditable')) { // also see CSS property user-modify below return true; } if (nodeName === 'audio' && (supports$1.focusAudioWithoutControls || element.hasAttribute('controls'))) { return true; } if (nodeName === 'video' && (supports$1.focusVideoWithoutControls || element.hasAttribute('controls'))) { return true; } if (supports$1.focusSummary && nodeName === 'summary') { return true; } var validTabindex = isValidTabindex(element); if (nodeName === 'img' && element.hasAttribute('usemap')) { // Gecko, Trident and Edge do not allow an image with an image map and tabindex to be focused, // it appears the tabindex is overruled so focus is still forwarded to the <map> return validTabindex && supports$1.focusImgUsemapTabindex || supports$1.focusRedirectImgUsemap; } if (supports$1.focusTable && (nodeName === 'table' || nodeName === 'td')) { // IE10-11 supports.can focus <table> and <td> return true; } if (supports$1.focusFieldset && nodeName === 'fieldset') { // IE10-11 supports.can focus <fieldset> return true; } var isSvgElement = nodeName === 'svg'; var isSvgContent = element.ownerSVGElement; var focusableAttribute = element.getAttribute('focusable'); var tabindex = tabindexValue(element); if (nodeName === 'use' && tabindex !== null && !supports$1.focusSvgUseTabindex) { // <use> cannot be made focusable by adding a tabindex attribute anywhere but Blink and WebKit return false; } if (nodeName === 'foreignobject') { // <use> can only be made focusable in Blink and WebKit return tabindex !== null && supports$1.focusSvgForeignobjectTabindex; } if (elementMatches(element, 'svg a') && element.hasAttribute('xlink:href')) { return true; } if ((isSvgElement || isSvgContent) && element.focus && !supports$1.focusSvgNegativeTabindexAttribute && tabindex < 0) { // Firefox 51 and 52 treat any natively tabbable SVG element with // tabindex="-1" as tabbable and everything else as inert // see https://bugzilla.mozilla.org/show_bug.cgi?id=1302340 return false; } if (isSvgElement) { return validTabindex || supports$1.focusSvg || supports$1.focusSvgInIframe || // Internet Explorer understands the focusable attribute introduced in SVG Tiny 1.2 Boolean(supports$1.focusSvgFocusableAttribute && focusableAttribute && focusableAttribute === 'true'); } if (isSvgContent) { if (supports$1.focusSvgTabindexAttribute && validTabindex) { return true; } if (supports$1.focusSvgFocusableAttribute) { // Internet Explorer understands the focusable attribute introduced in SVG Tiny 1.2 return focusableAttribute === 'true'; } } // https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute if (validTabindex) { return true; } var style = window.getComputedStyle(element, null); if (isUserModifyWritable(style)) { return true; } if (supports$1.focusImgIsmap && nodeName === 'img' && element.hasAttribute('ismap')) { // IE10-11 considers the <img> in <a href><img ismap> focusable // https://github.com/medialize/ally.js/issues/20 var hasLinkParent = getParents({ context: element }).some(function (parent) { return parent.nodeName.toLowerCase() === 'a' && parent.hasAttribute('href'); }); if (hasLinkParent) { return true; } } // https://github.com/medialize/ally.js/issues/21 if (!except.scrollable && supports$1.focusScrollContainer) { if (supports$1.focusScrollContainerWithoutOverflow) { // Internet Explorer does will consider the scrollable area focusable // if the element is a <div> or a <span> and it is in fact scrollable, // regardless of the CSS overflow property if (isScrollableContainer(element, nodeName)) { return true; } } else if (hasCssOverflowScroll(style)) { // Firefox requires proper overflow setting, IE does not necessarily // https://developer.mozilla.org/en-US/docs/Web/CSS/overflow return true; } } if (!except.flexbox && supports$1.focusFlexboxContainer && hasCssDisplayFlex(style)) { // elements with display:flex are focusable in IE10-11 return true; } var parent = element.parentElement; if (!except.scrollable && parent) { var parentNodeName = parent.nodeName.toLowerCase(); var parentStyle = window.getComputedStyle(parent, null); if (supports$1.focusScrollBody && isScrollableContainer(parent, nodeName, parentNodeName, parentStyle)) { // scrollable bodies are focusable Internet Explorer // https://github.com/medialize/ally.js/issues/21 return true; } // Children of focusable elements with display:flex are focusable in IE10-11 if (supports$1.focusChildrenOfFocusableFlexbox) { if (hasCssDisplayFlex(parentStyle)) { return true; } } } // NOTE: elements marked as inert are not focusable, // but that property is not exposed to the DOM // https://www.w3.org/TR/html5/editing.html#inert return false; } // bind exceptions to an iterator callback isFocusRelevantRules.except = function () { var except = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var isFocusRelevant = function isFocusRelevant(context) { return isFocusRelevantRules({ context: context, except: except }); }; isFocusRelevant.rules = isFocusRelevantRules; return isFocusRelevant; }; // provide isFocusRelevant(context) as default iterator callback var isFocusRelevant = isFocusRelevantRules.except({}); function findIndex(array, callback) { // attempt to use native or polyfilled Array#findIndex first if (array.findIndex) { return array.findIndex(callback); } var length = array.length; // shortcut if the array is empty if (length === 0) { return -1; } // otherwise loop over array for (var i = 0; i < length; i++) { if (callback(array[i], i, array)) { return i; } } return -1; } function getContentDocument(node) { try { // works on <object> and <iframe> return node.contentDocument || // works on <object> and <iframe> node.contentWindow && node.contentWindow.document || // works on <object> and <iframe> that contain SVG node.getSVGDocument && node.getSVGDocument() || null; } catch (e) { // SecurityError: Failed to read the 'contentDocument' property from 'HTMLObjectElement' // also IE may throw member not found exception e.g. on <object type="image/png"> return null; } } function getWindow(node) { var _document = getDocument(node); return _document.defaultView || window; } var shadowPrefix = void 0; function selectInShadows(selector) { if (typeof shadowPrefix !== 'string') { var operator = cssShadowPiercingDeepCombinator(); if (operator) { shadowPrefix = ', html ' + operator + ' '; } } if (!shadowPrefix) { return selector; } return selector + shadowPrefix + selector.replace(/\s*,\s*/g, ',').split(',').join(shadowPrefix); } var selector = void 0; function findDocumentHostElement(_window) { if (!selector) { selector = selectInShadows('object, iframe'); } if (_window._frameElement !== undefined) { return _window._frameElement; } _window._frameElement = null; var potentialHosts = _window.parent.document.querySelectorAll(selector); [].some.call(potentialHosts, function (element) { var _document = getContentDocument(element); if (_document !== _window.document) { return false; } _window._frameElement = element; return true; }); return _window._frameElement; } function getFrameElement(element) { var _window = getWindow(element); if (!_window.parent || _window.parent === _window) { // if there is no parent browsing context, // we're not going to get a frameElement either way return null; } try { // see https://developer.mozilla.org/en-US/docs/Web/API/Window/frameElement // does not work within <embed> anywhere, and not within in <object> in IE return _window.frameElement || findDocumentHostElement(_window); } catch (e) { return null; } } // https://www.w3.org/TR/html5/rendering.html#being-rendered // <area> is not rendered, but we *consider* it visible to simplfiy this function's usage var notRenderedElementsPattern = /^(area)$/; function computedStyle(element, property) { return window.getComputedStyle(element, null).getPropertyValue(property); } function notDisplayed(_path) { return _path.some(function (element) { // display:none is not visible (optimized away at layout) return computedStyle(element, 'display') === 'none'; }); } function notVisible(_path) { // https://github.com/jquery/jquery-ui/blob/master/ui/core.js#L109-L114 // NOTE: a nested element can reverse visibility:hidden|collapse by explicitly setting visibility:visible // NOTE: visibility can be ["", "visible", "hidden", "collapse"] var hidden = findIndex(_path, function (element) { var visibility = computedStyle(element, 'visibility'); return visibility === 'hidden' || visibility === 'collapse'; }); if (hidden === -1) { // there is no hidden element return false; } var visible = findIndex(_path, function (element) { return computedStyle(element, 'visibility') === 'visible'; }); if (visible === -1) { // there is no visible element (but a hidden element) return true; } if (hidden < visible) { // there is a hidden element and it's closer than the first visible element return true; } // there may be a hidden element, but the closest element is visible return false; } function collapsedParent(_path) { var offset = 1; if (_path[0].nodeName.toLowerCase() === 'summary') { offset = 2; } return _path.slice(offset).some(function (element) { // "content children" of a closed details element are not visible return element.nodeName.toLowerCase() === 'details' && element.open === false; }); } function isVisibleRules() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, context = _ref.context, _ref$except = _ref.except, except = _ref$except === undefined ? { notRendered: false, cssDisplay: false, cssVisibility: false, detailsElement: false, browsingContext: false } : _ref$except; var element = contextToElement({ label: 'is/visible', resolveDocument: true, context: context }); var nodeName = element.nodeName.toLowerCase(); if (!except.notRendered && notRenderedElementsPattern.test(nodeName)) { return true; } var _path = getParents({ context: element }); // in Internet Explorer <audio> has a default display: none, where others have display: inline // but IE allows focusing <audio style="display:none">, but not <div display:none><audio> // this is irrelevant to other browsers, as the controls attribute is required to make <audio> focusable var isAudioWithoutControls = nodeName === 'audio' && !element.hasAttribute('controls'); if (!except.cssDisplay && notDisplayed(isAudioWithoutControls ? _path.slice(1) : _path)) { return false; } if (!except.cssVisibility && notVisible(_path)) { return false; } if (!except.detailsElement && collapsedParent(_path)) { return false; } if (!except.browsingContext) { // elements within a browsing context are affected by the // browsing context host element's visibility and tabindex var frameElement = getFrameElement(element); var _isVisible = isVisibleRules.except(except); if (frameElement && !_isVisible(frameElement)) { return false; } } return true; } // bind exceptions to an iterator callback isVisibleRules.except = function () { var except = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var isVisible = function isVisible(context) { return isVisibleRules({ context: context, except: except }); }; isVisible.rules = isVisibleRules; return isVisible; }; // provide isVisible(context) as default iterator callback var isVisible = isVisibleRules.except({}); function getMapByName(name, _document) { // apparently getElementsByName() also considers id attribute in IE & opera // https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByName var map = _document.querySelector('map[name="' + (0, _css.default)(name) + '"]'); return map || null; } function getImageOfArea(element) { var map = element.parentElement; if (!map.name || map.nodeName.toLowerCase() !== 'map') { return null; } // NOTE: image maps can also be applied to <object> with image content, // but no browser supports this at the moment // HTML5 specifies HTMLMapElement.images to be an HTMLCollection of all // <img> and <object> referencing the <map> element, but no browser implements this // https://www.w3.org/TR/html5/embedded-content-0.html#the-map-element // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMapElement // the image must be valid and loaded for the map to take effect var _document = getDocument(element); return _document.querySelector('img[usemap="#' + (0, _css.default)(map.name) + '"]') || null; } var supports$2 = void 0; // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap // https://github.com/jquery/jquery-ui/blob/master/ui/core.js#L88-L107 function isValidArea(context) { if (!supports$2) { supports$2 = _supports(); } var element = contextToElement({ label: 'is/valid-area', context: context }); var nodeName = element.nodeName.toLowerCase(); if (nodeName !== 'area') { return false; } var hasTabindex = element.hasAttribute('tabindex'); if (!supports$2.focusAreaTabindex && hasTabindex) { // Blink and WebKit do not consider <area tabindex="-1" href="#void"> focusab