/* eslint-disable */
// @ts-nocheck
// Copied from https://github.com/medialize/ally.js
// License: MIT
// Copyright (c) 2015 Rodney Rehm
//
// Entrypoint: ally.js/maintain/tab-focus

import _platform from "@vercel/turbopack-next/compiled/platform";
import cssEscape from "@vercel/turbopack-next/compiled/css.escape";

// 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(_platform));

// 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() {
  // Constructs <foreignObject width="30" height="30"><input type="text"/></foreignObject>
  // without raising a Trusted Types violation
  var foreignObject = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "foreignObject"
  );
  foreignObject.width.baseVal.value = 30;
  foreignObject.height.baseVal.value = 30;
  foreignObject.appendChild(document.createElement("input"));
  foreignObject.lastChild.type = "text";

  return foreignObject;
}

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 querySelector() can't identify foreignObject, but getElementsByTagName() 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="' + cssEscape(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="#' + cssEscape(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"> focusable
    return false;
  }

  var img = getImageOfArea(element);
  if (!img || !isVisible(img)) {
    return false;
  }

  // Firefox only allows fully loaded images to reference image maps
  // https://stereochro.me/ideas/detecting-broken-images-js
  if (
    !supports$2.focusBrokenImageMap &&
    (!img.complete ||
      !img.naturalHeight ||
      img.offsetWidth <= 0 ||
      img.offsetHeight <= 0)
  ) {
    return false;
  }

  // Firefox supports.can focus area elements even if they don't have an href attribute
  if (!supports$2.focusAreaWithoutHref && !element.href) {
    // Internet explorer supports.can focus area elements without href if either
    // the area element or the image element has a tabindex attribute
    return (
      (supports$2.focusAreaTabindex && hasTabindex) ||
      (supports$2.focusAreaImgTabindex && img.hasAttribute("tabindex"))
    );
  }

  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-usemap
  var childOfInteractive = getParents({ context: img })
    .slice(1)
    .some(function (_element) {
      var name = _element.nodeName.toLowerCase();
      return name === "button" || name === "a";
    });

  if (childOfInteractive) {
    return false;
  }

  return true;
}

var supports$3 = void 0;

// https://www.w3.org/TR/html5/disabled-elements.html#concept-element-disabled
var disabledElementsPattern = void 0;
var disabledElements = {
  input: true,
  select: true,
  textarea: true,
  button: true,
  fieldset: true,
  form: true,
};

function isNativeDisabledSupported(context) {
  if (!supports$3) {
    supports$3 = _supports();

    if (supports$3.focusFieldsetDisabled) {
      delete disabledElements.fieldset;
    }

    if (supports$3.focusFormDisabled) {
      delete disabledElements.form;
    }

    disabledElementsPattern = new RegExp(
      "^(" + Object.keys(disabledElements).join("|") + ")$"
    );
  }

  var element = contextToElement({
    label: "is/native-disabled-supported",
    context: context,
  });

  var nodeName = element.nodeName.toLowerCase();
  return Boolean(disabledElementsPattern.test(nodeName));
}

var supports$4 = void 0;

function isDisabledFieldset(element) {
  var nodeName = element.nodeName.toLowerCase();
  return nodeName === "fieldset" && element.disabled;
}

function isDisabledForm(element) {
  var nodeName = element.nodeName.toLowerCase();
  return nodeName === "form" && element.disabled;
}

function isDisabled(context) {
  if (!supports$4) {
    supports$4 = _supports();
  }

  var element = contextToElement({
    label: "is/disabled",
    context: context,
  });

  if (element.hasAttribute("data-ally-disabled")) {
    // treat ally's element/disabled like the DOM native element.disabled
    return true;
  }

  if (!isNativeDisabledSupported(element)) {
    // non-form elements do not support the disabled attribute
    return false;
  }

  if (element.disabled) {
    // the element itself is disabled
    return true;
  }

  var parents = getParents({ context: element });
  if (parents.some(isDisabledFieldset)) {
    // a parental <fieldset> is disabld and inherits the state onto this element
    return true;
  }

  if (!supports$4.focusFormDisabled && parents.some(isDisabledForm)) {
    // a parental <form> is disabld and inherits the state onto this element
    return true;
  }

  return false;
}

function isOnlyTabbableRules() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    _ref$except = _ref.except,
    except =
      _ref$except === undefined
        ? {
            onlyFocusableBrowsingContext: false,
            visible: false,
          }
        : _ref$except;

  var element = contextToElement({
    label: "is/only-tabbable",
    resolveDocument: true,
    context: context,
  });

  if (!except.visible && !isVisible(element)) {
    return false;
  }

  if (
    !except.onlyFocusableBrowsingContext &&
    (platform.is.GECKO || platform.is.TRIDENT || platform.is.EDGE)
  ) {
    var frameElement = getFrameElement(element);
    if (frameElement) {
      if (tabindexValue(frameElement) < 0) {
        // iframe[tabindex="-1"] and object[tabindex="-1"] inherit the
        // tabbable demotion onto elements of their browsing contexts
        return false;
      }
    }
  }

  var nodeName = element.nodeName.toLowerCase();
  var tabindex = tabindexValue(element);

  if (nodeName === "label" && platform.is.GECKO) {
    // Firefox cannot focus, but tab to: label[tabindex=0]
    return tabindex !== null && tabindex >= 0;
  }

  // SVG Elements were keyboard focusable but not script focusable before Firefox 51.
  // Firefox 51 added the focus management DOM API (.focus and .blur) to SVGElement,
  // see https://bugzilla.mozilla.org/show_bug.cgi?id=778654
  if (platform.is.GECKO && element.ownerSVGElement && !element.focus) {
    if (nodeName === "a" && element.hasAttribute("xlink:href")) {
      // any focusable child of <svg> cannot be focused, but tabbed to
      if (platform.is.GECKO) {
        return true;
      }
    }
  }

  return false;
}

