gatsby
Version:
Blazing fast modern site generator for React
1,414 lines (1,335 loc) • 100 kB
JavaScript
"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