// bind exceptions to an iterator callback
isOnlyTabbableRules.except = function () {
  var except =
    arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

  var isOnlyTabbable = function isOnlyTabbable(context) {
    return isOnlyTabbableRules({
      context: context,
      except: except,
    });
  };

  isOnlyTabbable.rules = isOnlyTabbableRules;
  return isOnlyTabbable;
};

// provide isOnlyTabbable(context) as default iterator callback
var isOnlyTabbable = isOnlyTabbableRules.except({});

var supports$5 = void 0;

function isOnlyFocusRelevant(element) {
  var nodeName = element.nodeName.toLowerCase();
  if (nodeName === "embed" || nodeName === "keygen") {
    // embed is considered focus-relevant but not focusable
    // see https://github.com/medialize/ally.js/issues/82
    return true;
  }

  var _tabindex = tabindexValue(element);
  if (element.shadowRoot && _tabindex === null) {
    // ShadowDOM host elements *may* receive focus
    // even though they are not considered focuable
    return true;
  }

  if (nodeName === "label") {
    // <label tabindex="0"> is only tabbable in Firefox, not script-focusable
    // there's no way to make an element focusable other than by adding a tabindex,
    // and focus behavior of the label element seems hard-wired to ignore tabindex
    // in some browsers (like Gecko, Blink and WebKit)
    return !supports$5.focusLabelTabindex || _tabindex === null;
  }

  if (nodeName === "legend") {
    return _tabindex === null;
  }

  if (
    supports$5.focusSvgFocusableAttribute &&
    (element.ownerSVGElement || nodeName === "svg")
  ) {
    // Internet Explorer understands the focusable attribute introduced in SVG Tiny 1.2
    var focusableAttribute = element.getAttribute("focusable");
    return focusableAttribute && focusableAttribute === "false";
  }

  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 _tabindex === null || !supports$5.focusImgUsemapTabindex;
  }

  if (nodeName === "area") {
    // all <area>s are considered relevant,
    // but only the valid <area>s are focusable
    return !isValidArea(element);
  }

  return false;
}

function isFocusableRules() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    _ref$except = _ref.except,
    except =
      _ref$except === undefined
        ? {
            disabled: false,
            visible: false,
            onlyTabbable: false,
          }
        : _ref$except;

  if (!supports$5) {
    supports$5 = _supports();
  }

  var _isOnlyTabbable = isOnlyTabbable.rules.except({
    onlyFocusableBrowsingContext: true,
    visible: except.visible,
  });

  var element = contextToElement({
    label: "is/focusable",
    resolveDocument: true,
    context: context,
  });

  var focusRelevant = isFocusRelevant.rules({
    context: element,
    except: except,
  });

  if (!focusRelevant || isOnlyFocusRelevant(element)) {
    return false;
  }

  if (!except.disabled && isDisabled(element)) {
    return false;
  }

  if (!except.onlyTabbable && _isOnlyTabbable(element)) {
    // some elements may be keyboard focusable, but not script focusable
    return false;
  }

  // elements that are not rendered, cannot be focused
  if (!except.visible) {
    var visibilityOptions = {
      context: element,
      except: {},
    };

    if (supports$5.focusInHiddenIframe) {
      // WebKit and Blink can focus content in hidden <iframe> and <object>
      visibilityOptions.except.browsingContext = true;
    }

    if (supports$5.focusObjectSvgHidden) {
      // Blink allows focusing the object element, even if it has visibility: hidden;
      // @browser-issue Blink https://code.google.com/p/chromium/issues/detail?id=586191
      var _nodeName2 = element.nodeName.toLowerCase();
      if (_nodeName2 === "object") {
        visibilityOptions.except.cssVisibility = true;
      }
    }

    if (!isVisible.rules(visibilityOptions)) {
      return false;
    }
  }

  var frameElement = getFrameElement(element);
  if (frameElement) {
    var _nodeName = frameElement.nodeName.toLowerCase();
    if (_nodeName === "object" && !supports$5.focusInZeroDimensionObject) {
      if (!frameElement.offsetWidth || !frameElement.offsetHeight) {
        // WebKit can not focus content in <object> if it doesn't have dimensions
        return false;
      }
    }
  }

  var nodeName = element.nodeName.toLowerCase();
  if (
    nodeName === "svg" &&
    supports$5.focusSvgInIframe &&
    !frameElement &&
    element.getAttribute("tabindex") === null
  ) {
    return false;
  }

  return true;
}

// bind exceptions to an iterator callback
isFocusableRules.except = function () {
  var except =
    arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

  var isFocusable = function isFocusable(context) {
    return isFocusableRules({
      context: context,
      except: except,
    });
  };

  isFocusable.rules = isFocusableRules;
  return isFocusable;
};

// provide isFocusRelevant(context) as default iterator callback
var isFocusable = isFocusableRules.except({});

function createFilter(condition) {
  // see https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
  var filter = function filter(node) {
    if (node.shadowRoot) {
      // return ShadowRoot elements regardless of them being focusable,
      // so they can be walked recursively later
      return NodeFilter.FILTER_ACCEPT;
    }

    if (condition(node)) {
      // finds elements that could have been found by document.querySelectorAll()
      return NodeFilter.FILTER_ACCEPT;
    }

    return NodeFilter.FILTER_SKIP;
  };
  // IE requires a function, Browsers require {acceptNode: function}
  // see http://www.bennadel.com/blog/2607-finding-html-comment-nodes-in-the-dom-using-treewalker.htm
  filter.acceptNode = filter;
  return filter;
}

var PossiblyFocusableFilter = createFilter(isFocusRelevant);

function queryFocusableStrict() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    includeContext = _ref.includeContext,
    includeOnlyTabbable = _ref.includeOnlyTabbable,
    strategy = _ref.strategy;

  if (!context) {
    context = document.documentElement;
  }

  var _isFocusable = isFocusable.rules.except({
    onlyTabbable: includeOnlyTabbable,
  });

  var _document = getDocument(context);
  // see https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker
  var walker = _document.createTreeWalker(
    // root element to start search in
    context,
    // element type filter
    NodeFilter.SHOW_ELEMENT,
    // custom NodeFilter filter
    strategy === "all" ? PossiblyFocusableFilter : createFilter(_isFocusable),
    // deprecated, but IE requires it
    false
  );

  var list = [];

  while (walker.nextNode()) {
    if (walker.currentNode.shadowRoot) {
      if (_isFocusable(walker.currentNode)) {
        list.push(walker.currentNode);
      }

      list = list.concat(
        queryFocusableStrict({
          context: walker.currentNode.shadowRoot,
          includeOnlyTabbable: includeOnlyTabbable,
          strategy: strategy,
        })
      );
    } else {
      list.push(walker.currentNode);
    }
  }

  // add context if requested and focusable
  if (includeContext) {
    if (strategy === "all") {
      if (isFocusRelevant(context)) {
        list.unshift(context);
      }
    } else if (_isFocusable(context)) {
      list.unshift(context);
    }
  }

  return list;
}

// NOTE: this selector MUST *never* be used directly,
var supports$6 = void 0;

var selector$1 = void 0;

function selector$2() {
  if (!supports$6) {
    supports$6 = _supports();
  }

  if (typeof selector$1 === "string") {
    return selector$1;
  }

  // https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute
  selector$1 =
    "" +
    // IE11 supports.can focus <table> and <td>
    (supports$6.focusTable ? "table, td," : "") +
    // IE11 supports.can focus <fieldset>
    (supports$6.focusFieldset ? "fieldset," : "") +
    // Namespace problems of [xlink:href] explained in https://stackoverflow.com/a/23047888/515124
    // svg a[*|href] does not match in IE9, but since we're filtering
    // through is/focusable we can include all <a> from SVG
    "svg a," +
    // may behave as 'svg, svg *,' in chrome as *every* svg element with a focus event listener is focusable
    // navigational elements
    "a[href]," +
    // validity determined by is/valid-area.js
    "area[href]," +
    // validity determined by is/disabled.js
    "input, select, textarea, button," +
    // browsing context containers
    "iframe, object, embed," +
    // interactive content
    "keygen," +
    (supports$6.focusAudioWithoutControls ? "audio," : "audio[controls],") +
    (supports$6.focusVideoWithoutControls ? "video," : "video[controls],") +
    (supports$6.focusSummary ? "summary," : "") +
    // validity determined by is/valid-tabindex.js
    "[tabindex]," +
    // editing hosts
    "[contenteditable]";

  // where ShadowDOM is supported, we also want the shadowed focusable elements (via ">>>" or "/deep/")
  selector$1 = selectInShadows(selector$1);

  return selector$1;
}

function queryFocusableQuick() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    includeContext = _ref.includeContext,
    includeOnlyTabbable = _ref.includeOnlyTabbable;

  var _selector = selector$2();
  var elements = context.querySelectorAll(_selector);
  // the selector potentially matches more than really is focusable

  var _isFocusable = isFocusable.rules.except({
    onlyTabbable: includeOnlyTabbable,
  });

  var result = [].filter.call(elements, _isFocusable);

  // add context if requested and focusable
  if (includeContext && _isFocusable(context)) {
    result.unshift(context);
  }

  return result;
}

function queryFocusable() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    includeContext = _ref.includeContext,
    includeOnlyTabbable = _ref.includeOnlyTabbable,
    _ref$strategy = _ref.strategy,
    strategy = _ref$strategy === undefined ? "quick" : _ref$strategy;

  var element = contextToElement({
    label: "query/focusable",
    resolveDocument: true,
    defaultToDocument: true,
    context: context,
  });

  var options = {
    context: element,
    includeContext: includeContext,
    includeOnlyTabbable: includeOnlyTabbable,
    strategy: strategy,
  };

  if (strategy === "quick") {
    return queryFocusableQuick(options);
  } else if (strategy === "strict" || strategy === "all") {
    return queryFocusableStrict(options);
  }

  throw new TypeError(
    'query/focusable requires option.strategy to be one of ["quick", "strict", "all"]'
  );
}

var supports$7 = void 0;

// Internet Explorer 11 considers fieldset, table, td focusable, but not tabbable
// Internet Explorer 11 considers body to have [tabindex=0], but does not allow tabbing to it
var focusableElementsPattern = /^(fieldset|table|td|body)$/;

function isTabbableRules() {
  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,
            visible: false,
            onlyTabbable: false,
          }
        : _ref$except;

  if (!supports$7) {
    supports$7 = _supports();
  }

  var element = contextToElement({
    label: "is/tabbable",
    resolveDocument: true,
    context: context,
  });

  if (platform.is.BLINK && platform.is.ANDROID && platform.majorVersion > 42) {
    // External keyboard support worked fine in CHrome 42, but stopped working in Chrome 45.
    // The on-screen keyboard does not provide a way to focus the next input element (like iOS does).
    // That leaves us with no option to advance focus by keyboard, ergo nothing is tabbable (keyboard focusable).
    return false;
  }

  var frameElement = getFrameElement(element);
  if (frameElement) {
    if (platform.is.WEBKIT && platform.is.IOS) {
      // iOS only does not consider anything from another browsing context keyboard focusable
      return false;
    }

    // iframe[tabindex="-1"] and object[tabindex="-1"] inherit the
    // tabbable demotion onto elements of their browsing contexts
    if (tabindexValue(frameElement) < 0) {
      return false;
    }

    if (
      !except.visible &&
      (platform.is.BLINK || platform.is.WEBKIT) &&
      !isVisible(frameElement)
    ) {
      // Blink and WebKit consider elements in hidden browsing contexts focusable, but not tabbable
      return false;
    }

    // Webkit and Blink don't consider anything in <object> tabbable
    // Blink fixed that fixed in Chrome 54, Opera 41
    var frameNodeName = frameElement.nodeName.toLowerCase();
    if (frameNodeName === "object") {
      var isFixedBlink =
        (platform.name === "Chrome" && platform.majorVersion >= 54) ||
        (platform.name === "Opera" && platform.majorVersion >= 41);

      if (platform.is.WEBKIT || (platform.is.BLINK && !isFixedBlink)) {
        return false;
      }
    }
  }

  var nodeName = element.nodeName.toLowerCase();
  var _tabindex = tabindexValue(element);
  var tabindex = _tabindex === null ? null : _tabindex >= 0;

  if (
    platform.is.EDGE &&
    platform.majorVersion >= 14 &&
    frameElement &&
    element.ownerSVGElement &&
    _tabindex < 0
  ) {
    // Edge 14+ considers <a xlink:href="…" tabindex="-1"> keyboard focusable
    // if the element is in a nested browsing context
    return true;
  }

  var hasTabbableTabindexOrNone = tabindex !== false;
  var hasTabbableTabindex = _tabindex !== null && _tabindex >= 0;

  // NOTE: Firefox 31 considers [contenteditable] to have [tabindex=-1], but allows tabbing to it
  // fixed in Firefox 40 the latest - https://bugzilla.mozilla.org/show_bug.cgi?id=1185657
  if (element.hasAttribute("contenteditable")) {
    // tabbing can still be disabled by explicitly providing [tabindex="-1"]
    return hasTabbableTabindexOrNone;
  }

  if (focusableElementsPattern.test(nodeName) && tabindex !== true) {
    return false;
  }

  if (platform.is.WEBKIT && platform.is.IOS) {
    // iOS only considers a hand full of elements tabbable (keyboard focusable)
    // this holds true even with external keyboards
    var potentiallyTabbable =
      (nodeName === "input" && element.type === "text") ||
      element.type === "password" ||
      nodeName === "select" ||
      nodeName === "textarea" ||
      element.hasAttribute("contenteditable");

    if (!potentiallyTabbable) {
      var style = window.getComputedStyle(element, null);
      potentiallyTabbable = isUserModifyWritable(style);
    }

    if (!potentiallyTabbable) {
      return false;
    }
  }

  if (nodeName === "use" && _tabindex !== null) {
    if (
      platform.is.BLINK ||
      (platform.is.WEBKIT && platform.majorVersion === 9)
    ) {
      // In Chrome and Safari 9 the <use> element is keyboard focusable even for tabindex="-1"
      return true;
    }
  }

  if (elementMatches(element, "svg a") && element.hasAttribute("xlink:href")) {
    if (hasTabbableTabindexOrNone) {
      // in Trident and Gecko SVGElement does not handle the tabIndex property properly
      return true;
    }

    if (element.focus && !supports$7.focusSvgNegativeTabindexAttribute) {
      // 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 true;
    }
  }

  if (
    nodeName === "svg" &&
    supports$7.focusSvgInIframe &&
    hasTabbableTabindexOrNone
  ) {
    return true;
  }

  if (platform.is.TRIDENT || platform.is.EDGE) {
    if (nodeName === "svg") {
      if (supports$7.focusSvg) {
        // older Internet Explorers consider <svg> keyboard focusable
        // unless they have focsable="false", but then they wouldn't
        // be focusable and thus not even reach this filter
        return true;
      }

      // elements that have [focusable] are automatically keyboard focusable regardless of the attribute's value
      return element.hasAttribute("focusable") || hasTabbableTabindex;
    }

    if (element.ownerSVGElement) {
      if (supports$7.focusSvgTabindexAttribute && hasTabbableTabindex) {
        return true;
      }

      // elements that have [focusable] are automatically keyboard focusable regardless of the attribute's value
      return element.hasAttribute("focusable");
    }
  }
  if (element.tabIndex === undefined) {
    return Boolean(except.onlyTabbable);
  }

  if (nodeName === "audio") {
    if (!element.hasAttribute("controls")) {
      // In Internet Explorer the <audio> element is focusable, but not tabbable, and tabIndex property is wrong
      return false;
    } else if (platform.is.BLINK) {
      // In Chrome <audio controls tabindex="-1"> remains keyboard focusable
      return true;
    }
  }

  if (nodeName === "video") {
    if (!element.hasAttribute("controls")) {
      if (platform.is.TRIDENT || platform.is.EDGE) {
        // In Internet Explorer and Edge the <video> element is focusable, but not tabbable, and tabIndex property is wrong
        return false;
      }
    } else if (platform.is.BLINK || platform.is.GECKO) {
      // In Chrome and Firefox <video controls tabindex="-1"> remains keyboard focusable
      return true;
    }
  }

  if (nodeName === "object") {
    if (platform.is.BLINK || platform.is.WEBKIT) {
      // In all Blink and WebKit based browsers <embed> and <object> are never keyboard focusable, even with tabindex="0" set
      return false;
    }
  }

  if (nodeName === "iframe") {
    // In Internet Explorer all iframes are only focusable
    // In WebKit, Blink and Gecko iframes may be tabbable depending on content.
    // Since we can't reliably investigate iframe documents because of the
    // SameOriginPolicy, we're declaring everything only focusable.
    return false;
  }

  if (!except.scrollable && platform.is.GECKO) {
    // Firefox considers scrollable containers keyboard focusable,
    // even though their tabIndex property is -1
    var _style = window.getComputedStyle(element, null);
    if (hasCssOverflowScroll(_style)) {
      return hasTabbableTabindexOrNone;
    }
  }

  if (platform.is.TRIDENT || platform.is.EDGE) {
    // IE and Edge degrade <area> to script focusable, if the image
    // using the <map> has been given tabindex="-1"
    if (nodeName === "area") {
      var img = getImageOfArea(element);
      if (img && tabindexValue(img) < 0) {
        return false;
      }
    }

    var _style2 = window.getComputedStyle(element, null);
    if (isUserModifyWritable(_style2)) {
      // prevent being swallowed by the overzealous isScrollableContainer() below
      return element.tabIndex >= 0;
    }

    if (!except.flexbox && hasCssDisplayFlex(_style2)) {
      if (_tabindex !== null) {
        return hasTabbableTabindex;
      }

      return (
        isFocusRelevantWithoutFlexbox(element) &&
        isTabbableWithoutFlexbox(element)
      );
    }

    // IE considers scrollable containers script focusable only,
    // even though their tabIndex property is 0
    if (isScrollableContainer(element, nodeName)) {
      return false;
    }

    var parent = element.parentElement;
    if (parent) {
      var parentNodeName = parent.nodeName.toLowerCase();
      var parentStyle = window.getComputedStyle(parent, null);
      // IE considers scrollable bodies script focusable only,
      if (
        isScrollableContainer(parent, nodeName, parentNodeName, parentStyle)
      ) {
        return false;
      }

      // Children of focusable elements with display:flex are focusable in IE10-11,
      // even though their tabIndex property suggests otherwise
      if (hasCssDisplayFlex(parentStyle)) {
        // value of tabindex takes precedence
        return hasTabbableTabindex;
      }
    }
  }

  // https://www.w3.org/WAI/PF/aria-practices/#focus_tabindex
  return element.tabIndex >= 0;
}

// bind exceptions to an iterator callback
isTabbableRules.except = function () {
  var except =
    arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

  var isTabbable = function isTabbable(context) {
    return isTabbableRules({
      context: context,
      except: except,
    });
  };

  isTabbable.rules = isTabbableRules;
  return isTabbable;
};

var isFocusRelevantWithoutFlexbox = isFocusRelevant.rules.except({
  flexbox: true,
});
var isTabbableWithoutFlexbox = isTabbableRules.except({ flexbox: true });

// provide isTabbable(context) as default iterator callback
var isTabbable = isTabbableRules.except({});

function queryTabbable() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    includeContext = _ref.includeContext,
    includeOnlyTabbable = _ref.includeOnlyTabbable,
    strategy = _ref.strategy;

  var _isTabbable = isTabbable.rules.except({
    onlyTabbable: includeOnlyTabbable,
  });

  return queryFocusable({
    context: context,
    includeContext: includeContext,
    includeOnlyTabbable: includeOnlyTabbable,
    strategy: strategy,
  }).filter(_isTabbable);
}

// sorts a list of elements according to their order in the DOM

function compareDomPosition(a, b) {
  return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
    ? -1
    : 1;
}

function sortDomOrder(elements) {
  return elements.sort(compareDomPosition);
}

function getFirstSuccessorOffset(list, target) {
  // find the first element that comes AFTER the target element
  return findIndex(list, function (element) {
    return (
      target.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING
    );
  });
}

function findInsertionOffsets(list, elements, resolveElement) {
  // instead of mutating the elements list directly, remember position and map
  // to inject later, when we can do this more efficiently
  var insertions = [];
  elements.forEach(function (element) {
    var replace = true;
    var offset = list.indexOf(element);

    if (offset === -1) {
      // element is not in target list
      offset = getFirstSuccessorOffset(list, element);
      replace = false;
    }

    if (offset === -1) {
      // there is no successor in the tabsequence,
      // meaning the image must be the last element
      offset = list.length;
    }

    // allow the consumer to replace the injected element
    var injections = nodeArray(
      resolveElement ? resolveElement(element) : element
    );
    if (!injections.length) {
      // we can't inject zero elements
      return;
    }

    insertions.push({
      offset: offset,
      replace: replace,
      elements: injections,
    });
  });

  return insertions;
}

function insertElementsAtOffsets(list, insertions) {
  // remember the number of elements we have already injected
  // so we account for the caused index offset
  var inserted = 0;
  // make sure that we insert the elements in sequence,
  // otherwise the offset compensation won't work
  insertions.sort(function (a, b) {
    return a.offset - b.offset;
  });
  insertions.forEach(function (insertion) {
    // array.splice has an annoying function signature :(
    var remove = insertion.replace ? 1 : 0;
    var args = [insertion.offset + inserted, remove].concat(insertion.elements);
    list.splice.apply(list, args);
    inserted += insertion.elements.length - remove;
  });
}

function mergeInDomOrder() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    list = _ref.list,
    elements = _ref.elements,
    resolveElement = _ref.resolveElement;

  // operate on a copy so we don't mutate the original array
  var _list = list.slice(0);
  // make sure the elements we're injecting are provided in DOM order
  var _elements = nodeArray(elements).slice(0);
  sortDomOrder(_elements);
  // find the offsets within the target array (list) at which to inject
  // each individual element (from elements)
  var insertions = findInsertionOffsets(_list, _elements, resolveElement);
  // actually inject the elements into the target array at the identified positions
  insertElementsAtOffsets(_list, insertions);
  return _list;
}

var _createClass = (function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
})();

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Maps = (function () {
  function Maps(context) {
    _classCallCheck(this, Maps);

    this._document = getDocument(context);
    this.maps = {};
  }

  _createClass(Maps, [
    {
      key: "getAreasFor",
      value: function getAreasFor(name) {
        if (!this.maps[name]) {
          // the map is not defined within the context, so we
          // have to go find it elsewhere in the document
          this.addMapByName(name);
        }

        return this.maps[name];
      },
    },
    {
      key: "addMapByName",
      value: function addMapByName(name) {
        var map = getMapByName(name, this._document);
        if (!map) {
          // if there is no map, the img[usemap] wasn't doing anything anyway
          return;
        }

        this.maps[map.name] = queryTabbable({ context: map });
      },
    },
    {
      key: "extractAreasFromList",
      value: function extractAreasFromList(elements) {
        // remove all <area> elements from the elements list,
        // but put them the map for later retrieval
        return elements.filter(function (element) {
          var nodeName = element.nodeName.toLowerCase();
          if (nodeName !== "area") {
            return true;
          }

          var map = element.parentNode;
          if (!this.maps[map.name]) {
            this.maps[map.name] = [];
          }

          this.maps[map.name].push(element);
          return false;
        }, this);
      },
    },
  ]);

  return Maps;
})();

function sortArea(elements, context) {
  // images - unless they are focusable themselves, likely not
  // part of the elements list, so we'll have to find them and
  // sort them into the elements list manually
  var usemaps = context.querySelectorAll("img[usemap]");
  var maps = new Maps(context);

  // remove all <area> elements from the elements list,
  // but put them the map for later retrieval
  var _elements = maps.extractAreasFromList(elements);

  if (!usemaps.length) {
    // the context does not contain any <area>s so no need
    // to replace anything, just remove any maps
    return _elements;
  }

  return mergeInDomOrder({
    list: _elements,
    elements: usemaps,
    resolveElement: function resolveElement(image) {
      var name = image.getAttribute("usemap").slice(1);
      return maps.getAreasFor(name);
    },
  });
}

var _createClass$1 = (function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
})();

function _classCallCheck$1(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Shadows = (function () {
  function Shadows(context, sortElements) {
    _classCallCheck$1(this, Shadows);

    // document context we're working with
    this.context = context;
    // callback that sorts an array of elements
    this.sortElements = sortElements;
    // reference to create unique IDs for each ShadowHost
    this.hostCounter = 1;
    // reference map for child-ShadowHosts of a ShadowHost
    this.inHost = {};
    // reference map for child-ShadowHost of the document
    this.inDocument = [];
    // reference map for ShadowHosts
    this.hosts = {};
    // reference map for tabbable elements of a ShadowHost
    this.elements = {};
  }

  // remember which hosts we have to sort within later

  _createClass$1(Shadows, [
    {
      key: "_registerHost",
      value: function _registerHost(host) {
        if (host._sortingId) {
          return;
        }

        // make the ShadowHost identifiable (see cleanup() for undo)
        host._sortingId = "shadow-" + this.hostCounter++;
        this.hosts[host._sortingId] = host;

        // hosts may contain other hosts
        var parentHost = getShadowHost({ context: host });
        if (parentHost) {
          this._registerHost(parentHost);
          this._registerHostParent(host, parentHost);
        } else {
          this.inDocument.push(host);
        }
      },

      // remember which host is the child of which other host
    },
    {
      key: "_registerHostParent",
      value: function _registerHostParent(host, parent) {
        if (!this.inHost[parent._sortingId]) {
          this.inHost[parent._sortingId] = [];
        }

        this.inHost[parent._sortingId].push(host);
      },

      // remember which elements a host contains
    },
    {
      key: "_registerElement",
      value: function _registerElement(element, host) {
        if (!this.elements[host._sortingId]) {
          this.elements[host._sortingId] = [];
        }

        this.elements[host._sortingId].push(element);
      },

      // remove shadowed elements from the sequence and register
      // the ShadowHosts they belong to so we know what to sort
      // later on
    },
    {
      key: "extractElements",
      value: function extractElements(elements) {
        return elements.filter(function (element) {
          var host = getShadowHost({ context: element });
          if (!host) {
            return true;
          }

          this._registerHost(host);
          this._registerElement(element, host);
          return false;
        }, this);
      },

      // inject hosts into the sequence, sort everything,
      // and recoursively replace hosts by its descendants
    },
    {
      key: "sort",
      value: function sort(elements) {
        var _elements = this._injectHosts(elements);
        _elements = this._replaceHosts(_elements);
        this._cleanup();
        return _elements;
      },

      // merge ShadowHosts into the element lists of other ShadowHosts
      // or the document, then sort the individual lists
    },
    {
      key: "_injectHosts",
      value: function _injectHosts(elements) {
        Object.keys(this.hosts).forEach(function (_sortingId) {
          var _list = this.elements[_sortingId];
          var _elements = this.inHost[_sortingId];
          var _context = this.hosts[_sortingId].shadowRoot;
          this.elements[_sortingId] = this._merge(_list, _elements, _context);
        }, this);

        return this._merge(elements, this.inDocument, this.context);
      },
    },
    {
      key: "_merge",
      value: function _merge(list, elements, context) {
        var merged = mergeInDomOrder({
          list: list,
          elements: elements,
        });

        return this.sortElements(merged, context);
      },
    },
    {
      key: "_replaceHosts",
      value: function _replaceHosts(elements) {
        return mergeInDomOrder({
          list: elements,
          elements: this.inDocument,
          resolveElement: this._resolveHostElement.bind(this),
        });
      },
    },
    {
      key: "_resolveHostElement",
      value: function _resolveHostElement(host) {
        var merged = mergeInDomOrder({
          list: this.elements[host._sortingId],
          elements: this.inHost[host._sortingId],
          resolveElement: this._resolveHostElement.bind(this),
        });

        var _tabindex = tabindexValue(host);
        if (_tabindex !== null && _tabindex > -1) {
          return [host].concat(merged);
        }

        return merged;
      },
    },
    {
      key: "_cleanup",
      value: function _cleanup() {
        // remove those identifers we put on the ShadowHost to avoid using Map()
        Object.keys(this.hosts).forEach(function (key) {
          delete this.hosts[key]._sortingId;
        }, this);
      },
    },
  ]);

  return Shadows;
})();

function sortShadowed(elements, context, sortElements) {
  var shadows = new Shadows(context, sortElements);
  var _elements = shadows.extractElements(elements);

  if (_elements.length === elements.length) {
    // no shadowed content found, no need to continue
    return sortElements(elements);
  }

  return shadows.sort(_elements);
}

function sortTabindex(elements) {
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.tabIndex
  // elements with tabIndex "0" (including tabbableElements without tabIndex) should be navigated in the order they appear.
  // elements with a positive tabIndex:
  //   Elements that have identical tabIndexes should be navigated in the order they appear.
  //   Navigation proceeds from the lowest tabIndex to the highest tabIndex.

  // NOTE: sort implementation may be unstable and thus mess up DOM order,
  // that's why we build a map that's being sorted instead. If we were able to rely
  // on a stable sorting algorithm, sortTabindex() could be as simple as
  // elements.sort(function(a, b) { return a.tabIndex - b.tabIndex; });
  // at this time Chrome does not use a stable sorting algorithm
  // see http://blog.rodneyrehm.de/archives/14-Sorting-Were-Doing-It-Wrong.html#stability

  // NOTE: compareDocumentPosition seemed like more overhead than just sorting this with buckets
  // https://developer.mozilla.org/en-US/docs/Web/API/Node.compareDocumentPosition

  var map = {};
  var indexes = [];
  var normal = elements.filter(function (element) {
    // in Trident and Gecko SVGElement does not know about the tabIndex property
    var tabIndex = element.tabIndex;
    if (tabIndex === undefined) {
      tabIndex = tabindexValue(element);
    }

    // extract elements that don't need sorting
    if (tabIndex <= 0 || tabIndex === null || tabIndex === undefined) {
      return true;
    }

    if (!map[tabIndex]) {
      // create sortable bucket for dom-order-preservation of elements with the same tabIndex
      map[tabIndex] = [];
      // maintain a list of unique tabIndexes
      indexes.push(tabIndex);
    }

    // sort element into the proper bucket
    map[tabIndex].push(element);
    // element moved to sorting map, so not "normal" anymore
    return false;
  });

  // sort the tabindex ascending,
  // then resolve them to their appropriate buckets,
  // then flatten the array of arrays to an array
  var _elements = indexes
    .sort()
    .map(function (tabIndex) {
      return map[tabIndex];
    })
    .reduceRight(function (previous, current) {
      return current.concat(previous);
    }, normal);

  return _elements;
}

var supports$8 = void 0;

function moveContextToBeginning(elements, context) {
  var pos = elements.indexOf(context);
  if (pos > 0) {
    var tmp = elements.splice(pos, 1);
    return tmp.concat(elements);
  }

  return elements;
}

function sortElements(elements, _context) {
  if (supports$8.tabsequenceAreaAtImgPosition) {
    // Some browsers sort <area> in DOM order, some place the <area>s
    // where the <img> referecing them would've been in DOM order.
    // https://github.com/medialize/ally.js/issues/5
    elements = sortArea(elements, _context);
  }

  elements = sortTabindex(elements);
  return elements;
}

function queryTabsequence() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    context = _ref.context,
    includeContext = _ref.includeContext,
    includeOnlyTabbable = _ref.includeOnlyTabbable,
    strategy = _ref.strategy;

  if (!supports$8) {
    supports$8 = _supports();
  }

  var _context = nodeArray(context)[0] || document.documentElement;
  var elements = queryTabbable({
    context: _context,
    includeContext: includeContext,
    includeOnlyTabbable: includeOnlyTabbable,
    strategy: strategy,
  });

  if (document.body.createShadowRoot && platform.is.BLINK) {
    // sort tabindex localized to shadow dom
    // see https://github.com/medialize/ally.js/issues/6
    elements = sortShadowed(elements, _context, sortElements);
  } else {
    elements = sortElements(elements, _context);
  }

  if (includeContext) {
    // if we include the context itself, it has to be the first
    // element of the sequence
    elements = moveContextToBeginning(elements, _context);
  }

  return elements;
}

// codes mostly cloned from https://github.com/keithamus/jwerty/blob/master/jwerty.js
// deliberately not exposing characters like <,.-#* because they vary *wildly*
// across keyboard layouts and may cause various problems
// (e.g. "*" is "Shift +" on a German Mac keyboard)
// (e.g. "@" is "Alt L" on a German Mac keyboard)

var keycode = {
  // Element Focus
  tab: 9,

  // Navigation
  left: 37,
  up: 38,
  right: 39,
  down: 40,
  pageUp: 33,
  "page-up": 33,
  pageDown: 34,
  "page-down": 34,
  end: 35,
  home: 36,

  // Action
  enter: 13,
  escape: 27,
  space: 32,

  // Modifier
  shift: 16,
  capsLock: 20,
  "caps-lock": 20,
  ctrl: 17,
  alt: 18,
  meta: 91,
  // in firefox: 224
  // on mac (chrome): meta-left=91, meta-right=93
  // on win (IE11): meta-left=91, meta-right=92
  pause: 19,

  // Content Manipulation
  insert: 45,
  delete: 46,
  backspace: 8,

  // the same logical key may be identified through different keyCodes
  _alias: {
    91: [92, 93, 224],
  },
};

// Function keys (112 - 137)
// NOTE: not every keyboard knows F13+
for (var n = 1; n < 26; n++) {
  keycode["f" + n] = n + 111;
}

// Number keys (48-57, numpad 96-105)
// NOTE: not every keyboard knows num-0+
for (var _n = 0; _n < 10; _n++) {
  var code = _n + 48;
  var numCode = _n + 96;
  keycode[_n] = code;
  keycode["num-" + _n] = numCode;
  keycode._alias[code] = [numCode];
}

// Latin characters (65 - 90)
for (var _n2 = 0; _n2 < 26; _n2++) {
  var _code = _n2 + 65;
  var name$1 = String.fromCharCode(_code).toLowerCase();
  keycode[name$1] = _code;
}

var modifier = {
  alt: "altKey",
  ctrl: "ctrlKey",
  meta: "metaKey",
  shift: "shiftKey",
};

var modifierSequence = Object.keys(modifier).map(function (name) {
  return modifier[name];
});

function createExpectedModifiers(ignoreModifiers) {
  var value = ignoreModifiers ? null : false;
  return {
    altKey: value,
    ctrlKey: value,
    metaKey: value,
    shiftKey: value,
  };
}

function resolveModifiers(modifiers) {
  var ignoreModifiers = modifiers.indexOf("*") !== -1;
  var expected = createExpectedModifiers(ignoreModifiers);

  modifiers.forEach(function (token) {
    if (token === "*") {
      // we've already covered the all-in operator
      return;
    }

    // we want the modifier pressed
    var value = true;
    var operator = token.slice(0, 1);
    if (operator === "?") {
      // we don't care if the modifier is pressed
      value = null;
    } else if (operator === "!") {
      // we do not want the modifier pressed
      value = false;
    }

    if (value !== true) {
      // compensate for the modifier's operator
      token = token.slice(1);
    }

    var propertyName = modifier[token];
    if (!propertyName) {
      throw new TypeError('Unknown modifier "' + token + '"');
    }

    expected[propertyName] = value;
  });

  return expected;
}

function resolveKey(key) {
  var code = keycode[key] || parseInt(key, 10);
  if (!code || typeof code !== "number" || isNaN(code)) {
    throw new TypeError('Unknown key "' + key + '"');
  }

  return [code].concat(keycode._alias[code] || []);
}

function matchModifiers(expected, event) {
  // returns true on match
  return !modifierSequence.some(function (prop) {
    // returns true on mismatch
    return (
      typeof expected[prop] === "boolean" &&
      Boolean(event[prop]) !== expected[prop]
    );
  });
}

function keyBinding(text) {
  return text.split(/\s+/).map(function (_text) {
    var tokens = _text.split("+");
    var _modifiers = resolveModifiers(tokens.slice(0, -1));
    var _keyCodes = resolveKey(tokens.slice(-1));
    return {
      keyCodes: _keyCodes,
      modifiers: _modifiers,
      matchModifiers: matchModifiers.bind(null, _modifiers),
    };
  });
}

// Node.compareDocumentPosition is available since IE9
// see https://developer.mozilla.org/en-US/docs/Web/API/Node.compareDocumentPosition

// callback returns true when element is contained by parent or is the parent suited for use with Array.some()
/*
  USAGE:
    var isChildOf = getParentComparator({parent: someNode});
    listOfElements.some(isChildOf)
*/

function getParentComparator() {
  var _ref =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
    parent = _ref.parent,
    element = _ref.element,
    includeSelf = _ref.includeSelf;

  if (parent) {
    return function isChildOf(node) {
      return Boolean(
        (includeSelf && node === parent) ||
          parent.compareDocumentPosition(node) &
            Node.DOCUMENT_POSITION_CONTAINED_BY
      );
    };
  } else if (element) {
    return function isParentOf(node) {
      return Boolean(
        (includeSelf && element === node) ||
          node.compareDocumentPosition(element) &
            Node.DOCUMENT_POSITION_CONTAINED_BY
      );
    };
  }

  throw new TypeError(
    "util/compare-position#getParentComparator required either options.parent or options.element"
  );
}

// Bug 286933 - Key events in the autocomplete popup should be hidden from page scripts
// @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=286933

function whenKey() {
  var map =
    arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

  var bindings = {};

  var context = nodeArray(map.context)[0] || document.documentElement;
  delete map.context;
  var filter = nodeArray(map.filter);
  delete map.filter;

  var mapKeys = Object.keys(map);
  if (!mapKeys.length) {
    throw new TypeError("when/key requires at least one option key");
  }

  var registerBinding = function registerBinding(event) {
    event.keyCodes.forEach(function (code) {
      if (!bindings[code]) {
        bindings[code] = [];
      }

      bindings[code].push(event);
    });
  };

  mapKeys.forEach(function (text) {
    if (typeof map[text] !== "function") {
      throw new TypeError(
        'when/key requires option["' + text + '"] to be a function'
      );
    }

    var addCallback = function addCallback(event) {
      event.callback = map[text];
      return event;
    };

    keyBinding(text).map(addCallback).forEach(registerBinding);
  });

  var handleKeyDown = function handleKeyDown(event) {
    if (event.defaultPrevented) {
      return;
    }

    if (filter.length) {
      // ignore elements within the exempted sub-trees
      var isParentOfElement = getParentComparator({
        element: event.target,
        includeSelf: true,
      });
      if (filter.some(isParentOfElement)) {
        return;
      }
    }

    var key = event.keyCode || event.which;
    if (!bindings[key]) {
      return;
    }

    bindings[key].forEach(function (_event) {
      if (!_event.matchModifiers(event)) {
        return;
      }

      _event.callback.call(context, event, disengage);
    });
  };

  context.addEventListener("keydown", handleKeyDown, false);

  var disengage = function disengage() {
    context.removeEventListener("keydown", handleKeyDown, false);
  };

  return { disengage: disengage };
}

export default function ({ context } = {}) {
  if (!context) {
    context = document.documentElement;
  }

  // Make sure the supports tests are run before intercepting the Tab key,
  // or IE10 and IE11 will fail to process the first Tab key event. Not
  // limiting this warm-up to IE because it may be a problem elsewhere, too.
  queryTabsequence();

  return whenKey({
    // Safari on OSX may require ALT+TAB to reach links,
    // see https://github.com/medialize/ally.js/issues/146
    "?alt+?shift+tab": function altShiftTab(event) {
      // we're completely taking over the Tab key handling
      event.preventDefault();

      var sequence = queryTabsequence({
        context: context,
      });

      var backward = event.shiftKey;
      var first = sequence[0];
      var last = sequence[sequence.length - 1];

      // wrap around first to last, last to first
      var source = backward ? first : last;
      var target = backward ? last : first;
      if (isActiveElement(source)) {
        target.focus();
        return;
      }

      // find current position in tabsequence
      var currentIndex = void 0;
      var found = sequence.some(function (element, index) {
        if (!isActiveElement(element)) {
          return false;
        }

        currentIndex = index;
        return true;
      });

      if (!found) {
        // redirect to first as we're not in our tabsequence
        first.focus();
        return;
      }

      // shift focus to previous/next element in the sequence
      var offset = backward ? -1 : 1;
      sequence[currentIndex + offset].focus();
    },
  });
}
