{ "version": 3, "sources": ["../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/webpack/universalModuleDefinition", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/webpack/bootstrap", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/isObject.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/debounce.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/throttle.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_root.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_Symbol.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/smoothscroll-polyfill/dist/smoothscroll.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/tabbable/index.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/now.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_freeGlobal.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/(webpack)/buildin/global.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/toNumber.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_baseTrim.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_trimmedEndIndex.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/isSymbol.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_baseGetTag.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_getRawTag.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/_objectToString.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/lodash/isObjectLike.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/node_modules/element-closest/index.mjs", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/toArray.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/qsa.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/getElements.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/hasOwnProperty.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/isObject.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/minmax.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/passive.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/pick.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/values.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/snap-slider.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/getStyle.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/getClosestAttribute.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/onReady.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/on.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/helpers/dashes.js", "../node_modules/@tannerhodges/snap-slider/webpack:/SnapSlider/src/modules/logger.js", "../node_modules/css-has-pseudo/src/encode/decode.mjs", "../node_modules/css-has-pseudo/src/encode/extract.mjs", "../node_modules/css-has-pseudo/src/encode/encode.mjs", "../node_modules/css-has-pseudo/src/browser.js", "../node_modules/@mrhenry/core-web/modules/~element-qsa-has.js", "../modules/dril_quip.com/dril-quip/video/default/index.js", "../modules/dril_quip.com/dril-quip/product-group/default/index.js", "../modules/dril_quip.com/dril-quip/gallery/default/index.js", "../node_modules/dialog-polyfill/dist/dialog-polyfill.esm.js", "../modules/dril_quip.com/dril-quip/people/default/index.js", "../modules/dril_quip.com/dril-quip/page-list/filters/index.js", "../modules/dril_quip.com/dril-quip/site-header/index.js", "../modules/dril_quip.com/dril-quip/site-header/mega-menu.js", "../modules/dril_quip.com/dril-quip/tabs/default/index.js", "../modules/dril_quip.com/dril-quip/tabs/default/samfix.js", "../modules/dril_quip.com/dril-quip/article/default/index.js", "../modules/dril_quip.com/dril-quip/accordion/default/index.js", "../modules/dril_quip.com/dril-quip/contact-form/default/index.js", "../modules/dril_quip.com/dril-quip/contact-form/two-column/index.js", "../modules/dril_quip.com/core/search/Google-CSE/index.js", "dril_quip.com/tables.js", "dril_quip.com/index.js"], "sourcesContent": ["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"SnapSlider\"] = factory();\n\telse\n\t\troot[\"SnapSlider\"] = factory();\n})(window, function() {\nreturn ", " \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 18);\n", "/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n}\n\nmodule.exports = isObject;\n", "var isObject = require('./isObject'),\n now = require('./now'),\n toNumber = require('./toNumber');\n\n/** Error message constants. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n timeWaiting = wait - timeSinceLastCall;\n\n return maxing\n ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)\n : timeWaiting;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n clearTimeout(timerId);\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\nmodule.exports = debounce;\n", "var debounce = require('./debounce'),\n isObject = require('./isObject');\n\n/** Error message constants. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/**\n * Creates a throttled function that only invokes `func` at most once per\n * every `wait` milliseconds. The throttled function comes with a `cancel`\n * method to cancel delayed `func` invocations and a `flush` method to\n * immediately invoke them. Provide `options` to indicate whether `func`\n * should be invoked on the leading and/or trailing edge of the `wait`\n * timeout. The `func` is invoked with the last arguments provided to the\n * throttled function. Subsequent calls to the throttled function return the\n * result of the last `func` invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the throttled function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.throttle` and `_.debounce`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to throttle.\n * @param {number} [wait=0] The number of milliseconds to throttle invocations to.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=true]\n * Specify invoking on the leading edge of the timeout.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new throttled function.\n * @example\n *\n * // Avoid excessively updating the position while scrolling.\n * jQuery(window).on('scroll', _.throttle(updatePosition, 100));\n *\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });\n * jQuery(element).on('click', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * jQuery(window).on('popstate', throttled.cancel);\n */\nfunction throttle(func, wait, options) {\n var leading = true,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (isObject(options)) {\n leading = 'leading' in options ? !!options.leading : leading;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n return debounce(func, wait, {\n 'leading': leading,\n 'maxWait': wait,\n 'trailing': trailing\n });\n}\n\nmodule.exports = throttle;\n", "var freeGlobal = require('./_freeGlobal');\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\nmodule.exports = root;\n", "var root = require('./_root');\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\n", "/* smoothscroll v0.4.4 - 2019 - Dustan Kasten, Jeremias Menichelli - MIT License */\n(function () {\n 'use strict';\n\n // polyfill\n function polyfill() {\n // aliases\n var w = window;\n var d = document;\n\n // return if scroll behavior is supported and polyfill is not forced\n if (\n 'scrollBehavior' in d.documentElement.style &&\n w.__forceSmoothScrollPolyfill__ !== true\n ) {\n return;\n }\n\n // globals\n var Element = w.HTMLElement || w.Element;\n var SCROLL_TIME = 468;\n\n // object gathering original scroll methods\n var original = {\n scroll: w.scroll || w.scrollTo,\n scrollBy: w.scrollBy,\n elementScroll: Element.prototype.scroll || scrollElement,\n scrollIntoView: Element.prototype.scrollIntoView\n };\n\n // define timing method\n var now =\n w.performance && w.performance.now\n ? w.performance.now.bind(w.performance)\n : Date.now;\n\n /**\n * indicates if a the current browser is made by Microsoft\n * @method isMicrosoftBrowser\n * @param {String} userAgent\n * @returns {Boolean}\n */\n function isMicrosoftBrowser(userAgent) {\n var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/'];\n\n return new RegExp(userAgentPatterns.join('|')).test(userAgent);\n }\n\n /*\n * IE has rounding bug rounding down clientHeight and clientWidth and\n * rounding up scrollHeight and scrollWidth causing false positives\n * on hasScrollableSpace\n */\n var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0;\n\n /**\n * changes scroll position inside an element\n * @method scrollElement\n * @param {Number} x\n * @param {Number} y\n * @returns {undefined}\n */\n function scrollElement(x, y) {\n this.scrollLeft = x;\n this.scrollTop = y;\n }\n\n /**\n * returns result of applying ease math function to a number\n * @method ease\n * @param {Number} k\n * @returns {Number}\n */\n function ease(k) {\n return 0.5 * (1 - Math.cos(Math.PI * k));\n }\n\n /**\n * indicates if a smooth behavior should be applied\n * @method shouldBailOut\n * @param {Number|Object} firstArg\n * @returns {Boolean}\n */\n function shouldBailOut(firstArg) {\n if (\n firstArg === null ||\n typeof firstArg !== 'object' ||\n firstArg.behavior === undefined ||\n firstArg.behavior === 'auto' ||\n firstArg.behavior === 'instant'\n ) {\n // first argument is not an object/null\n // or behavior is auto, instant or undefined\n return true;\n }\n\n if (typeof firstArg === 'object' && firstArg.behavior === 'smooth') {\n // first argument is an object and behavior is smooth\n return false;\n }\n\n // throw error when behavior is not supported\n throw new TypeError(\n 'behavior member of ScrollOptions ' +\n firstArg.behavior +\n ' is not a valid value for enumeration ScrollBehavior.'\n );\n }\n\n /**\n * indicates if an element has scrollable space in the provided axis\n * @method hasScrollableSpace\n * @param {Node} el\n * @param {String} axis\n * @returns {Boolean}\n */\n function hasScrollableSpace(el, axis) {\n if (axis === 'Y') {\n return el.clientHeight + ROUNDING_TOLERANCE < el.scrollHeight;\n }\n\n if (axis === 'X') {\n return el.clientWidth + ROUNDING_TOLERANCE < el.scrollWidth;\n }\n }\n\n /**\n * indicates if an element has a scrollable overflow property in the axis\n * @method canOverflow\n * @param {Node} el\n * @param {String} axis\n * @returns {Boolean}\n */\n function canOverflow(el, axis) {\n var overflowValue = w.getComputedStyle(el, null)['overflow' + axis];\n\n return overflowValue === 'auto' || overflowValue === 'scroll';\n }\n\n /**\n * indicates if an element can be scrolled in either axis\n * @method isScrollable\n * @param {Node} el\n * @param {String} axis\n * @returns {Boolean}\n */\n function isScrollable(el) {\n var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y');\n var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X');\n\n return isScrollableY || isScrollableX;\n }\n\n /**\n * finds scrollable parent of an element\n * @method findScrollableParent\n * @param {Node} el\n * @returns {Node} el\n */\n function findScrollableParent(el) {\n while (el !== d.body && isScrollable(el) === false) {\n el = el.parentNode || el.host;\n }\n\n return el;\n }\n\n /**\n * self invoked function that, given a context, steps through scrolling\n * @method step\n * @param {Object} context\n * @returns {undefined}\n */\n function step(context) {\n var time = now();\n var value;\n var currentX;\n var currentY;\n var elapsed = (time - context.startTime) / SCROLL_TIME;\n\n // avoid elapsed times higher than one\n elapsed = elapsed > 1 ? 1 : elapsed;\n\n // apply easing to elapsed time\n value = ease(elapsed);\n\n currentX = context.startX + (context.x - context.startX) * value;\n currentY = context.startY + (context.y - context.startY) * value;\n\n context.method.call(context.scrollable, currentX, currentY);\n\n // scroll more if we have not reached our destination\n if (currentX !== context.x || currentY !== context.y) {\n w.requestAnimationFrame(step.bind(w, context));\n }\n }\n\n /**\n * scrolls window or element with a smooth behavior\n * @method smoothScroll\n * @param {Object|Node} el\n * @param {Number} x\n * @param {Number} y\n * @returns {undefined}\n */\n function smoothScroll(el, x, y) {\n var scrollable;\n var startX;\n var startY;\n var method;\n var startTime = now();\n\n // define scroll context\n if (el === d.body) {\n scrollable = w;\n startX = w.scrollX || w.pageXOffset;\n startY = w.scrollY || w.pageYOffset;\n method = original.scroll;\n } else {\n scrollable = el;\n startX = el.scrollLeft;\n startY = el.scrollTop;\n method = scrollElement;\n }\n\n // scroll looping over a frame\n step({\n scrollable: scrollable,\n method: method,\n startTime: startTime,\n startX: startX,\n startY: startY,\n x: x,\n y: y\n });\n }\n\n // ORIGINAL METHODS OVERRIDES\n // w.scroll and w.scrollTo\n w.scroll = w.scrollTo = function() {\n // avoid action when no arguments are passed\n if (arguments[0] === undefined) {\n return;\n }\n\n // avoid smooth behavior if not required\n if (shouldBailOut(arguments[0]) === true) {\n original.scroll.call(\n w,\n arguments[0].left !== undefined\n ? arguments[0].left\n : typeof arguments[0] !== 'object'\n ? arguments[0]\n : w.scrollX || w.pageXOffset,\n // use top prop, second argument if present or fallback to scrollY\n arguments[0].top !== undefined\n ? arguments[0].top\n : arguments[1] !== undefined\n ? arguments[1]\n : w.scrollY || w.pageYOffset\n );\n\n return;\n }\n\n // LET THE SMOOTHNESS BEGIN!\n smoothScroll.call(\n w,\n d.body,\n arguments[0].left !== undefined\n ? ~~arguments[0].left\n : w.scrollX || w.pageXOffset,\n arguments[0].top !== undefined\n ? ~~arguments[0].top\n : w.scrollY || w.pageYOffset\n );\n };\n\n // w.scrollBy\n w.scrollBy = function() {\n // avoid action when no arguments are passed\n if (arguments[0] === undefined) {\n return;\n }\n\n // avoid smooth behavior if not required\n if (shouldBailOut(arguments[0])) {\n original.scrollBy.call(\n w,\n arguments[0].left !== undefined\n ? arguments[0].left\n : typeof arguments[0] !== 'object' ? arguments[0] : 0,\n arguments[0].top !== undefined\n ? arguments[0].top\n : arguments[1] !== undefined ? arguments[1] : 0\n );\n\n return;\n }\n\n // LET THE SMOOTHNESS BEGIN!\n smoothScroll.call(\n w,\n d.body,\n ~~arguments[0].left + (w.scrollX || w.pageXOffset),\n ~~arguments[0].top + (w.scrollY || w.pageYOffset)\n );\n };\n\n // Element.prototype.scroll and Element.prototype.scrollTo\n Element.prototype.scroll = Element.prototype.scrollTo = function() {\n // avoid action when no arguments are passed\n if (arguments[0] === undefined) {\n return;\n }\n\n // avoid smooth behavior if not required\n if (shouldBailOut(arguments[0]) === true) {\n // if one number is passed, throw error to match Firefox implementation\n if (typeof arguments[0] === 'number' && arguments[1] === undefined) {\n throw new SyntaxError('Value could not be converted');\n }\n\n original.elementScroll.call(\n this,\n // use left prop, first number argument or fallback to scrollLeft\n arguments[0].left !== undefined\n ? ~~arguments[0].left\n : typeof arguments[0] !== 'object' ? ~~arguments[0] : this.scrollLeft,\n // use top prop, second argument or fallback to scrollTop\n arguments[0].top !== undefined\n ? ~~arguments[0].top\n : arguments[1] !== undefined ? ~~arguments[1] : this.scrollTop\n );\n\n return;\n }\n\n var left = arguments[0].left;\n var top = arguments[0].top;\n\n // LET THE SMOOTHNESS BEGIN!\n smoothScroll.call(\n this,\n this,\n typeof left === 'undefined' ? this.scrollLeft : ~~left,\n typeof top === 'undefined' ? this.scrollTop : ~~top\n );\n };\n\n // Element.prototype.scrollBy\n Element.prototype.scrollBy = function() {\n // avoid action when no arguments are passed\n if (arguments[0] === undefined) {\n return;\n }\n\n // avoid smooth behavior if not required\n if (shouldBailOut(arguments[0]) === true) {\n original.elementScroll.call(\n this,\n arguments[0].left !== undefined\n ? ~~arguments[0].left + this.scrollLeft\n : ~~arguments[0] + this.scrollLeft,\n arguments[0].top !== undefined\n ? ~~arguments[0].top + this.scrollTop\n : ~~arguments[1] + this.scrollTop\n );\n\n return;\n }\n\n this.scroll({\n left: ~~arguments[0].left + this.scrollLeft,\n top: ~~arguments[0].top + this.scrollTop,\n behavior: arguments[0].behavior\n });\n };\n\n // Element.prototype.scrollIntoView\n Element.prototype.scrollIntoView = function() {\n // avoid smooth behavior if not required\n if (shouldBailOut(arguments[0]) === true) {\n original.scrollIntoView.call(\n this,\n arguments[0] === undefined ? true : arguments[0]\n );\n\n return;\n }\n\n // LET THE SMOOTHNESS BEGIN!\n var scrollableParent = findScrollableParent(this);\n var parentRects = scrollableParent.getBoundingClientRect();\n var clientRects = this.getBoundingClientRect();\n\n if (scrollableParent !== d.body) {\n // reveal element inside parent\n smoothScroll.call(\n this,\n scrollableParent,\n scrollableParent.scrollLeft + clientRects.left - parentRects.left,\n scrollableParent.scrollTop + clientRects.top - parentRects.top\n );\n\n // reveal parent in viewport unless is fixed\n if (w.getComputedStyle(scrollableParent).position !== 'fixed') {\n w.scrollBy({\n left: parentRects.left,\n top: parentRects.top,\n behavior: 'smooth'\n });\n }\n } else {\n // reveal element in viewport\n w.scrollBy({\n left: clientRects.left,\n top: clientRects.top,\n behavior: 'smooth'\n });\n }\n };\n }\n\n if (typeof exports === 'object' && typeof module !== 'undefined') {\n // commonjs\n module.exports = { polyfill: polyfill };\n } else {\n // global\n polyfill();\n }\n\n}());\n", "var candidateSelectors = [\n 'input',\n 'select',\n 'textarea',\n 'a[href]',\n 'button',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n];\nvar candidateSelector = candidateSelectors.join(',');\n\nvar matches = typeof Element === 'undefined'\n ? function () {}\n : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;\n\nfunction tabbable(el, options) {\n options = options || {};\n\n var regularTabbables = [];\n var orderedTabbables = [];\n\n var candidates = el.querySelectorAll(candidateSelector);\n\n if (options.includeContainer) {\n if (matches.call(el, candidateSelector)) {\n candidates = Array.prototype.slice.apply(candidates);\n candidates.unshift(el);\n }\n }\n\n var i, candidate, candidateTabindex;\n for (i = 0; i < candidates.length; i++) {\n candidate = candidates[i];\n\n if (!isNodeMatchingSelectorTabbable(candidate)) continue;\n\n candidateTabindex = getTabindex(candidate);\n if (candidateTabindex === 0) {\n regularTabbables.push(candidate);\n } else {\n orderedTabbables.push({\n documentOrder: i,\n tabIndex: candidateTabindex,\n node: candidate,\n });\n }\n }\n\n var tabbableNodes = orderedTabbables\n .sort(sortOrderedTabbables)\n .map(function(a) { return a.node })\n .concat(regularTabbables);\n\n return tabbableNodes;\n}\n\ntabbable.isTabbable = isTabbable;\ntabbable.isFocusable = isFocusable;\n\nfunction isNodeMatchingSelectorTabbable(node) {\n if (\n !isNodeMatchingSelectorFocusable(node)\n || isNonTabbableRadio(node)\n || getTabindex(node) < 0\n ) {\n return false;\n }\n return true;\n}\n\nfunction isTabbable(node) {\n if (!node) throw new Error('No node provided');\n if (matches.call(node, candidateSelector) === false) return false;\n return isNodeMatchingSelectorTabbable(node);\n}\n\nfunction isNodeMatchingSelectorFocusable(node) {\n if (\n node.disabled\n || isHiddenInput(node)\n || isHidden(node)\n ) {\n return false;\n }\n return true;\n}\n\nvar focusableCandidateSelector = candidateSelectors.concat('iframe').join(',');\nfunction isFocusable(node) {\n if (!node) throw new Error('No node provided');\n if (matches.call(node, focusableCandidateSelector) === false) return false;\n return isNodeMatchingSelectorFocusable(node);\n}\n\nfunction getTabindex(node) {\n var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);\n if (!isNaN(tabindexAttr)) return tabindexAttr;\n // Browsers do not return `tabIndex` correctly for contentEditable nodes;\n // so if they don't have a tabindex attribute specifically set, assume it's 0.\n if (isContentEditable(node)) return 0;\n return node.tabIndex;\n}\n\nfunction sortOrderedTabbables(a, b) {\n return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;\n}\n\nfunction isContentEditable(node) {\n return node.contentEditable === 'true';\n}\n\nfunction isInput(node) {\n return node.tagName === 'INPUT';\n}\n\nfunction isHiddenInput(node) {\n return isInput(node) && node.type === 'hidden';\n}\n\nfunction isRadio(node) {\n return isInput(node) && node.type === 'radio';\n}\n\nfunction isNonTabbableRadio(node) {\n return isRadio(node) && !isTabbableRadio(node);\n}\n\nfunction getCheckedRadio(nodes) {\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].checked) {\n return nodes[i];\n }\n }\n}\n\nfunction isTabbableRadio(node) {\n if (!node.name) return true;\n // This won't account for the edge case where you have radio groups with the same\n // in separate forms on the same page.\n var radioSet = node.ownerDocument.querySelectorAll('input[type=\"radio\"][name=\"' + node.name + '\"]');\n var checked = getCheckedRadio(radioSet);\n return !checked || checked === node;\n}\n\nfunction isHidden(node) {\n // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,\n // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.\n return node.offsetParent === null || getComputedStyle(node).visibility === 'hidden';\n}\n\nmodule.exports = tabbable;\n", "var root = require('./_root');\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\nmodule.exports = now;\n", "/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\nmodule.exports = freeGlobal;\n", "var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n", "var baseTrim = require('./_baseTrim'),\n isObject = require('./isObject'),\n isSymbol = require('./isSymbol');\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = baseTrim(value);\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = toNumber;\n", "var trimmedEndIndex = require('./_trimmedEndIndex');\n\n/** Used to match leading whitespace. */\nvar reTrimStart = /^\\s+/;\n\n/**\n * The base implementation of `_.trim`.\n *\n * @private\n * @param {string} string The string to trim.\n * @returns {string} Returns the trimmed string.\n */\nfunction baseTrim(string) {\n return string\n ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')\n : string;\n}\n\nmodule.exports = baseTrim;\n", "/** Used to match a single whitespace character. */\nvar reWhitespace = /\\s/;\n\n/**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace\n * character of `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the index of the last non-whitespace character.\n */\nfunction trimmedEndIndex(string) {\n var index = string.length;\n\n while (index-- && reWhitespace.test(string.charAt(index))) {}\n return index;\n}\n\nmodule.exports = trimmedEndIndex;\n", "var baseGetTag = require('./_baseGetTag'),\n isObjectLike = require('./isObjectLike');\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && baseGetTag(value) == symbolTag);\n}\n\nmodule.exports = isSymbol;\n", "var Symbol = require('./_Symbol'),\n getRawTag = require('./_getRawTag'),\n objectToString = require('./_objectToString');\n\n/** `Object#toString` result references. */\nvar nullTag = '[object Null]',\n undefinedTag = '[object Undefined]';\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n}\n\nmodule.exports = baseGetTag;\n", "var Symbol = require('./_Symbol');\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\nfunction getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n}\n\nmodule.exports = getRawTag;\n", "/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\nfunction objectToString(value) {\n return nativeObjectToString.call(value);\n}\n\nmodule.exports = objectToString;\n", "/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return value != null && typeof value == 'object';\n}\n\nmodule.exports = isObjectLike;\n", "function polyfill(window) {\n var ElementPrototype = window.Element.prototype;\n\n if (typeof ElementPrototype.matches !== 'function') {\n ElementPrototype.matches = ElementPrototype.msMatchesSelector || ElementPrototype.mozMatchesSelector || ElementPrototype.webkitMatchesSelector || function matches(selector) {\n var element = this;\n var elements = (element.document || element.ownerDocument).querySelectorAll(selector);\n var index = 0;\n\n while (elements[index] && elements[index] !== element) {\n ++index;\n }\n\n return Boolean(elements[index]);\n };\n }\n\n if (typeof ElementPrototype.closest !== 'function') {\n ElementPrototype.closest = function closest(selector) {\n var element = this;\n\n while (element && element.nodeType === 1) {\n if (element.matches(selector)) {\n return element;\n }\n\n element = element.parentNode;\n }\n\n return null;\n };\n }\n}\n\nexport default polyfill;\n//# sourceMappingURL=index.mjs.map\n", "/**\n * Convert a value to an array.\n * @param {mixed} val\n * @return {Array}\n */\nexport default function toArray(val) {\n return Array.prototype.slice.call(val);\n}\n", "import toArray from './toArray';\n\n/**\n * QSA = \"Query Selector All\" that returns an Array instead of a NodeList.\n * @param {String} selector\n * @param {Element} context\n * @return {Array}\n */\nexport default function qsa(selector, context) {\n return selector ? toArray((context || document).querySelectorAll(selector)) : [];\n}\n", "import qsa from './qsa';\n\n/**\n * Get an array of elements from a mixed-value parameter.\n * Accepts Strings, Elements, and Array-like values.\n * @param {String|Element|Array} elements\n * @param {Element} context\n * @return {Array}\n */\nexport default function getElements(elements, context) {\n // 1. If value is a String, query the DOM\n if (typeof elements === 'string') {\n return qsa(elements, context);\n }\n\n // 2. Put single element in an Array\n if (elements instanceof Element) {\n return [elements];\n }\n\n // 3. Assume everything can be treated like an Array\n if (elements) {\n return Array.prototype.slice.call(elements);\n }\n\n // 4. Otherwise, fallback to an empty array\n return [];\n}\n", "/**\n * Check whether an object actually has a property.\n * @param {Object} obj\n * @param {String} prop\n * @return {Boolean}\n */\nexport default function hasOwnProperty(obj, prop) {\n return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n", "/**\n * Strict check for Objects. Literally checks whether\n * the value's constructor is \"Object\".\n * @param {mixed} val\n * @return {String}\n */\nexport default function isObject(val) {\n return val && val.constructor.name === 'Object';\n}\n", "/**\n * Keep a value within some minimum and maximum values.\n * @param {Number} value\n * @param {Number} min\n * @param {Number} max\n * @return {Number}\n */\nexport default function minmax(value, min, max) {\n value = Math.min(max, value);\n value = Math.max(min, value);\n return value;\n}\n", "/* eslint-disable getter-return */\n\n/**\n * Passive Event Listeners.\n *\n * Pass this value as a 3rd argument to your event listeners\n * to dramatically improve performance.\n *\n * @see https://developers.google.com/web/updates/2016/06/passive-event-listeners\n * @see https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection\n * @type {Boolean}\n */\nconst passive = (() => {\n let result = false;\n\n try {\n const opts = Object.defineProperty({}, 'passive', {\n get: function get() {\n result = { passive: true };\n },\n });\n window.addEventListener('testPassive', null, opts);\n window.removeEventListener('testPassive', null, opts);\n } catch (error) {\n // \uD83E\uDD2B\n }\n\n return result;\n})();\n\nexport default passive;\n", "/**\n * Pick keys from an object.\n * @param {Object} obj\n * @param {Array} keys\n * @return {Object}\n */\nexport default function pick(obj, keys) {\n return keys.reduce((result, key) => {\n result[key] = obj[key];\n\n return result;\n }, {});\n}\n", "/* eslint-disable no-restricted-syntax, no-var, vars-on-top */\n\nimport hasOwnProperty from './hasOwnProperty';\n\n/**\n * Helper that does the same thing as `Object.values()`. Works in IE!\n * @param {Object} obj\n * @return {Array}\n */\nfunction values(obj) {\n var arr = [];\n\n for (var prop in obj) {\n if (hasOwnProperty(obj, prop)) {\n arr.push(obj[prop]);\n }\n }\n\n return arr;\n}\n\n// Use native code if supported, else return polyfill.\n// eslint-disable-next-line no-confusing-arrow\nconst fn = (() => typeof Object.values === 'function' ? Object.values : values)();\n\nexport default fn;\n", "// Dependencies\nimport elementClosest from 'element-closest';\nimport smoothscroll from 'smoothscroll-polyfill';\nimport tabbable from 'tabbable';\n\n// Helpers\nimport debounce from 'lodash/debounce';\nimport throttle from 'lodash/throttle';\nimport getClosestAttribute from './helpers/getClosestAttribute';\nimport getElements from './helpers/getElements';\nimport getStyle from './helpers/getStyle';\nimport hasOwnProperty from './helpers/hasOwnProperty';\nimport isObject from './helpers/isObject';\nimport minmax from './helpers/minmax';\nimport on from './helpers/on';\nimport onReady from './helpers/onReady';\nimport passive from './helpers/passive';\nimport pick from './helpers/pick';\nimport qsa from './helpers/qsa';\nimport toArray from './helpers/toArray';\nimport values from './helpers/values';\n\n// Modules\nconst logger = (process.env.NODE_ENV !== 'production')\n ? require('./modules/logger')\n : {};\n\n// Internal Variables\nlet counter = 1;\n\n/**\n * Snap Slider.\n * @class\n */\nclass SnapSlider {\n /**\n * New Snap Slider.\n *\n * See `init()` for a full breakdown of `options`.\n *\n * @param {String|Element|Array|Object} containerOrOptions\n * @param {Object} options\n * @constructor\n */\n constructor(containerOrOptions, options = {}) {\n // Setup internal variables.\n this.terms = {\n prev: /(prev|back|before|left|up)/,\n next: /(next|forward|after|right|down)/,\n };\n\n /* eslint-disable quote-props */\n this.callbacks = {\n 'load': [],\n 'change': [],\n 'change.click': [],\n 'change.scroll': [],\n 'change.keydown': [],\n 'change.focusin': [],\n 'scroll': [],\n 'scroll.start': [],\n 'scroll.end': [],\n };\n /* eslint-enable quote-props */\n\n this.init(containerOrOptions, options);\n\n // Don't construct sliders with empty containers.\n if (!this.container) {\n return;\n }\n\n this.watchForChanges();\n\n // Keep track of the slider so we can reference & debug it later.\n this.container.SnapSlider = this;\n window._SnapSliders[this.id] = this;\n }\n\n /**\n * Initialize this slider.\n *\n * @param {String|Element|Array|Object} containerOrOptions\n * @param {Object} options\n * @param {String|Element|Array} options.container\n * @param {String} options.id\n * @param {String|Element|Array} options.slides\n * @param {String|Number} options.start\n * @param {String|Element|Array} options.nav\n * @param {String|Element|Array} options.buttons\n * @param {String|Element|Array} options.prev\n * @param {String|Element|Array} options.next\n * @return {void}\n */\n init(containerOrOptions, options = {}) {\n // Allow people to quickly spin up sliders by just passing a container\n // element, or by passing in a single options object.\n if (isObject(containerOrOptions)) {\n options = containerOrOptions;\n }\n\n // Fill default options.\n this.options = {\n container: containerOrOptions,\n id: '',\n slides: '',\n nav: '',\n buttons: '',\n prev: '',\n next: '',\n start: 0,\n loop: null,\n on: {},\n ...options,\n };\n\n // Get single element from params.\n const container = getElements(this.options.container).shift();\n\n // Don't construct sliders with empty containers.\n if (!container) {\n if (process.env.NODE_ENV !== 'production') {\n console.log(`\n\uD83D\uDEAB Whoops! Snap Slider can't find a container element matching \"${this.options.container}\".\\n\n\uD83D\uDD0D Please check your selectors for typos. Make sure the element actually exists\n in the DOM when Snap Slider tries to initialize it.\\n\n\uD83D\uDC47 Here's a copy of the options you tried to initialize with for debugging:\\n\\n`, this.options, '\\n\\n');\n }\n return;\n }\n\n // Great! Now let's start initializing everything.\n this.container = container;\n\n // Get selectors from JavaScript or data attributes.\n this.options.buttons = options.buttons || this.container.getAttribute('data-snap-slider-buttons');\n this.options.prev = options.prev || this.container.getAttribute('data-snap-slider-prev');\n this.options.next = options.next || this.container.getAttribute('data-snap-slider-next');\n\n // Get and set persistent options in data attributes.\n this.id = this.getMaybeSetID(container, this.options.id);\n this.slides = this.getMaybeSetSlides(container, this.options.slides);\n this.align = this.getMaybeSetAlign(container, this.options.align);\n this.current = this.getMaybeSetStart(container, this.options.start);\n this.loop = this.getMaybeSetLoop(container, this.options.loop);\n\n // Reset internal variables.\n this.transition = null;\n this.scrolling = false;\n\n // Add custom callbacks.\n // eslint-disable-next-line no-restricted-syntax\n for (const eventName in this.options.on) {\n if (hasOwnProperty(this.options.on, eventName)) {\n this.on(eventName, this.options.on[eventName]);\n }\n }\n\n // Setup navigation.\n // NOTE: If left blank, `addNav()` will handle the fallbacks for button selectors.\n const navOptions = pick(this.options, [\n 'buttons',\n 'prev',\n 'next',\n ]);\n\n // Init custom goto buttons in the container.\n // NOTE: \"Goto\" buttons are automatically handled by delegated click\n // events on the `body`. For more details, see `handleGotoClick()`.\n this.addGotoButtons({ ...navOptions, container });\n\n // Init standard navs with data attributes.\n this.addNav(`[data-snap-slider-nav=\"${this.id}\"]`, navOptions);\n\n // Then init custom navs too.\n if (this.options.nav) {\n this.addNav(this.options.nav, navOptions);\n }\n\n // Go to the slide we want to start on.\n this.update();\n }\n\n /**\n * Get and maybe set a slider's ID on the closest container element.\n *\n * If no ID was specified, generates a fallback ID.\n *\n * @param {Element} container\n * @param {String} id\n * @return {String}\n */\n getMaybeSetID(container, id) {\n // Either use the ID we were given or the ID already on the container.\n id = id\n || container.getAttribute('data-snap-slider')\n || container.id;\n\n // If we don't have an ID, make one up and increment our internal\n // counter for the next slider.\n if (!id) {\n id = `slider-${counter}`;\n counter += 1;\n }\n\n // Store value in data attribute.\n container.setAttribute('data-snap-slider', id);\n\n // Return the final ID.\n return id;\n }\n\n /**\n * Get all slide elements for a given container.\n *\n * Defaults to container's children.\n *\n * @param {Element} container\n * @param {String} selector\n * @return {Array}\n */\n getMaybeSetSlides(container, selector) {\n // Get selector from JavaScript or data attribute.\n selector = selector && typeof selector === 'string'\n ? selector\n : container.getAttribute('data-snap-slider-slides');\n\n // Store value in data attribute.\n container.setAttribute('data-snap-slider-slides', selector || '');\n\n // If selector exists, use those elements. Otherwise,\n // assume the container's immediate children are slides.\n const slides = selector\n ? getElements(selector, container)\n : toArray(container.children);\n\n // Ensure all slides are focusable but not tabbable.\n slides.forEach((slide) => slide.setAttribute('tabindex', '-1'));\n\n // Return array of slides.\n return slides;\n }\n\n /**\n * Get alignment fallback for a given container.\n *\n * @param {Element} container\n * @param {String} align\n * @return {String}\n */\n getMaybeSetAlign(container, align) {\n // Get align index from JavaScript, data attribute, or leave blank.\n align = align || container.getAttribute('data-snap-slider-align') || '';\n\n // Store value in data attribute.\n container.setAttribute('data-snap-slider-align', align);\n\n return align;\n }\n\n /**\n * Get start index for a given container.\n *\n * Defaults to 1.\n *\n * @param {Element} container\n * @param {String} start\n * @return {String|Number}\n */\n getMaybeSetStart(container, start) {\n // Get start index from JavaScript, data attribute, or default to 1.\n if (!SnapSlider.isValidIndex(start)) {\n start = container.getAttribute('data-snap-slider-start') || 1;\n }\n\n // Store value in data attribute.\n container.setAttribute('data-snap-slider-start', start);\n\n return start;\n }\n\n /**\n * Get and maybe set a slider's `loop` option on the closest container element.\n *\n * @param {Element} container\n * @param {Boolean} loop\n * @return {String}\n */\n getMaybeSetLoop(container, loop) {\n // If we were given a Boolean value to set, use that.\n // Else check for an existing data attribute.\n // Defaults to `false`.\n loop = typeof loop === 'boolean'\n ? loop\n : container.getAttribute('data-snap-slider-loop') === 'true';\n\n // Store value in data attribute.\n container.setAttribute('data-snap-slider-loop', loop);\n\n // Return the final loop value.\n return loop;\n }\n\n /**\n * Get the `scroll-snap-align` for a snap slider element.\n *\n * Falls back to `data-snap-slider-align` when no CSS\n * is detected, otherwise defaults to `start`.\n *\n * @param {Element} el\n * @return {String}\n */\n getSnapAlign(el) {\n // Get element's CSS align value.\n const style = getStyle(el, 'scrollSnapAlign');\n\n // If browser supports Scroll Snap and slide\n // has a non-empty value, return it.\n if (style && style.indexOf('none') < 0) {\n return style;\n }\n\n // Otherwise, fallback to the slider's align attribute.\n // Else assume \"start\" for everything.\n return getClosestAttribute(el, 'data-snap-slider-align')\n || 'start';\n }\n\n /**\n * Get a specific slide element. Accepts any valid goto alias.\n *\n * @param {Number} index Starts at 1.\n * @return {Element}\n */\n getSlide(index) {\n // Convert index aliases to numbers.\n index = this.getIndexNumber(index);\n\n // Return the slide for that numeric index.\n // NOTE: Subtract 1 because the array is 0-index, but our API is 1-index.\n return this.slides[index - 1];\n }\n\n /**\n * Get the current slide element.\n *\n * @return {Element}\n */\n getCurrentSlide() {\n // NOTE: Subtract 1 because the array is 0-index, but our API is 1-index.\n return this.slides[this.current - 1];\n }\n\n /**\n * Is this a valid index?\n *\n * - first\n * - middle\n * - last\n * - prev\n * - next\n *\n * @param {String|Number} index\n * @return {Number}\n */\n static isValidIndex(index) {\n const aliases = [\n 'first',\n 'middle',\n 'last',\n 'prev',\n 'next',\n ];\n\n // Valid indexes are either a known alias,\n // or a positive integer.\n return aliases.indexOf(index) >= 0\n || parseInt(index, 10) >= 1;\n }\n\n /**\n * Get the slide number for any index.\n *\n * Returns -1 if index is invalid.\n *\n * @param {String|Number} index\n * @return {Number}\n */\n getIndexNumber(index) {\n let num;\n\n if (index === 'first') {\n // Get the first slide.\n num = 1;\n } else if (index === 'middle') {\n // Get the middle slide.\n num = Math.ceil(this.slides.length / 2);\n } else if (index === 'last') {\n // Get the last slide.\n num = this.slides.length;\n } else if (index === 'prev') {\n // Get the previous slide.\n num = this.current - 1;\n } else if (index === 'next') {\n // Get the next slide.\n num = this.current + 1;\n } else {\n // Try to get a number.\n num = parseInt(index, 10) || -1;\n }\n\n if (this.loop) {\n // If we're looping, send out-of-bounds requests\n // to the other end of the slider.\n if (num < 1) {\n num = this.slides.length;\n }\n\n if (num > this.slides.length) {\n num = 1;\n }\n } else if (num < 1 || num > this.slides.length) {\n // Otherwise, ignore out-of-range indexes.\n num = -1;\n }\n\n // Return numeric index. Or, if something goes wrong,\n // fallback to the first slide.\n return num || 1;\n }\n\n /**\n * Get the offset we should scroll to for a specific slide.\n *\n * @param {Element} slide\n * @return {Object} { top, left }\n */\n getScrollOffset(slide) {\n const { container } = this;\n const align = this.getSnapAlign(slide);\n\n // Calculate the 'start' position by default.\n // NOTE: This forces slides with align `none` to still snap into place.\n let top = slide.offsetTop;\n let left = slide.offsetLeft;\n\n // NOTE: Because Safari uses the 2-value syntax, we simply check for matching\n // keywords. If this causes incorrect behavior, use the `data-snap-slider-align`\n // attribute to override our automatic CSS detection.\n if (align.indexOf('center') >= 0) {\n // To center a slide, start with its beginning offset (the 'start' position).\n // Then add half the slide's size minus half the container size.\n top = slide.offsetTop + slide.offsetHeight / 2 - container.offsetHeight / 2;\n left = slide.offsetLeft + slide.offsetWidth / 2 - container.offsetWidth / 2;\n } else if (align.indexOf('end') >= 0) {\n // To align the end of a slide, start with its beginning offset (the 'start' position).\n // Then subtract the size of the container, but add back the size of the slide.\n top = slide.offsetTop - container.offsetHeight + slide.offsetHeight;\n left = slide.offsetLeft - container.offsetWidth + slide.offsetWidth;\n }\n\n // Keep offsets within the scrollable area.\n top = minmax(top, 0, container.scrollHeight);\n left = minmax(left, 0, container.scrollWidth);\n\n return { top, left };\n }\n\n /**\n * Go to a slide.\n *\n * @param {String|Number} index Starts at 1.\n * @param {Object} options\n * @param {Boolean} options.focus\n * @param {Boolean} options.force\n * @param {Boolean} options.ignoreCallbacks\n * @param {Boolean} options.immediate\n * @param {Event} event\n * @return {Boolean}\n */\n goto(index, options = {}, event) {\n // Fill default options.\n options = {\n // By default, focus the slide we're going to.\n focus: true,\n // Force-update the scroll position, even if we're already on the current slide.\n force: false,\n // Ignore custom callbacks for events.\n ignoreCallbacks: false,\n // Immediately update position without smooth scrolling.\n immediate: false,\n ...options,\n };\n\n // Get the next slide we should go to.\n const next = this.getIndexNumber(index);\n\n // If nothing changed, don't do anything (as long as\n // we're not trying to force it).\n if (!options.force && next === this.current) {\n return false;\n }\n\n // Get the next slide.\n const slide = this.getSlide(next);\n\n if (!slide) {\n return false;\n }\n\n // Scroll to it!\n const { top, left } = this.getScrollOffset(slide);\n\n if (options.immediate) {\n // Scroll immediately.\n this.container.scroll({ top, left });\n } else {\n // Let the event handlers know we're coming, then smooth scroll.\n this.startTransition(next);\n this.container.scroll({ top, left, behavior: 'smooth' });\n }\n\n // Update state.\n this.current = next;\n\n // We changed slides!\n this.fireEvent('change', event, options);\n\n return true;\n }\n\n /**\n * Build the `goto` attribute for a nav button.\n *\n * @param {Element|Boolean} nav\n * @param {String|Number} index\n * @return {String}\n */\n buildGoto(nav, index = '') {\n // Start with an empty string.\n let goto = '';\n\n // If this button isn't part of a nav, include the slider ID.\n if (!nav) {\n goto += `${this.id}:`;\n }\n\n // Add the index and return.\n return goto + index;\n }\n\n /**\n * Set the `goto` attribute for nav buttons.\n *\n * @param {String|Element|Array} buttons\n * @param {String} index\n * @return {void}\n */\n setGoto(buttons, index) {\n buttons = getElements(buttons);\n\n // If we found custom prev/next buttons, set their `goto` attributes\n // before we loop through the rest of the buttons.\n buttons.forEach((button) => {\n button.setAttribute('data-snap-slider-goto', this.buildGoto(\n // Don't assume this button is grouped with the others. It may\n // be somewhere else on the page, so double check for a parent\n // slider or nav container.\n button.closest('[data-snap-slider], [data-snap-slider-nav]'),\n index,\n ));\n });\n }\n\n /**\n * Get the slider ID and slide index a goto button is targeting.\n *\n * NOTE: This method is static so we can call it in the delegated body\n * click events. For more details, see `handleGotoClick()`.\n *\n * @param {String|Element|Array} button\n * @return {Object} { sliderID, index }\n */\n static getButtonTarget(button) {\n // Where are we going?\n const goto = button ? button.getAttribute('data-snap-slider-goto') : '';\n\n // Ignore missing buttons and attributes.\n if (!goto) {\n return {};\n }\n\n // Parse slide index and slider ID from `goto` attribute.\n const args = goto.split(':').map((str) => str.trim());\n const index = args.pop();\n let sliderID = args.pop();\n\n // If the slider ID wasn't included, check for a parent nav or container element.\n if (!sliderID) {\n const nav = button.closest('[data-snap-slider-nav]');\n const container = button.closest('[data-snap-slider]');\n\n // If it is in a nav or container, get the slider ID from there.\n if (nav) {\n sliderID = nav.getAttribute('data-snap-slider-nav');\n }\n\n if (container) {\n sliderID = container.getAttribute('data-snap-slider');\n }\n }\n\n // If there's still no slider ID, is this button already in a slider?\n if (!sliderID) {\n const slider = button.closest('data-snap-slider');\n\n // If it is in a slider, get the slider ID from there.\n if (slider) {\n sliderID = slider.getAttribute('data-snap-slider');\n }\n }\n\n return { sliderID, index };\n }\n\n /**\n * Handle click events for nav (aka \"goto\") buttons.\n *\n * By delegating events to the body, we can automatically\n * handle dynamic goto buttons (i.e., without having to\n * reinitialize slider events).\n *\n * @param {Event} event\n * @return {void}\n */\n static handleGoto(event) {\n // Get the button we clicked.\n const button = event.target.closest('[data-snap-slider-goto]');\n\n // Get the slider we're trying to update.\n const { sliderID, index } = SnapSlider.getButtonTarget(button);\n const slider = window._SnapSliders[sliderID];\n\n // Make sure it actually exists.\n if (!slider) {\n return;\n }\n\n // Go! But only focus the slide if we're NOT clicking a prev/next button.\n slider.goto(index, null, event);\n }\n\n /**\n * Start transitioning to another slide.\n *\n * This way when you click a nav button, the current slide updates\n * immediately but the scroll listener doesn't override it, or fire\n * extra change events.\n *\n * @param {Number} next\n * @return {void}\n */\n startTransition(next) {\n // Tell the scroll listener which slide we're transitioning to.\n this.transition = {\n from: this.current,\n to: next,\n diff: Math.abs(next - this.current),\n };\n\n // In case someone's fast enough to start scrolling again before our\n // scroll listener resolves the `transition` flag, or if the slide's\n // already visible and nothing actually has to scroll,\n // set a timeout to resolve the transition.\n const stuck = this.transition.to;\n\n // If there's already a check waiting, clear it to avoid accidentally\n // reverting to the wrong slide.\n if (this.checkTransition) {\n clearTimeout(this.checkTransition);\n }\n\n // Now make sure we don't get stuck!\n this.checkTransition = setTimeout(() => {\n if (this.transition.to === stuck) {\n this.stopTransition();\n }\n }, 1000);\n }\n\n /**\n * Stop the transitions! Set things back to normal.\n *\n * @return {void}\n */\n stopTransition() {\n // Clear transition checks.\n this.transition = null;\n clearTimeout(this.checkTransition);\n }\n\n /**\n * Is this a \"previous\" button?\n *\n * @param {String|Element|Array} button\n * @return {Boolean}\n */\n isPrevButton(button) {\n button = getElements(button).shift();\n\n // Ignore missing elements.\n if (!button) {\n return false;\n }\n\n // Check whether the `goto` attribute is \"prev\".\n // If not, check the text & class for common \"prev\" terms.\n return (button.getAttribute('data-snap-slider-goto') || '').match(/\\bprev$/)\n || button.textContent.toLowerCase().match(this.terms.prev)\n || button.className.toLowerCase().match(this.terms.prev);\n }\n\n /**\n * Is this a \"next\" button?\n *\n * @param {String|Element|Array} button\n * @return {Boolean}\n */\n isNextButton(button) {\n button = getElements(button).shift();\n\n // Ignore missing elements.\n if (!button) {\n return false;\n }\n\n // Check whether the `goto` attribute is \"next\".\n // If not, check the text & class for common \"next\" terms.\n return (button.getAttribute('data-snap-slider-goto') || '').match(/\\bnext$/)\n || button.textContent.toLowerCase().match(this.terms.next)\n || button.className.toLowerCase().match(this.terms.next);\n }\n\n /**\n * Is this index a relative term? I.e., is it `prev` or `next`?\n *\n * @param {String|Number} index\n * @return {Boolean}\n */\n static isRelative(index) {\n return index === 'prev' || index === 'next';\n }\n\n /**\n * Does an index match the current slide?\n *\n * @param {String|Number} index\n * @return {Boolean}\n */\n isCurrent(index) {\n // Ignore relative indexes (i.e., `prev` and `next`) since they\n // always refer to one more or less than the current index.\n if (SnapSlider.isRelative(index)) {\n return false;\n }\n\n // Does this numeric index match the current slide?\n return this.getIndexNumber(index) === this.current;\n }\n\n /**\n * Add goto buttons for the current slider.\n *\n * @param {String|Element|Array|Object} buttonsOrOptions\n * @param {Object} options\n * @param {String|Element|Array} options.container\n * @param {String|Element|Array} options.buttons\n * @param {String|Element|Array} options.prev\n * @param {String|Element|Array} options.next\n * @return {Boolean}\n */\n addGotoButtons(buttonsOrOptions, options = {}) {\n // Allow people to quickly add nav buttons by just passing the\n // selector, or by passing in a single options object.\n if (isObject(buttonsOrOptions)) {\n options = buttonsOrOptions;\n }\n\n // Fill default options.\n options = {\n container: '',\n buttons: buttonsOrOptions,\n prev: '',\n next: '',\n ...options,\n };\n\n // Get button elements.\n // NOTE: If someone passes an overly-generic selector (e.g., `button`)\n // this will query the entire document. In general, you should either\n // specify a container element, use specific selectors, or pass\n // the elements directly.\n const buttons = getElements(options.buttons, options.container);\n const prev = getElements(options.prev, options.container);\n const next = getElements(options.next, options.container);\n\n // If we found custom prev/next buttons, set their `goto` attributes\n // before we loop through the rest of the buttons.\n prev.forEach((b) => b.hasAttribute('data-snap-slider-goto') || this.setGoto(prev, 'prev'));\n next.forEach((b) => b.hasAttribute('data-snap-slider-goto') || this.setGoto(next, 'next'));\n\n // Keep track of the index outside of the loop so we can\n // skip prev/next buttons but still go in order.\n let nextIndex = 1;\n\n // Loop through the buttons and set each one's `goto` attribute.\n buttons.forEach((button) => {\n // Ignore buttons that already have a `goto` attribute.\n if (button.hasAttribute('data-snap-slider-goto')) {\n return null;\n }\n\n // Previous\n if (this.isPrevButton(button)) {\n return this.setGoto(button, 'prev');\n }\n\n // Next\n if (this.isNextButton(button)) {\n return this.setGoto(button, 'next');\n }\n\n // Numeric: Check the text for a number, else fallback to the next index.\n const index = parseInt(button.textContent.replace(/.*\\b(\\d+)\\b.*/, '$1'), 10) || nextIndex;\n\n // Increment the next index.\n nextIndex = index + 1;\n\n return this.setGoto(button, index);\n });\n\n this.updateButtons();\n\n return true;\n }\n\n /**\n * Get navs for the current slider.\n *\n * @return {Array}\n */\n getNavs() {\n // eslint-disable-next-line arrow-body-style\n return qsa('[data-snap-slider-nav]').filter((nav) => {\n // Only return navs targeting the current slider.\n return nav.getAttribute('data-snap-slider-nav') === this.id;\n });\n }\n\n /**\n * Get nav buttons for the current slider.\n *\n * @return {Array}\n */\n getButtons() {\n return qsa('[data-snap-slider-goto]').filter((button) => {\n const { sliderID } = SnapSlider.getButtonTarget(button);\n // Only return buttons targeting the current slider.\n return sliderID === this.id;\n });\n }\n\n /**\n * Update nav buttons for the current slider.\n *\n * @return {void}\n */\n updateButtons() {\n // Wait until the slider has initialized.\n if (!this.current) {\n return;\n }\n\n // Loop through all the nav buttons.\n this.getButtons().forEach((button) => {\n // Figure out which slide it's for...\n const { index } = SnapSlider.getButtonTarget(button);\n\n // And update its class.\n if (this.isCurrent(index)) {\n button.classList.add('is-current');\n } else {\n button.classList.remove('is-current');\n }\n\n // Also, enable/disable relative buttons unless `loop` is on.\n if (!this.loop && SnapSlider.isRelative(index)) {\n // Disable prev button on first slide.\n // Disable next button on last slide.\n const disabled = (index === 'prev' && this.current === 1)\n || (index === 'next' && this.current === this.slides.length);\n\n if (disabled) {\n // button.setAttribute('disabled', '');\n button.classList.add('is-disabled');\n } else {\n // button.removeAttribute('disabled', '');\n button.classList.remove('is-disabled');\n }\n }\n });\n }\n\n /**\n * Update slide active states when the slider changes.\n *\n * @return {void}\n */\n updateSlides() {\n this.slides.forEach((slide, index) => {\n // NOTE: Subtract 1 because the array is 0-index, but our API is 1-index.\n if (index === this.current - 1) {\n slide.classList.add('is-current');\n slide.removeAttribute('aria-hidden');\n\n // Enable tabbing for current slide\n qsa('[data-snap-slider-tabindex]', slide).forEach((tab) => {\n tab.removeAttribute('tabindex');\n });\n } else {\n slide.classList.remove('is-current');\n slide.setAttribute('aria-hidden', 'true');\n\n // Disable tabbing for non-current slides\n tabbable(slide).forEach((tab) => {\n tab.setAttribute('tabindex', '-1');\n tab.setAttribute('data-snap-slider-tabindex', '');\n });\n }\n });\n }\n\n /**\n * Add a nav element for the current slider. Automatically hooks up any nav\n * buttons inside the nav.\n *\n * @param {String|Element|Array|Object} containerOrOptions\n * @param {Object} options\n * @param {String|Element|Array} options.container\n * @param {String|Element|Array} options.buttons\n * @param {String|Element|Array} options.prev\n * @param {String|Element|Array} options.next\n * @return {Boolean}\n */\n addNav(containerOrOptions, options = {}) {\n // Allow people to quickly add a nav by just passing a container\n // element, or by passing in a single options object.\n if (isObject(containerOrOptions)) {\n options = containerOrOptions;\n }\n\n // Fill default options.\n options = {\n container: containerOrOptions,\n buttons: '',\n prev: '',\n next: '',\n ...options,\n };\n\n // Get matching nav containers.\n const navContainers = getElements(options.container);\n\n // Don't add navs without container elements.\n if (!navContainers.length) {\n return false;\n }\n\n navContainers.forEach((navContainer) => {\n // Set a data attribute assigning the nav to this slider.\n navContainer.setAttribute('data-snap-slider-nav', this.id);\n\n // Get button selectors from JavaScript, data attribute, or default to 'button'.\n // NOTE: In this case, allow the nav's data attribute to override the parent\n // container's options.\n const buttons = navContainer.getAttribute('data-snap-slider-buttons')\n || options.buttons\n || 'button';\n const prev = options.prev || navContainer.getAttribute('data-snap-slider-prev');\n const next = options.next || navContainer.getAttribute('data-snap-slider-next');\n\n // And add them.\n this.addGotoButtons({\n container: navContainer,\n buttons,\n prev,\n next,\n });\n });\n\n return true;\n }\n\n /**\n * Which slide is closest to its active offset position?\n *\n * Returns an object include the slide's index, element,\n * and the diff between its active offset and our\n * current scroll position.\n *\n * @return {Object} { index, slide, diff }\n */\n getClosest() {\n return this.slides.reduce((prev, slide, index) => {\n // 1-index to stay consistent with our API.\n index += 1;\n\n // How far away are we from the next slide's active offset position?\n const offset = this.getScrollOffset(slide);\n const diff = {\n top: Math.abs(this.container.scrollTop - offset.top),\n left: Math.abs(this.container.scrollLeft - offset.left),\n };\n\n // Save the next slide's info to compare with other slides.\n const next = { index, slide, diff };\n\n // If this is the first slide, return it and compare the next one.\n if (!prev) {\n return next;\n }\n\n // Compare each slide to see which one is the closest to its active offset position.\n // As soon as the next slide is at least as close as the previous one, return it.\n if (next.diff.left <= prev.diff.left && next.diff.top <= prev.diff.top) {\n return next;\n }\n\n // Otherwise, keep the last closest slide.\n return prev;\n // Init with `false` so the first slide gets processed just like the rest of them.\n }, false);\n }\n\n /**\n * Watch the container scroll for when the current slide changes.\n *\n * @return {void}\n */\n watchForChanges() {\n // Scroll listener. Save so we can remove it during `destroy()`.\n this.scrollListener = throttle((event) => {\n // Which slide is closest to their active offset position?\n const closest = this.getClosest();\n\n // If someone's passively scrolling (i.e., not in a transition),\n // then as soon as we've scrolled to another slide, mark that\n // slide as the new current one and fire a change event.\n if (!this.transition && closest.index !== this.current) {\n this.current = closest.index;\n this.fireEvent('change', event);\n }\n\n // If we just started scrolling, update state and\n // fire a `scroll.start` event.\n if (!this.scrolling) {\n this.scrolling = true;\n this.fireEvent('scroll.start', event);\n }\n\n // Fire a generic `scroll` event.\n this.fireEvent('scroll', event);\n }, 250);\n\n // Scroll end listener. Save so we can remove it during `destroy()`.\n this.scrollEndListener = debounce((event) => {\n // We're done scrolling!\n this.scrolling = false;\n this.fireEvent('scroll.end', event);\n\n // Clear any previous transition checks.\n // NOTE: This has to happen *after* we fire the `scroll.end` event,\n // otherwise `handleFocus` won't be able to access `this.transition`.\n this.stopTransition();\n }, 250);\n\n // Arrow key listener. Save so we can remove it during `destroy()`.\n this.arrowKeyListener = throttle((event) => {\n // Ignore events that have already been prevented.\n if (event.defaultPrevented) {\n return;\n }\n\n // Listen for arrow keys.\n // @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n const isPrev = ['Up', 'ArrowUp', 'Left', 'ArrowLeft'].indexOf(event.key) >= 0;\n const isNext = ['Down', 'ArrowDown', 'Right', 'ArrowRight'].indexOf(event.key) >= 0;\n\n // Ignore non-arrow keys.\n if (!isPrev && !isNext) {\n return;\n }\n\n // Go to the next or previous slide.\n this.goto(isNext ? 'next' : 'prev', null, event);\n\n // Prevent default browser scroll.\n event.preventDefault();\n }, 250);\n\n // Focus listener. Save so we can remove it during `destroy()`.\n this.focusListener = (event) => {\n // Only trigger `goto` on focus when we're not passively scrolling.\n // However, if someone manually triggered a transition then\n // allow them to click or tab away to a different slide.\n if (this.scrolling && !this.transition) {\n return;\n }\n\n // Get slide + index.\n let slide;\n let index;\n\n this.slides.forEach((s, i) => {\n if (s.contains(event.target)) {\n slide = s;\n index = i + 1;\n }\n }, null);\n\n // If there's a matching slide, go to it.\n if (slide) {\n this.goto(index, null, event);\n }\n };\n\n // Resize Observer. Save so we can disconnect it during `destroy()`.\n // Only init if browser supports it, else fallback to noop.\n this.resizeObserver = { observe: () => {}, disconnect: () => {} };\n if ('ResizeObserver' in window) {\n this.resizeObserver = new ResizeObserver(this.resizeCallback.bind(this));\n }\n\n // Add all our listeners.\n // Set timeout to avoid initial `goto` event triggering a scroll listener.\n setTimeout(() => {\n this.container.addEventListener('scroll', this.scrollListener, passive);\n this.container.addEventListener('scroll', this.scrollEndListener, passive);\n this.container.addEventListener('keydown', this.arrowKeyListener);\n this.container.addEventListener('focusin', this.focusListener);\n this.resizeObserver.observe(this.container);\n\n // Done loading!\n this.fireEvent('load');\n }, 100);\n }\n\n /**\n * Update the slider on load.\n *\n * @return {void}\n */\n hasLoaded() {\n this.container.classList.add('has-loaded');\n }\n\n /**\n * Update this slider (e.g., on resize). Basically just repositions the\n * current slide.\n *\n * @return {void}\n */\n update() {\n // Make sure we're still on the current slide.\n this.goto(this.current, {\n focus: false,\n force: true,\n ignoreCallbacks: true,\n immediate: true,\n });\n }\n\n /**\n * Destroy this slider. Stop any active transitions, remove its event\n * listeners, and delete it from our internal array of slider instances.\n *\n * @return {void}\n */\n destroy() {\n // Stop running transitions, event listeners, etc.\n this.stopTransition();\n this.container.removeEventListener('scroll', this.scrollListener);\n this.container.removeEventListener('scroll', this.scrollEndListener);\n this.container.removeEventListener('keydown', this.arrowKeyListener);\n this.resizeObserver.disconnect();\n\n // Reset callbacks.\n // eslint-disable-next-line no-restricted-syntax\n for (const eventName in this.callbacks) {\n if (hasOwnProperty(this.callbacks, eventName)) {\n this.callbacks[eventName] = [];\n }\n }\n\n // Remove references to this slider.\n delete this.container.SnapSlider;\n delete window._SnapSliders[this.id];\n }\n\n /**\n * Reset this slider (e.g., after adding or removing a slide).\n *\n * See `init()` for a full breakdown of `options`.\n *\n * @param {Object} options\n * @return {void}\n */\n reset(options = {}) {\n // Copy initial options.\n const initialOptions = this.options;\n\n // Remove initial callbacks to avoid duplicating them.\n delete initialOptions.on;\n\n // Don't let people reset critical options during reset (e.g., slider ID).\n delete options.container;\n delete options.id;\n\n // Re-initialize this slider with initial options + overrides.\n this.init(this.container, { ...initialOptions, ...options });\n }\n\n /**\n * Handle resize observer events.\n *\n * @return {void}\n */\n resizeCallback() {\n this.update();\n }\n\n /**\n * When an event happens, fire all the callback functions for that event.\n *\n * @param {String} eventName\n * @param {Event} event\n * @param {Object} options\n * @param {Boolean} options.focus\n * @param {Boolean} options.ignoreCallbacks\n * @return {void}\n */\n fireEvent(eventName, event, options = {}) {\n // Ignore invalid events.\n if (!hasOwnProperty(this.callbacks, eventName)) {\n return;\n }\n\n // Fill default options.\n options = {\n // By default, focus the slide we're going to.\n focus: true,\n // Ignore custom callbacks for events.\n ignoreCallbacks: false,\n ...options,\n };\n\n // Required: Update slider attributes on load.\n if (eventName === 'load') {\n this.hasLoaded();\n }\n\n // Required: Update buttons and slides on every change.\n if (eventName === 'change') {\n this.updateButtons();\n this.updateSlides();\n }\n\n // Allow focus events to be ignored.\n if (options.focus) {\n this.handleFocus(eventName, event);\n }\n\n // Allow callbacks to be ignored.\n if (options.ignoreCallbacks) {\n return;\n }\n\n // Fallback object for `null` events.\n event = event || {};\n\n // Include more granular event types for easier callbacks.\n const events = [eventName];\n\n if (hasOwnProperty(this.callbacks, `${eventName}.${event.type}`)) {\n events.push(`${eventName}.${event.type}`);\n }\n\n // Fire all the callbacks for each event.\n events.forEach((name) => {\n this.callbacks[name].forEach((callback) => {\n if (typeof callback === 'function') {\n callback(this, event);\n }\n });\n });\n }\n\n /**\n * Handle focus events differently depending on whether we're manually\n * triggering changes or passively scrolling.\n *\n * @param {String} eventName\n * @param {Event} event\n * @return {void}\n */\n handleFocus(eventName, event) {\n // Only handle focus for manually triggered changes (e.g., clicks and key presses).\n // Ignore passive scrolling to avoid mistakenly hijacking someone's focus.\n if (!this.transition) {\n return;\n }\n\n // Only focus the slide if we're NOT clicking a prev/next button.\n if (event && eventName === 'change') {\n // Did we click a button?\n const button = event.target.closest('[data-snap-slider-goto]');\n const { index } = SnapSlider.getButtonTarget(button);\n\n // If we clicked a relative button, get out.\n if (SnapSlider.isRelative(index)) {\n return;\n }\n }\n\n // If we're only transitioning one slide over, focus immediately on change.\n if (this.transition.diff <= 1 && eventName === 'change') {\n this.getCurrentSlide().focus({ preventScroll: true });\n }\n\n // If we're transitioning across multiple slides, wait until the scroll ends to focus.\n // Otherwise, we'll cause the scroll to flicker.\n if (this.transition.diff > 1 && eventName === 'scroll.end') {\n // Only focus the slide if we haven't already focused on another\n // element during the transition.\n if (!document.activeElement\n || document.activeElement === document.body\n || document.activeElement.closest('[data-snap-slider-goto]')) {\n this.getCurrentSlide().focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * Add callbacks to fire on specific events.\n *\n * @param {String} event Event name.\n * @param {Function} callback Function w/ slider and event params (e.g., `fn(slider, event)`).\n * @return {void}\n */\n on(event, callback) {\n // Ignore invalid events.\n if (!hasOwnProperty(this.callbacks, event)) {\n if (process.env.NODE_ENV !== 'production') {\n console.log(`\n\uD83D\uDEAB Whoops! Snap Slider can't add events for \"${event}\".\\n\n\uD83D\uDCDD Please make sure your event matches one of the ones in this list:\\n\\n`, Object.keys(this.callbacks), '\\n\\n');\n }\n return;\n }\n\n // Ignore invalid callbacks.\n if (typeof callback !== 'function') {\n if (process.env.NODE_ENV !== 'production') {\n console.log(`\n \uD83D\uDEAB Whoops! Snap Slider can only add functions as callbacks.\\n\n \uD83D\uDC40 It looks like you passed a \"${typeof callback}\" instead.\\n\\n`, callback, '\\n\\n');\n }\n return;\n }\n\n // Add the callback for our event.\n this.callbacks[event].push(callback);\n }\n\n /**\n * Log that we couldn't find the element you're looking for.\n *\n * @param {mixed} element\n * @return {void}\n */\n static notFound(element) {\n if (process.env.NODE_ENV !== 'production') {\n console.log(`\n\uD83D\uDE22 Oh no! Snap Slider couldn't find a slider for \"${element}\".\\n\n-------------------------------------------------------------------------------------------\n\u2139\uFE0F NOTE: Make sure the elements you're trying to debug have a \\`data-snap-slider\\` attribute.\n-------------------------------------------------------------------------------------------\\n\\n`);\n }\n }\n\n /**\n * Get the `SnapSlider` object for a slider based on its ID.\n *\n * @param {String} id\n * @return {SnapSlider}\n */\n static get(id) {\n return window._SnapSliders[id];\n }\n\n /**\n * `console.log` info about a slider, its nav, or goto buttons.\n *\n * @param {String|Element|Array} idOrElements\n * @return {void}\n */\n static debug(idOrElements) {\n if (process.env.NODE_ENV !== 'production') {\n /* eslint-disable no-irregular-whitespace */\n let elements = [];\n\n // 1. Debug all sliders by default.\n if (arguments.length === 0) {\n idOrElements = '[data-snap-slider]';\n }\n\n // 2. Debug a slider by its ID.\n if (typeof idOrElements === 'string'\n && hasOwnProperty(window._SnapSliders, idOrElements)) {\n idOrElements = `[data-snap-slider=\"${idOrElements}\"]`;\n }\n\n // 3. Debug slider elements.\n elements = getElements(idOrElements);\n\n if (!elements.length) {\n SnapSlider.notFound(idOrElements);\n return;\n }\n\n // Debug all the things!\n elements.forEach((el, i) => {\n // What are we debugging? Is this a button, nav, or container?\n const button = el.closest('[data-snap-slider-goto]');\n const nav = el.closest('[data-snap-slider-nav]');\n const container = el.closest('[data-snap-slider]');\n\n // If we're debugging more than one element at a time,\n // add the index # to each section heading.\n const num = elements.length > 1 ? `#${i + 1} ` : '';\n\n // 2a. Buttons\n if (button) {\n // Get details for the target slider & slide.\n const target = SnapSlider.getButtonTarget(button);\n const slider = window._SnapSliders[target.sliderID];\n\n // Make sure slide index is valid.\n const { index } = target;\n const slideIndex = SnapSlider.isValidIndex(index)\n ? `\"${index}\"`\n : `\uD83D\uDEAB \"${index}\" - Yikes! This index is invalid.\\n\\nUse a positive number instead, or one of the following aliases:\\n\n ${['first', 'middle', 'last', 'prev', 'next'].map((a) => `\u2022\u00A0${a}`).join('\\n')}`;\n\n // \"We couldn't find anything.\"\n const sliderID = target.sliderID\n ? `\"${target.sliderID}\"`\n : `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Make sure your button is inside a \\`data-snap-slider-nav\\` element, or...\n \u2022\u00A0Include the slider ID you want to target in your \\`data-snap-slider-goto\\` attribute.\n \u25E6 For example, \\`data-snap-slider-goto=\"example-slider:${target.index || 'prev'}\"\\`.`;\n\n let sliderContainer = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Double check that your slider ID is correct (\uD83D\uDC46).\n \u2022\u00A0Make sure your slider has the same ID in its \\`data-snap-slider\\` attribute.\n \u25E6 For example, \\`data-snap-slider=\"example-slider\"\\`.`;\n let slideIndexNumber = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Double check that your index is valid (\uD83D\uDC46).\n \u2022\u00A0Make sure a slide actually exists at that index (\uD83D\uDC47).`;\n let slide = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Double check that your index is valid (\uD83D\uDC46).\n \u2022\u00A0Make sure a slide actually exists at that index.\n \u2022\u00A0Make sure your slider recognizes the slide element as a slide.`;\n\n // We found it!\n if (slider && slider.container) {\n sliderContainer = slider.container;\n slideIndexNumber = slider.getIndexNumber(index);\n slide = slider.getSlide(index);\n }\n\n // Log 'em.\n return logger.section({\n heading: `\uD83D\uDD79 Button ${num}`,\n description: button,\n groups: [\n {\n heading: '1. What slider is this button targeting?',\n items: [\n { heading: 'Slider ID', description: sliderID },\n { heading: 'Slider Element', description: sliderContainer },\n { heading: 'Slider Object', description: slider || \"\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\" },\n ],\n },\n {\n heading: '2. Which slide will it go to?',\n items: [\n { heading: 'Slide Index', description: slideIndex },\n { heading: 'Slide Index (Number)', description: slideIndexNumber },\n { heading: 'Slide Element', description: slide },\n ],\n },\n ],\n collapsed: true,\n });\n }\n\n // 2b. Navigation\n if (nav) {\n // Get details for the target slider\n let sliderID = nav.getAttribute('data-snap-slider-nav');\n const slider = window._SnapSliders[sliderID];\n let buttons = qsa('[data-snap-slider-goto]', nav);\n\n // \"We couldn't find anything.\"\n sliderID = sliderID\n ? `\"${sliderID}\"`\n : `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Include the slider ID you want to target in your \\`data-snap-slider-nav\\` attribute.\n \u25E6 For example, \\`data-snap-slider-nav=\"example-slider\"\\`.`;\n\n let sliderContainer = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Make sure the ID in your container's \\`data-snap-slider\\` attribute and the ID in your nav's \\`data-snap-slider-nav\\` attribute both match.`;\n\n if (!buttons.length) {\n buttons = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Make sure your buttons have a \\`data-snap-slider-goto\\` attribute.`;\n }\n\n // We found it!\n if (slider && slider.container) {\n sliderContainer = slider.container;\n }\n\n // Log 'em.\n return logger.section({\n heading: `\uD83D\uDDFA Navigation ${num}`,\n description: nav,\n groups: [\n {\n heading: '1. What slider is this nav targeting?',\n items: [\n { heading: 'Slider ID', description: sliderID },\n { heading: 'Slider Element', description: sliderContainer },\n { heading: 'Slider Object', description: slider || \"\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\" },\n ],\n },\n {\n heading: '2. What buttons are in this nav?',\n items: [{ heading: 'Buttons', description: buttons }],\n },\n ],\n collapsed: true,\n });\n }\n\n // 2c. Containers (aka sliders)\n if (container) {\n // Get details for the slider.\n let sliderID = container.getAttribute('data-snap-slider');\n const slider = window._SnapSliders[sliderID];\n let navs = qsa(`[data-snap-slider-nav=\"${sliderID}\"]`);\n let buttons = slider ? slider.getButtons() : [];\n\n // \"We couldn't find anything.\"\n sliderID = sliderID\n ? `\"${sliderID}\"`\n : `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Include the slider ID you want in your \\`data-snap-slider\\` attribute.\n \u25E6 For example, \\`data-snap-slider=\"example-slider\"\\`.`;\n\n if (!navs.length) {\n navs = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Make sure the ID in your container's \\`data-snap-slider\\` attribute and the ID in your nav's \\`data-snap-slider-nav\\` attribute both match.`;\n }\n\n if (!buttons.length) {\n buttons = `\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\\n\n \u2022\u00A0Make sure your button is inside a \\`data-snap-slider-nav\\` element, or...\n \u2022\u00A0Include the slider ID you want to target in your \\`data-snap-slider-goto\\` attribute.\n \u25E6 For example, \\`data-snap-slider-goto=\"example-slider:prev\"\\`.`;\n }\n\n // Log 'em.\n return logger.section({\n heading: `\uD83E\uDD68 Slider ${num}`,\n description: container,\n groups: [\n {\n heading: '1. What slider is this?',\n items: [\n { heading: 'Slider ID', description: sliderID },\n { heading: 'Slider Object', description: slider || \"\uD83E\uDD37\u200D\u2640\uFE0F We couldn't find any.\" },\n ],\n },\n {\n heading: '2. What navs target this slider?',\n items: [{ heading: 'Navs', description: navs }],\n },\n {\n heading: '3. What buttons target this slider?',\n items: [{ heading: 'Buttons', description: buttons }],\n },\n ],\n collapsed: true,\n });\n }\n\n return SnapSlider.notFound(idOrElements);\n });\n /* eslint-enable no-irregular-whitespace */\n }\n }\n}\n\n// Keep track of all the sliders on the page to reference.\nwindow._SnapSliders = [];\n\n// Make the constructor globally accessible.\nwindow.SnapSlider = SnapSlider;\n\n// If jQuery exists, integrate.\nif (typeof $ !== 'undefined') {\n // eslint-disable-next-line no-undef, func-names\n $.fn.snapSlider = function (options) {\n return new SnapSlider(this, options);\n };\n}\n\n// Auto-init once the DOM is ready.\nonReady(() => {\n // Init polyfills.\n elementClosest(window);\n smoothscroll.polyfill();\n\n // Initialize all sliders with data attributes.\n qsa('[data-snap-slider]').forEach((el) => new SnapSlider(el));\n\n // Setup click events for *all* nav elements.\n on('body', 'click', '[data-snap-slider-goto]', SnapSlider.handleGoto);\n});\n\nexport default SnapSlider;\n", "import hasOwnProperty from './hasOwnProperty';\n\n/**\n * Get a computed style for some element.\n * @param {Element} el\n * @param {String} prop\n * @return {String}\n */\nexport default function getStyle(el, prop) {\n const style = window.getComputedStyle(el);\n\n // Ignore mmissing elements or props\n if (!style || !hasOwnProperty(style, prop)) {\n return '';\n }\n\n return style[prop];\n}\n", "/**\n * Get an attribute for the closest element with it.\n * @param {Element} el\n * @param {String} attr\n * @return {String}\n */\nexport default function getClosestAttribute(el, attr) {\n // Ignore missing elements\n if (!el) {\n return '';\n }\n\n // Find the closest element with a mattring attribute\n el = el.closest(`[${attr}]`);\n\n // If we found a match, return the attribute, otherwise\n // return an empty string.\n return el ? el.getAttribute(attr) : '';\n}\n", "/**\n * Wait to run a function on DOMContentLoaded, or fire immediately if the\n * event has already fired.\n * @param {Function} fn\n * @return {void}\n */\nexport default function onReady(fn) {\n if (document.readyState !== 'loading') {\n fn();\n } else {\n document.addEventListener('DOMContentLoaded', fn);\n }\n}\n", "/* eslint-disable consistent-return, func-names, no-var, prefer-arrow-callback, vars-on-top */\n\n/**\n * Plain JavaScript event delegation. Add a handler for whenever an element's\n * children trigger a specified event.\n * @see https://bdadam.com/blog/plain-javascript-event-delegation.html\n * @param {String} parentSelector\n * @param {String} eventName\n * @param {String} childSelector\n * @param {Function} fn\n * @return {Boolean}\n */\nexport default function on(parentSelector, eventName, childSelector, fn) {\n var parent = document.querySelector(parentSelector);\n\n if (!parent) {\n return false;\n }\n\n parent.addEventListener(eventName, function (event) {\n var possibleTargets = parent.querySelectorAll(childSelector);\n var target = event.target;\n\n for (var i = 0, l = possibleTargets.length; i < l; i += 1) {\n var el = target;\n var p = possibleTargets[i];\n\n while (el && el !== parent) {\n if (el === p) {\n return fn.call(p, event);\n }\n\n el = el.parentNode;\n }\n }\n });\n\n return true;\n}\n\n// Example:\n// on('body', 'click', '.product', function(e) {\n// console.log(e.target);\n// });\n", "/**\n * Return a string of dashes.\n *\n * @param {Number} num\n * @param {String} char\n * @return {String}\n */\nexport default function dashes(num, char = '-') {\n let str = '';\n\n for (let i = 0; i < num; i += 1) {\n str += char;\n }\n\n return str;\n}\n", "/* eslint-disable indent */\n\n// Helpers\nimport dashes from '../helpers/dashes';\n\n/**\n * Get the substitution string for to format this value in a console.log.\n *\n * - %o = Object\n * - %d = Number (aka \"Digit\")\n * - %s = String\n * - %c = CSS\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions\n * @param {mixed} value\n * @return {String}\n */\nexport function getSubstitution(value) {\n // Object\n if (typeof value === 'object') {\n return '%o';\n }\n\n // Number\n if (typeof value === 'number') {\n return '%d';\n }\n\n // When in doubt, assume everything else is a String.\n return '%s';\n}\n\n/**\n * Log a full debug section.\n *\n * @param {Object} params\n * @param {Object} params.heading\n * @return {void}\n */\nexport function section(params) {\n const {\n heading,\n description,\n groups,\n collapsed,\n } = params;\n\n // Allow groups to be collapsed.\n const method = collapsed ? 'groupCollapsed' : 'group';\n\n // HEADING\n console.log(`%c\n\u250F\u2501\u2501${dashes(heading.length, '\u2501')}\u2501\u2501\u2513\n\u2503 ${heading} \u2503\n\u2517\u2501\u2501${dashes(heading.length, '\u2501')}\u2501\u2501\u251B`, 'font-size: 1.25em;', '\\n\\n',\n description,\n '\\n\\n');\n\n // GROUPS\n groups.forEach((group) => {\n // Heading\n console[method](`%c${group.heading}`, 'font-weight: bold;');\n\n // Build up a single console.log for all the items in the group.\n const args = [];\n let content = '';\n\n group.items.forEach((item) => {\n // eslint-disable-next-line no-shadow\n const { heading, description } = item;\n\n // Heading\n content += `\\n%c${getSubstitution(heading)}\\n`;\n args.push('text-decoration: underline;', heading);\n\n // Description\n content += `\\n%c${getSubstitution(description)}\\n`;\n args.push('text-decoration: none;', description);\n });\n\n // Log the items!\n console.log(content, ...args);\n\n // And end the group.\n console.groupEnd();\n });\n\n // Add an empty line after the section.\n console.log('\\n');\n}\n", "\n/** Decodes an identifier back into a CSS selector */\nexport default function decodeCSS(value) {\n\tif (value.slice(0, 13) !== 'csstools-has-') {\n\t\treturn '';\n\t}\n\n\tvalue = value.slice(13);\n\tlet values = value.split('-');\n\n\tlet result = '';\n\tfor (let i = 0; i < values.length; i++) {\n\t\tresult += String.fromCharCode(parseInt(values[i], 36));\n\t}\n\n\treturn result;\n}\n", "import decodeCSS from './decode.mjs';\n\n/** Extract encoded selectors out of attribute selectors */\nexport default function extractEncodedSelectors(value) {\n\tlet out = [];\n\n\tlet depth = 0;\n\tlet candidate;\n\n\tlet quoted = false;\n\tlet quotedMark;\n\n\tlet containsUnescapedUnquotedHasAtDepth1 = false;\n\n\t// Stryker disable next-line EqualityOperator\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i];\n\n\t\tswitch (char) {\n\t\t\tcase '[':\n\t\t\t\tif (quoted) {\n\t\t\t\t\tcandidate += char;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (depth === 0) {\n\t\t\t\t\tcandidate = '';\n\t\t\t\t} else {\n\t\t\t\t\tcandidate += char;\n\t\t\t\t}\n\n\t\t\t\tdepth++;\n\t\t\t\tcontinue;\n\t\t\tcase ']':\n\t\t\t\tif (quoted) {\n\t\t\t\t\tcandidate += char;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\tdepth--;\n\t\t\t\t\tif (depth === 0) {\n\t\t\t\t\t\tconst decoded = decodeCSS(candidate);\n\t\t\t\t\t\tif (containsUnescapedUnquotedHasAtDepth1) {\n\t\t\t\t\t\t\tout.push(decoded);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcandidate += char;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\tcase '\\\\':\n\t\t\t\tcandidate += value[i];\n\t\t\t\tcandidate += value[i+1];\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\n\t\t\tcase '\"':\n\t\t\tcase '\\'':\n\t\t\t\tif (quoted && char === quotedMark) {\n\t\t\t\t\tquoted = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t} else if (quoted) {\n\t\t\t\t\tcandidate += char;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tquoted = true;\n\t\t\t\tquotedMark = char;\n\t\t\t\tcontinue;\n\n\t\t\tdefault:\n\t\t\t\tif (candidate === '' && depth === 1 && (value.slice(i, i + 13) === 'csstools-has-')) {\n\t\t\t\t\tcontainsUnescapedUnquotedHasAtDepth1 = true;\n\t\t\t\t}\n\n\t\t\t\tcandidate += char;\n\t\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst unique = [];\n\tfor (let i = 0; i < out.length; i++) {\n\t\tif (unique.indexOf(out[i]) === -1) {\n\t\t\tunique.push(out[i]);\n\t\t}\n\t}\n\n\treturn unique;\n}\n", "\n/** Returns the string as an encoded CSS identifier. */\nexport default function encodeCSS(value) {\n\tif (value === '') {\n\t\treturn '';\n\t}\n\n\tlet hex;\n\tlet result = '';\n\tfor (let i = 0; i < value.length; i++) {\n\t\thex = value.charCodeAt(i).toString(36);\n\t\tif (i === 0) {\n\t\t\tresult += hex;\n\t\t} else {\n\t\t\tresult += '-' + hex;\n\t\t}\n\t}\n\n\treturn 'csstools-has-' + result;\n}\n", "/* global MutationObserver,requestAnimationFrame,cancelAnimationFrame,self,HTMLElement */\n\nimport '@mrhenry/core-web/modules/~element-qsa-has.js';\nimport extractEncodedSelectors from './encode/extract.mjs';\nimport encodeCSS from './encode/encode.mjs';\n\nfunction hasNativeSupport() {\n\ttry {\n\t\tif (!('CSS' in self) || !('supports' in self.CSS) || !self.CSS.supports('selector(:has(div))')) {\n\t\t\treturn false;\n\t\t}\n\n\t} catch (_) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nexport default function cssHasPseudo(document, options) {\n\t// OPTIONS\n\t{\n\t\tif (!options) {\n\t\t\toptions = {};\n\t\t}\n\n\t\toptions = {\n\t\t\thover: (!!options.hover) || false,\n\t\t\tdebug: (!!options.debug) || false,\n\t\t\tobservedAttributes: options.observedAttributes || [],\n\t\t\tforcePolyfill: (!!options.forcePolyfill) || false,\n\t\t};\n\n\t\toptions.mustPolyfill = options.forcePolyfill || !hasNativeSupport();\n\n\t\tif (!Array.isArray(options.observedAttributes)) {\n\t\t\toptions.observedAttributes = [];\n\t\t}\n\n\t\toptions.observedAttributes = options.observedAttributes.filter((x) => {\n\t\t\treturn (typeof x === 'string');\n\t\t});\n\n\t\t// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes\n\t\t// `data-*` and `style` were omitted\n\t\toptions.observedAttributes = options.observedAttributes.concat(['accept', 'accept-charset', 'accesskey', 'action', 'align', 'allow', 'alt', 'async', 'autocapitalize', 'autocomplete', 'autofocus', 'autoplay', 'buffered', 'capture', 'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu', 'controls', 'coords', 'crossorigin', 'csp', 'data', 'datetime', 'decoding', 'default', 'defer', 'dir', 'dirname', 'disabled', 'download', 'draggable', 'enctype', 'enterkeyhint', 'for', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', 'headers', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'importance', 'integrity', 'intrinsicsize', 'inputmode', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'loop', 'low', 'manifest', 'max', 'maxlength', 'minlength', 'media', 'method', 'min', 'multiple', 'muted', 'name', 'novalidate', 'open', 'optimum', 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'radiogroup', 'readonly', 'referrerpolicy', 'rel', 'required', 'reversed', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'summary', 'tabindex', 'target', 'title', 'translate', 'type', 'usemap', 'value', 'width', 'wrap']);\n\t}\n\n\tconst observedItems = [];\n\n\t// document.createAttribute() doesn't support `:` in the name. innerHTML does\n\tconst attributeElement = document.createElement('x');\n\n\t// walk all stylesheets to collect observed css rules\n\t[].forEach.call(document.styleSheets, walkStyleSheet);\n\tif (!options.mustPolyfill) {\n\t\t// Cleanup of rules will have happened in `walkStyleSheet`\n\t\t// Native support will take over from here\n\t\treturn;\n\t}\n\n\ttransformObservedItemsThrottled();\n\n\t// observe DOM modifications that affect selectors\n\tif ('MutationObserver' in self) {\n\t\tconst mutationObserver = new MutationObserver((mutationsList) => {\n\t\t\tmutationsList.forEach(mutation => {\n\t\t\t\t[].forEach.call(mutation.addedNodes || [], node => {\n\t\t\t\t\t// walk stylesheets to collect observed css rules\n\t\t\t\t\tif (node.nodeType === 1 && node.sheet) {\n\t\t\t\t\t\twalkStyleSheet(node.sheet);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// transform observed css rules\n\t\t\t\tcleanupObservedCssRules();\n\t\t\t\ttransformObservedItemsThrottled();\n\t\t\t});\n\t\t});\n\n\t\tmutationObserver.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: options.observedAttributes });\n\t}\n\n\t// observe DOM events that affect pseudo-selectors\n\tdocument.addEventListener('focus', transformObservedItemsThrottled, true);\n\tdocument.addEventListener('blur', transformObservedItemsThrottled, true);\n\tdocument.addEventListener('input', transformObservedItemsThrottled);\n\tdocument.addEventListener('change', transformObservedItemsThrottled, true);\n\n\tif (options.hover) {\n\t\tif ('onpointerenter' in document) {\n\t\t\tdocument.addEventListener('pointerenter', transformObservedItemsThrottled, true);\n\t\t\tdocument.addEventListener('pointerleave', transformObservedItemsThrottled, true);\n\t\t} else {\n\t\t\tdocument.addEventListener('mouseover', transformObservedItemsThrottled, true);\n\t\t\tdocument.addEventListener('mouseout', transformObservedItemsThrottled, true);\n\t\t}\n\t}\n\n\t// observe Javascript setters that effect pseudo-selectors\n\tif ('defineProperty' in Object && 'getOwnPropertyDescriptor' in Object && 'hasOwnProperty' in Object) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\tfunction observeProperty(proto, property) {\n\t\t\t\t// eslint-disable-next-line no-prototype-builtins\n\t\t\t\tif (proto.hasOwnProperty(property)) {\n\t\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(proto, property);\n\t\t\t\t\tif (descriptor && descriptor.configurable && 'set' in descriptor) {\n\t\t\t\t\t\tObject.defineProperty(proto, property, {\n\t\t\t\t\t\t\tconfigurable: descriptor.configurable,\n\t\t\t\t\t\t\tenumerable: descriptor.enumerable,\n\t\t\t\t\t\t\tget: function () {\n\t\t\t\t\t\t\t\treturn descriptor.get.apply(this, arguments);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tset: function () {\n\t\t\t\t\t\t\t\tdescriptor.set.apply(this, arguments);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\ttransformObservedItemsThrottled();\n\t\t\t\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t\t\t\t// should never happen as there is an inner try/catch\n\t\t\t\t\t\t\t\t\t// but just in case\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ('HTMLElement' in self && HTMLElement.prototype) {\n\t\t\t\tobserveProperty(HTMLElement.prototype, 'disabled');\n\t\t\t}\n\n\t\t\t// Not all of these elements have all of these properties.\n\t\t\t// But the code above checks if they exist first.\n\t\t\t['checked', 'selected', 'readOnly', 'required'].forEach((property) => {\n\t\t\t\t[\n\t\t\t\t\t'HTMLButtonElement',\n\t\t\t\t\t'HTMLFieldSetElement',\n\t\t\t\t\t'HTMLInputElement',\n\t\t\t\t\t'HTMLMeterElement',\n\t\t\t\t\t'HTMLOptGroupElement',\n\t\t\t\t\t'HTMLOptionElement',\n\t\t\t\t\t'HTMLOutputElement',\n\t\t\t\t\t'HTMLProgressElement',\n\t\t\t\t\t'HTMLSelectElement',\n\t\t\t\t\t'HTMLTextAreaElement',\n\t\t\t\t].forEach((elementName) => {\n\t\t\t\t\tif (elementName in self && self[elementName].prototype) {\n\t\t\t\t\t\tobserveProperty(self[elementName].prototype, property);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tif (options.debug) {\n\t\t\t\tconsole.error(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tlet transformObservedItemsThrottledBusy = false;\n\tfunction transformObservedItemsThrottled() {\n\t\tif (transformObservedItemsThrottledBusy) {\n\t\t\tcancelAnimationFrame(transformObservedItemsThrottledBusy);\n\t\t}\n\n\t\ttransformObservedItemsThrottledBusy = requestAnimationFrame(() => {\n\t\t\ttransformObservedItems();\n\t\t});\n\t}\n\n\t// transform observed css rules\n\tfunction transformObservedItems() {\n\t\tobservedItems.forEach((item) => {\n\t\t\tconst nodes = [];\n\n\t\t\tlet matches = [];\n\t\t\ttry {\n\t\t\t\tmatches = document.querySelectorAll(item.selector);\n\t\t\t} catch (e) {\n\t\t\t\tif (options.debug) {\n\t\t\t\t\tconsole.error(e);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t[].forEach.call(matches, (element) => {\n\t\t\t\t// memorize the node\n\t\t\t\tnodes.push(element);\n\n\t\t\t\t// set an attribute with an irregular attribute name\n\t\t\t\t// document.createAttribute() doesn't support special characters\n\t\t\t\tattributeElement.innerHTML = '';\n\n\t\t\t\telement.setAttributeNode(attributeElement.children[0].attributes[0].cloneNode());\n\n\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t});\n\n\t\t\t// remove the encoded attribute from all nodes that no longer match them\n\t\t\titem.nodes.forEach(node => {\n\t\t\t\tif (nodes.indexOf(node) === -1) {\n\t\t\t\t\tnode.removeAttribute(item.attributeName);\n\n\t\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// update the\n\t\t\titem.nodes = nodes;\n\t\t});\n\t}\n\n\t// remove any observed cssrules that no longer apply\n\tfunction cleanupObservedCssRules() {\n\t\t[].push.apply(\n\t\t\tobservedItems,\n\t\t\tobservedItems.splice(0).filter((item) => {\n\t\t\t\treturn item.rule.parentStyleSheet &&\n\t\t\t\t\titem.rule.parentStyleSheet.ownerNode &&\n\t\t\t\t\tdocument.documentElement.contains(item.rule.parentStyleSheet.ownerNode);\n\t\t\t}),\n\t\t);\n\t}\n\n\t// walk a stylesheet to collect observed css rules\n\tfunction walkStyleSheet(styleSheet) {\n\t\ttry {\n\t\t\t// walk a css rule to collect observed css rules\n\t\t\t[].forEach.call(styleSheet.cssRules || [], (rule, index) => {\n\t\t\t\tif (rule.selectorText) {\n\t\t\t\t\trule.selectorText = rule.selectorText.replace(/\\.js-has-pseudo\\s/g, '');\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// decode the selector text in all browsers to:\n\t\t\t\t\t\tconst hasSelectors = extractEncodedSelectors(rule.selectorText.toString());\n\t\t\t\t\t\tif (hasSelectors.length === 0) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!options.mustPolyfill) {\n\t\t\t\t\t\t\tstyleSheet.deleteRule(index);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (let i = 0; i < hasSelectors.length; i++) {\n\t\t\t\t\t\t\tconst hasSelector = hasSelectors[i];\n\t\t\t\t\t\t\tobservedItems.push({\n\t\t\t\t\t\t\t\trule: rule,\n\t\t\t\t\t\t\t\tselector: hasSelector,\n\t\t\t\t\t\t\t\tattributeName: encodeCSS(hasSelector),\n\t\t\t\t\t\t\t\tnodes: [],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (options.debug) {\n\t\t\t\t\t\t\tconsole.error(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\twalkStyleSheet(rule);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tif (options.debug) {\n\t\t\t\tconsole.error(e);\n\t\t\t}\n\t\t}\n\t}\n}\n", "/* eslint-disable */\n(function (global) {\n\ttry {\n\t\t// test for has support\n\t\tglobal.document.querySelector(':has(*, :does-not-exist, > *)');\n\n\t\tif (\n\t\t\t!global.document.querySelector(':has(:scope *)') &&\n\t\t\tCSS.supports('selector(:has(div))')\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t} catch (_) { }\n\n\t// ELEMENT\n\t// polyfill Element#querySelector\n\tvar querySelectorWithHasElement = polyfill(global.Element.prototype.querySelector);\n\n\tglobal.Element.prototype.querySelector = function querySelector(selectors) {\n\t\treturn querySelectorWithHasElement.apply(this, arguments);\n\t};\n\n\t// polyfill Element#querySelectorAll\n\tvar querySelectorAllWithHasElement = polyfill(global.Element.prototype.querySelectorAll);\n\n\tglobal.Element.prototype.querySelectorAll = function querySelectorAll(selectors) {\n\t\treturn querySelectorAllWithHasElement.apply(this, arguments);\n\t};\n\n\t// polyfill Element#matches\n\tif (global.Element.prototype.matches) {\n\t\tvar matchesWithHasElement = polyfill(global.Element.prototype.matches);\n\n\t\tglobal.Element.prototype.matches = function matches(selectors) {\n\t\t\treturn matchesWithHasElement.apply(this, arguments);\n\t\t};\n\t}\n\n\t// polyfill Element#closest\n\tif (global.Element.prototype.closest) {\n\t\tvar closestWithHasElement = polyfill(global.Element.prototype.closest);\n\n\t\tglobal.Element.prototype.closest = function closest(selectors) {\n\t\t\treturn closestWithHasElement.apply(this, arguments);\n\t\t};\n\t}\n\n\t// DOCUMENT\n\tif ('Document' in global && 'prototype' in global.Document) {\n\t\t// polyfill Document#querySelector\n\t\tvar querySelectorWithHasDocument = polyfill(global.Document.prototype.querySelector);\n\n\t\tglobal.Document.prototype.querySelector = function querySelector(selectors) {\n\t\t\treturn querySelectorWithHasDocument.apply(this, arguments);\n\t\t};\n\n\t\t// polyfill Document#querySelectorAll\n\t\tvar querySelectorAllWithHasDocument = polyfill(global.Document.prototype.querySelectorAll);\n\n\t\tglobal.Document.prototype.querySelectorAll = function querySelectorAll(selectors) {\n\t\t\treturn querySelectorAllWithHasDocument.apply(this, arguments);\n\t\t};\n\n\t\t// polyfill Document#matches\n\t\tif (global.Document.prototype.matches) {\n\t\t\tvar matchesWithHasDocument = polyfill(global.Document.prototype.matches);\n\n\t\t\tglobal.Document.prototype.matches = function matches(selectors) {\n\t\t\t\treturn matchesWithHasDocument.apply(this, arguments);\n\t\t\t};\n\t\t}\n\n\t\t// polyfill Document#closest\n\t\tif (global.Document.prototype.closest) {\n\t\t\tvar closestWithHasDocument = polyfill(global.Document.prototype.closest);\n\n\t\t\tglobal.Document.prototype.closest = function closest(selectors) {\n\t\t\t\treturn closestWithHasDocument.apply(this, arguments);\n\t\t\t};\n\t\t}\n\t}\n\n\tfunction pseudoClassHasInnerQuery(query) {\n\t\tvar current = '';\n\t\tvar start = 0;\n\t\tvar depth = 0;\n\n\t\tvar escaped = false;\n\n\t\tvar quoted = false;\n\t\tvar quotedMark = false;\n\n\t\tvar inHas = false;\n\n\t\tvar bracketed = 0;\n\n\t\tfor (var i = 0; i < query.length; i++) {\n\t\t\tvar char = query[i];\n\n\t\t\tif (escaped) {\n\t\t\t\tcurrent += char;\n\t\t\t\tescaped = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (quoted) {\n\t\t\t\tif (char === quotedMark) {\n\t\t\t\t\tquoted = false;\n\t\t\t\t}\n\n\t\t\t\tcurrent += char;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (current.toLowerCase() === ':has(' && !inHas) {\n\t\t\t\tinHas = true;\n\t\t\t\tstart = i;\n\t\t\t\tcurrent = '';\n\t\t\t}\n\n\t\t\tswitch (char) {\n\t\t\t\tcase ':':\n\t\t\t\t\tif (!inHas) {\n\t\t\t\t\t\tcurrent = '';\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '(':\n\t\t\t\t\tif (inHas) {\n\t\t\t\t\t\tdepth++;\n\t\t\t\t\t}\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase ')':\n\t\t\t\t\tif (inHas) {\n\t\t\t\t\t\tif (depth === 0) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tinnerQuery: current,\n\t\t\t\t\t\t\t\tstart: start,\n\t\t\t\t\t\t\t\tend: i-1\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdepth--;\n\t\t\t\t\t}\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tescaped = true;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\"':\n\t\t\t\tcase \"'\":\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tquoted = true;\n\t\t\t\t\tquotedMark = char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '[':\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tbracketed++;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase \"]\":\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tif (bracketed > 0) {\n\t\t\t\t\t\tbracketed--\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\t\t\t\n\t\t\t\tdefault:\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction replaceScopeWithAttr(query, attr) {\n\t\tvar parts = [];\n\t\tvar current = '';\n\n\t\tvar escaped = false;\n\n\t\tvar quoted = false;\n\t\tvar quotedMark = false;\n\n\t\tvar bracketed = 0;\n\n\t\tfor (var i = 0; i < query.length; i++) {\n\t\t\tvar char = query[i];\n\n\t\t\tif (escaped) {\n\t\t\t\tcurrent += char;\n\t\t\t\tescaped = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (quoted) {\n\t\t\t\tif (char === quotedMark) {\n\t\t\t\t\tquoted = false;\n\t\t\t\t}\n\n\t\t\t\tcurrent += char;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (current.toLowerCase() === ':scope' && !bracketed && (/^[\\[\\.\\:\\\\\"\\s|+>~#&,)]/.test(char || ''))) {\n\t\t\t\tparts.push(current.slice(0, current.length - 6));\n\t\t\t\tparts.push('[' + attr + ']');\n\t\t\t\tcurrent = '';\n\t\t\t}\n\n\t\t\tswitch (char) {\n\t\t\t\tcase ':':\n\t\t\t\t\tparts.push(current);\n\t\t\t\t\tcurrent = '';\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tescaped = true;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\"':\n\t\t\t\tcase \"'\":\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tquoted = true;\n\t\t\t\t\tquotedMark = char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '[':\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tbracketed++;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase \"]\":\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tif (bracketed > 0) {\n\t\t\t\t\t\tbracketed--\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\n\t\t\t\tdefault:\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (current.toLowerCase() === ':scope') {\n\t\t\tparts.push(current.slice(0, current.length - 6));\n\t\t\tparts.push('[' + attr + ']');\n\t\t\tcurrent = '';\n\t\t}\n\n\t\tif (parts.length === 0) {\n\t\t\treturn query;\n\t\t}\n\n\t\treturn parts.join('') + current;\n\t}\n\n\tfunction charIsNestedMarkMirror(char, mark) {\n\t\tif (mark === '(' && char === ')') {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (mark === '[' && char === ']') {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction splitSelector(query) {\n\t\tvar selectors = [];\n\t\tvar current = '';\n\n\t\tvar escaped = false;\n\n\t\tvar quoted = false;\n\t\tvar quotedMark = false;\n\n\t\tvar nestedMark = false;\n\t\tvar nestedDepth = 0;\n\n\t\tfor (var i = 0; i < query.length; i++) {\n\t\t\tvar char = query[i];\n\n\t\t\tif (escaped) {\n\t\t\t\tcurrent += char;\n\t\t\t\tescaped = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tswitch (char) {\n\t\t\t\tcase ',':\n\t\t\t\t\tif (quoted) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (nestedDepth > 0) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tselectors.push(current);\n\t\t\t\t\tcurrent = '';\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tescaped = true;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '\"':\n\t\t\t\tcase \"'\":\n\t\t\t\t\tif (quoted && char === quotedMark) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tquoted = false;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tquoted = true;\n\t\t\t\t\tquotedMark = char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase '(':\n\t\t\t\tcase ')':\n\t\t\t\tcase '[':\n\t\t\t\tcase ']':\n\t\t\t\t\tif (quoted) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (charIsNestedMarkMirror(char, nestedMark)) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tnestedDepth--;\n\n\t\t\t\t\t\tif (nestedDepth === 0) {\n\t\t\t\t\t\t\tnestedMark = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (char === nestedMark) {\n\t\t\t\t\t\tcurrent += char;\n\t\t\t\t\t\tnestedDepth++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tnestedDepth++;\n\t\t\t\t\tnestedMark = char;\n\t\t\t\t\tcontinue;\n\n\t\t\t\tdefault:\n\t\t\t\t\tcurrent += char;\n\t\t\t\t\tcontinue;\n\n\t\t\t}\n\t\t}\n\n\t\tselectors.push(current);\n\n\t\treturn selectors;\n\t}\n\n\tfunction replaceAllWithTempAttr(query, nested, callback) {\n\t\tvar inner = pseudoClassHasInnerQuery(query);\n\t\tif (!inner) {\n\t\t\treturn query;\n\t\t}\n\n\t\tif (nested) {\n\t\t\treturn false;\n\t\t}\n\n\t\tvar innerQuery = inner.innerQuery;\n\t\tvar attr = 'q-has' + (Math.floor(Math.random() * 9000000) + 1000000);\n\t\tvar innerReplacement = '[' + attr + ']';\n\n\t\tvar x = query;\n\n\t\tif (inner.innerQuery.toLowerCase().indexOf(':has(') > -1) {\n\t\t\tvar innerParts = splitSelector(inner.innerQuery);\n\t\t\tvar newInnerParts = [];\n\t\t\tfor (var i = 0; i < innerParts.length; i++) {\n\t\t\t\tvar innerPart = innerParts[i];\n\n\t\t\t\t// Nested has is not supported.\n\t\t\t\t// If a recursive/nested call returns \"false\" we throw\n\t\t\t\tvar innerPartReplaced = replaceAllWithTempAttr(innerPart, true, function () { });\n\t\t\t\tif (!innerPartReplaced) {\n\t\t\t\t\tthrow new Error(\"Nested :has() is not supported\")\n\t\t\t\t} else {\n\t\t\t\t\tnewInnerParts.push(innerPart);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar _prefix = x.substring(0, inner.start - 5); // ':has('.length === 5\n\t\t\tvar _suffix = x.substring(inner.end + 2); // ')'.length === 1\n\n\t\t\treturn _prefix + newInnerParts.join(', ') + _suffix;\n\t\t}\n\n\t\tvar _prefix = x.substring(0, inner.start - 5); // ':has('.length === 5\n\t\tvar _suffix = x.substring(inner.end + 2); // ')'.length === 1\n\n\t\tx = _prefix + innerReplacement + _suffix;\n\n\t\tcallback(innerQuery, attr);\n\t\tif (x.toLowerCase().indexOf(':has(') > -1) {\n\t\t\tvar y = replaceAllWithTempAttr(x, false, callback);\n\t\t\tif (y) {\n\t\t\t\treturn y;\n\t\t\t}\n\t\t}\n\n\t\treturn x;\n\t}\n\n\tfunction walkNode(rootNode, callback) {\n\t\tif (('setAttribute' in (rootNode)) && ('querySelector' in (rootNode))) {\n\t\t\tcallback(rootNode);\n\t\t}\n\n\t\tif (rootNode.hasChildNodes()) {\n\t\t\tvar nodes = rootNode.childNodes;\n\t\t\tfor (var i = 0; i < nodes.length; ++i) {\n\t\t\t\twalkNode(nodes[i], callback);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction polyfill(qsa) {\n\t\treturn function (selectors) {\n\t\t\tif ((selectors.toLowerCase().indexOf(':has(') === -1) || !pseudoClassHasInnerQuery(selectors)) {\n\t\t\t\treturn qsa.apply(this, arguments);\n\t\t\t}\n\n\t\t\tvar rootNode;\n\t\t\tif ('getRootNode' in this) {\n\t\t\t\trootNode = this.getRootNode();\n\t\t\t} else {\n\t\t\t\tvar r = this;\n\t\t\t\twhile (r) {\n\t\t\t\t\trootNode = r;\n\t\t\t\t\tr = r.parentNode;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar _focus = this;\n\t\t\tif (_focus === global.document) {\n\t\t\t\t_focus = global.document.documentElement;\n\t\t\t}\n\n\t\t\tvar scopeAttr = 'q-has-scope' + (Math.floor(Math.random() * 9000000) + 1000000);\n\t\t\t_focus.setAttribute(scopeAttr, '');\n\n\t\t\ttry {\n\t\t\t\tselectors = replaceScopeWithAttr(selectors, scopeAttr);\n\n\t\t\t\tvar attrs = [scopeAttr];\n\t\t\t\tvar newQuery = replaceAllWithTempAttr(selectors, false, function (inner, attr) {\n\t\t\t\t\tattrs.push(attr);\n\n\t\t\t\t\tvar selectorParts = splitSelector(inner);\n\t\t\t\t\tfor (var x = 0; x < selectorParts.length; x++) {\n\t\t\t\t\t\tvar selectorPart = selectorParts[x].trim();\n\t\t\t\t\t\tvar absoluteSelectorPart = selectorPart;\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tselectorPart[0] === '>' ||\n\t\t\t\t\t\t\tselectorPart[0] === '+' ||\n\t\t\t\t\t\t\tselectorPart[0] === '~'\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tabsoluteSelectorPart = selectorPart.slice(1).trim();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tabsoluteSelectorPart = ':scope ' + selectorPart;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\twalkNode(rootNode, function (node) {\n\t\t\t\t\t\t\tif (!(node.querySelector(absoluteSelectorPart))) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tswitch (selectorPart[0]) {\n\t\t\t\t\t\t\t\tcase '~':\n\t\t\t\t\t\t\t\tcase '+':\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvar siblings = node.childNodes;\n\t\t\t\t\t\t\t\t\t\tfor (var i = 0; i < siblings.length; i++) {\n\t\t\t\t\t\t\t\t\t\t\tvar sibling = siblings[i];\n\t\t\t\t\t\t\t\t\t\t\tif (!('setAttribute' in sibling)) {\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tvar idAttr = 'q-has-id' + (Math.floor(Math.random() * 9000000) + 1000000);\n\t\t\t\t\t\t\t\t\t\t\tsibling.setAttribute(idAttr, '');\n\n\t\t\t\t\t\t\t\t\t\t\tif (node.querySelector(':scope [' + idAttr + ']' + ' ' + selectorPart)) {\n\t\t\t\t\t\t\t\t\t\t\t\tsibling.setAttribute(attr, '');\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tsibling.removeAttribute(idAttr);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase '>':\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvar idAttr = 'q-has-id' + (Math.floor(Math.random() * 9000000) + 1000000);\n\t\t\t\t\t\t\t\t\t\tnode.setAttribute(idAttr, '');\n\n\t\t\t\t\t\t\t\t\t\tif (node.querySelector(':scope[' + idAttr + ']' + ' ' + selectorPart)) {\n\t\t\t\t\t\t\t\t\t\t\tnode.setAttribute(attr, '');\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tnode.removeAttribute(idAttr);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tnode.setAttribute(attr, '');\n\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\targuments[0] = newQuery;\n\n\t\t\t\t// results of the qsa\n\t\t\t\tvar elementOrNodeList = qsa.apply(this, arguments);\n\n\t\t\t\t_focus.removeAttribute(scopeAttr);\n\n\t\t\t\tif (attrs.length > 0) {\n\t\t\t\t\t// remove the fallback attribute\n\t\t\t\t\tvar attrsForQuery = [];\n\t\t\t\t\tfor (var j = 0; j < attrs.length; j++) {\n\t\t\t\t\t\tattrsForQuery.push('[' + attrs[j] + ']');\n\t\t\t\t\t}\n\n\t\t\t\t\tvar elements = global.document.querySelectorAll(attrsForQuery.join(','));\n\t\t\t\t\tfor (var k = 0; k < elements.length; k++) {\n\t\t\t\t\t\tvar element = elements[k];\n\t\t\t\t\t\tfor (var l = 0; l < attrs.length; l++) {\n\t\t\t\t\t\t\telement.removeAttribute(attrs[l]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// return the results of the qsa\n\t\t\t\treturn elementOrNodeList;\n\t\t\t} catch (err) {\n\t\t\t\t_focus.removeAttribute(scopeAttr);\n\n\t\t\t\tif (attrs.length > 0) {\n\t\t\t\t\t// remove the fallback attribute\n\t\t\t\t\tvar attrsForQuery = [];\n\t\t\t\t\tfor (var j = 0; j < attrs.length; j++) {\n\t\t\t\t\t\tattrsForQuery.push('[' + attrs[j] + ']');\n\t\t\t\t\t}\n\n\t\t\t\t\tvar elements = global.document.querySelectorAll(attrsForQuery.join(','));\n\t\t\t\t\tfor (var k = 0; k < elements.length; k++) {\n\t\t\t\t\t\tvar element = elements[k];\n\t\t\t\t\t\tfor (var l = 0; l < attrs.length; l++) {\n\t\t\t\t\t\t\telement.removeAttribute(attrs[l]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar errorMessage = '';\n\t\t\t\ttry {\n\t\t\t\t\tqsa.apply(this, [':core-web-does-not-exist']);\n\t\t\t\t} catch (dummyError) {\n\t\t\t\t\terrorMessage = dummyError.message;\n\t\t\t\t\tif (errorMessage) {\n\t\t\t\t\t\terrorMessage = errorMessage.replace(':core-web-does-not-exist', selectors);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!errorMessage) {\n\t\t\t\t\terrorMessage = \"Failed to execute 'querySelector' on 'Document': '\" + selectors + \"' is not a valid selector.\";\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tthrow new DOMException(errorMessage);\n\t\t\t\t} catch (_) {\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n})(self);\n", "const $ = (selector, parent = document) => parent.querySelector(selector);\nconst $$ = (selector, parent = document) => parent.querySelectorAll(selector);\nconst videoLinks = $$(\".video-embed a\");\n\nfunction youtube_parser(url){\n const regExp = /^.*(youtu.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=|&v=)([^#&?]*).*/;\n const match = url.match(regExp);\n return match && match[2].length === 11 ? match[2] : undefined;\n}\n\nfunction vimeo_parser(url){\n const regex = /\\/(\\d+)(?:\\/(\\w+))?/;\n const match = url.match(regex);\n\n if (match) {\n return [match[1], match[2]];\n } else {\n return undefined;\n }\n}\n\nvideoLinks.forEach((link) => {\n const url = link.href;\n let formattedURL = \"\";\n\n if (url.indexOf(\"vimeo\") !== -1) {\n const [ID,unlisted] = vimeo_parser(url);\n if (ID) {\n formattedURL = `https://player.vimeo.com/video/${ID}?h=${unlisted}&title=0&byline=0&portrait=0&autoplay=1&dnt=1`;\n }\n }\n\n if (formattedURL !== \"\") {\n link.addEventListener(\"click\", function(event) {\n event.preventDefault();\n const replacementDiv = document.createElement('div');\n const videoEmbed = ``;\n replacementDiv.innerHTML = videoEmbed;\n link.replaceWith(replacementDiv);\n })\n }\n});", "const $ = (selector, parent = document) => parent.querySelector(selector);\nconst $$ = (selector, parent = document) => parent.querySelectorAll(selector);\n\nconst productGroups = $$(\".product-group\");\n\nproductGroups.forEach(function(group) {\n const loadMoreButton = $(\".load-more button\", group);\n const products = $$(\".products > div.product\", group);\n\n if (products.length > 2) {\n loadMoreButton.parentNode.classList.remove(\"hidden\");\n }\n\n loadMoreButton.addEventListener(\"click\", function(e){\n e.preventDefault();\n products.forEach(function(product) {\n product.classList.add(\"reveal\");\n })\n\n loadMoreButton.classList.add(\"hidden\");\n })\n});\n", "const $ = (selector, parent = document) => parent.querySelector(selector);\nconst $$ = (selector, parent = document) => parent.querySelectorAll(selector);\nimport SnapSlider from '@tannerhodges/snap-slider';\n\nconst desktopMediaQuery = window.matchMedia('(min-width: 768px)');\nconst tabletMediaQuery = window.matchMedia('(min-width: 576px) and (max-width: 767px)');\nconst mobileMediaQuery = window.matchMedia('(max-width: 575px)');\n\nconst galleries = $$(\".gallery\");\n\nfunction empty(element) {\n // Just in case it's faster to check for firstChild than lastChild\n // (e.g. if the element list is implemented as a directed linked-list by the UA).\n while (element.firstChild) {\n // In general, it's significantly faster to remove the last element of a collection\n // than it is to remove the first element.\n element.removeChild(element.lastChild);\n }\n}\n\nfunction addNavButtons(gallery) {\n const gallerySlides = $$(\".gallery-slide\", gallery);\n const galleryNav = $(\".slider-nav\", gallery);\n const lastButton = $(\"li:last-child\", galleryNav);\n const list = document.createElement(\"ul\");\n let buttonCollection = ``;\n\n gallerySlides.forEach((slide, index) => {\n buttonCollection = buttonCollection + `
  • `;\n })\n\n list.innerHTML = buttonCollection;\n\n galleryNav.insertBefore(list, lastButton);\n}\n\nfunction adjustNav(gallery) {\n const gallerySlides = $$(\".gallery-slide\", gallery);\n const galleryNav = $(\".slider-nav\", gallery);\n const buttons = $$(\"button:not(.end-button)\",galleryNav);\n let buttonCount = 0;\n\n if (desktopMediaQuery.matches) {\n if (gallerySlides.length > 4) {\n buttonCount = gallerySlides.length - 4 + 1;\n galleryNav.classList.remove(\"hidden\");\n } else {\n galleryNav.classList.add(\"hidden\");\n }\n }\n\n if (tabletMediaQuery.matches) {\n if (gallerySlides.length > 2) {\n buttonCount = gallerySlides.length - 2 + 1;\n galleryNav.classList.remove(\"hidden\");\n } else {\n galleryNav.classList.add(\"hidden\");\n }\n }\n\n if (mobileMediaQuery.matches) {\n if (gallerySlides.length > 1) {\n buttonCount = gallerySlides.length;\n galleryNav.classList.remove(\"hidden\");\n } else {\n galleryNav.classList.add(\"hidden\");\n }\n }\n\n buttons.forEach((button, index) => {\n if (index < buttonCount) {\n button.parentNode.classList.remove(\"!hidden\");\n } else {\n button.parentNode.classList.add(\"!hidden\");\n }\n });\n}\n\ngalleries.forEach(function(gallery) {\n const dialog = $(\".gallery-dialog\", gallery);\n const dialogContent = $(\".dialog-content\", dialog);\n const dialogClose = $(\".close\", dialog);\n const galleryLinks = $$(\"a.gallery-link\", gallery);\n addNavButtons(gallery);\n adjustNav(gallery);\n\n galleryLinks.forEach(function (link) {\n const href = link.getAttribute(\"href\");\n\n link.addEventListener(\"click\", function(e) {\n e.preventDefault();\n\n const image = ``;\n\n dialogContent.insertAdjacentHTML(\"beforeend\", image);\n dialog.showModal();\n });\n })\n\n dialogClose.addEventListener(\"click\", function(e) {\n empty(dialogContent);\n dialog.close();\n })\n});\n\nif (galleries.length > 0) {\n let resizeTimer;\n\n window.addEventListener('resize', function(e) {\n clearTimeout(resizeTimer);\n resizeTimer = setTimeout(function() {\n galleries.forEach((gallery) => {\n adjustNav(gallery);\n })\n }, 250);\n });\n}", "// nb. This is for IE10 and lower _only_.\nvar supportCustomEvent = window.CustomEvent;\nif (!supportCustomEvent || typeof supportCustomEvent === 'object') {\n supportCustomEvent = function CustomEvent(event, x) {\n x = x || {};\n var ev = document.createEvent('CustomEvent');\n ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);\n return ev;\n };\n supportCustomEvent.prototype = window.Event.prototype;\n}\n\n/**\n * Dispatches the passed event to both an \"on\" handler as well as via the\n * normal dispatch operation. Does not bubble.\n *\n * @param {!EventTarget} target\n * @param {!Event} event\n * @return {boolean}\n */\nfunction safeDispatchEvent(target, event) {\n var check = 'on' + event.type.toLowerCase();\n if (typeof target[check] === 'function') {\n target[check](event);\n }\n return target.dispatchEvent(event);\n}\n\n/**\n * @param {Element} el to check for stacking context\n * @return {boolean} whether this el or its parents creates a stacking context\n */\nfunction createsStackingContext(el) {\n while (el && el !== document.body) {\n var s = window.getComputedStyle(el);\n var invalid = function(k, ok) {\n return !(s[k] === undefined || s[k] === ok);\n };\n\n if (s.opacity < 1 ||\n invalid('zIndex', 'auto') ||\n invalid('transform', 'none') ||\n invalid('mixBlendMode', 'normal') ||\n invalid('filter', 'none') ||\n invalid('perspective', 'none') ||\n s['isolation'] === 'isolate' ||\n s.position === 'fixed' ||\n s.webkitOverflowScrolling === 'touch') {\n return true;\n }\n el = el.parentElement;\n }\n return false;\n}\n\n/**\n * Finds the nearest from the passed element.\n *\n * @param {Element} el to search from\n * @return {HTMLDialogElement} dialog found\n */\nfunction findNearestDialog(el) {\n while (el) {\n if (el.localName === 'dialog') {\n return /** @type {HTMLDialogElement} */ (el);\n }\n if (el.parentElement) {\n el = el.parentElement;\n } else if (el.parentNode) {\n el = el.parentNode.host;\n } else {\n el = null;\n }\n }\n return null;\n}\n\n/**\n * Blur the specified element, as long as it's not the HTML body element.\n * This works around an IE9/10 bug - blurring the body causes Windows to\n * blur the whole application.\n *\n * @param {Element} el to blur\n */\nfunction safeBlur(el) {\n // Find the actual focused element when the active element is inside a shadow root\n while (el && el.shadowRoot && el.shadowRoot.activeElement) {\n el = el.shadowRoot.activeElement;\n }\n\n if (el && el.blur && el !== document.body) {\n el.blur();\n }\n}\n\n/**\n * @param {!NodeList} nodeList to search\n * @param {Node} node to find\n * @return {boolean} whether node is inside nodeList\n */\nfunction inNodeList(nodeList, node) {\n for (var i = 0; i < nodeList.length; ++i) {\n if (nodeList[i] === node) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * @param {HTMLFormElement} el to check\n * @return {boolean} whether this form has method=\"dialog\"\n */\nfunction isFormMethodDialog(el) {\n if (!el || !el.hasAttribute('method')) {\n return false;\n }\n return el.getAttribute('method').toLowerCase() === 'dialog';\n}\n\n/**\n * @param {!DocumentFragment|!Element} hostElement\n * @return {?Element}\n */\nfunction findFocusableElementWithin(hostElement) {\n // Note that this is 'any focusable area'. This list is probably not exhaustive, but the\n // alternative involves stepping through and trying to focus everything.\n var opts = ['button', 'input', 'keygen', 'select', 'textarea'];\n var query = opts.map(function(el) {\n return el + ':not([disabled])';\n });\n // TODO(samthor): tabindex values that are not numeric are not focusable.\n query.push('[tabindex]:not([disabled]):not([tabindex=\"\"])'); // tabindex != \"\", not disabled\n var target = hostElement.querySelector(query.join(', '));\n\n if (!target && 'attachShadow' in Element.prototype) {\n // If we haven't found a focusable target, see if the host element contains an element\n // which has a shadowRoot.\n // Recursively search for the first focusable item in shadow roots.\n var elems = hostElement.querySelectorAll('*');\n for (var i = 0; i < elems.length; i++) {\n if (elems[i].tagName && elems[i].shadowRoot) {\n target = findFocusableElementWithin(elems[i].shadowRoot);\n if (target) {\n break;\n }\n }\n }\n }\n return target;\n}\n\n/**\n * Determines if an element is attached to the DOM.\n * @param {Element} element to check\n * @return {boolean} whether the element is in DOM\n */\nfunction isConnected(element) {\n return element.isConnected || document.body.contains(element);\n}\n\n/**\n * @param {!Event} event\n * @return {?Element}\n */\nfunction findFormSubmitter(event) {\n if (event.submitter) {\n return event.submitter;\n }\n\n var form = event.target;\n if (!(form instanceof HTMLFormElement)) {\n return null;\n }\n\n var submitter = dialogPolyfill.formSubmitter;\n if (!submitter) {\n var target = event.target;\n var root = ('getRootNode' in target && target.getRootNode() || document);\n submitter = root.activeElement;\n }\n\n if (!submitter || submitter.form !== form) {\n return null;\n }\n return submitter;\n}\n\n/**\n * @param {!Event} event\n */\nfunction maybeHandleSubmit(event) {\n if (event.defaultPrevented) {\n return;\n }\n var form = /** @type {!HTMLFormElement} */ (event.target);\n\n // We'd have a value if we clicked on an imagemap.\n var value = dialogPolyfill.imagemapUseValue;\n var submitter = findFormSubmitter(event);\n if (value === null && submitter) {\n value = submitter.value;\n }\n\n // There should always be a dialog as this handler is added specifically on them, but check just\n // in case.\n var dialog = findNearestDialog(form);\n if (!dialog) {\n return;\n }\n\n // Prefer formmethod on the button.\n var formmethod = submitter && submitter.getAttribute('formmethod') || form.getAttribute('method');\n if (formmethod !== 'dialog') {\n return;\n }\n event.preventDefault();\n\n if (value != null) {\n // nb. we explicitly check against null/undefined\n dialog.close(value);\n } else {\n dialog.close();\n }\n}\n\n/**\n * @param {!HTMLDialogElement} dialog to upgrade\n * @constructor\n */\nfunction dialogPolyfillInfo(dialog) {\n this.dialog_ = dialog;\n this.replacedStyleTop_ = false;\n this.openAsModal_ = false;\n\n // Set a11y role. Browsers that support dialog implicitly know this already.\n if (!dialog.hasAttribute('role')) {\n dialog.setAttribute('role', 'dialog');\n }\n\n dialog.show = this.show.bind(this);\n dialog.showModal = this.showModal.bind(this);\n dialog.close = this.close.bind(this);\n\n dialog.addEventListener('submit', maybeHandleSubmit, false);\n\n if (!('returnValue' in dialog)) {\n dialog.returnValue = '';\n }\n\n if ('MutationObserver' in window) {\n var mo = new MutationObserver(this.maybeHideModal.bind(this));\n mo.observe(dialog, {attributes: true, attributeFilter: ['open']});\n } else {\n // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also\n // seem to fire even if the element was removed as part of a parent removal. Use the removed\n // events to force downgrade (useful if removed/immediately added).\n var removed = false;\n var cb = function() {\n removed ? this.downgradeModal() : this.maybeHideModal();\n removed = false;\n }.bind(this);\n var timeout;\n var delayModel = function(ev) {\n if (ev.target !== dialog) { return; } // not for a child element\n var cand = 'DOMNodeRemoved';\n removed |= (ev.type.substr(0, cand.length) === cand);\n window.clearTimeout(timeout);\n timeout = window.setTimeout(cb, 0);\n };\n ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {\n dialog.addEventListener(name, delayModel);\n });\n }\n // Note that the DOM is observed inside DialogManager while any dialog\n // is being displayed as a modal, to catch modal removal from the DOM.\n\n Object.defineProperty(dialog, 'open', {\n set: this.setOpen.bind(this),\n get: dialog.hasAttribute.bind(dialog, 'open')\n });\n\n this.backdrop_ = document.createElement('div');\n this.backdrop_.className = 'backdrop';\n this.backdrop_.addEventListener('mouseup' , this.backdropMouseEvent_.bind(this));\n this.backdrop_.addEventListener('mousedown', this.backdropMouseEvent_.bind(this));\n this.backdrop_.addEventListener('click' , this.backdropMouseEvent_.bind(this));\n}\n\ndialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({\n\n get dialog() {\n return this.dialog_;\n },\n\n /**\n * Maybe remove this dialog from the modal top layer. This is called when\n * a modal dialog may no longer be tenable, e.g., when the dialog is no\n * longer open or is no longer part of the DOM.\n */\n maybeHideModal: function() {\n if (this.dialog_.hasAttribute('open') && isConnected(this.dialog_)) { return; }\n this.downgradeModal();\n },\n\n /**\n * Remove this dialog from the modal top layer, leaving it as a non-modal.\n */\n downgradeModal: function() {\n if (!this.openAsModal_) { return; }\n this.openAsModal_ = false;\n this.dialog_.style.zIndex = '';\n\n // This won't match the native exactly because if the user set top on a centered\n // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's\n // possible to polyfill this perfectly.\n if (this.replacedStyleTop_) {\n this.dialog_.style.top = '';\n this.replacedStyleTop_ = false;\n }\n\n // Clear the backdrop and remove from the manager.\n this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);\n dialogPolyfill.dm.removeDialog(this);\n },\n\n /**\n * @param {boolean} value whether to open or close this dialog\n */\n setOpen: function(value) {\n if (value) {\n this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');\n } else {\n this.dialog_.removeAttribute('open');\n this.maybeHideModal(); // nb. redundant with MutationObserver\n }\n },\n\n /**\n * Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if\n * they were on the dialog itself.\n *\n * @param {!Event} e to redirect\n */\n backdropMouseEvent_: function(e) {\n if (!this.dialog_.hasAttribute('tabindex')) {\n // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be\n // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this\n // would not be needed - clicks would move the implicit cursor there.\n var fake = document.createElement('div');\n this.dialog_.insertBefore(fake, this.dialog_.firstChild);\n fake.tabIndex = -1;\n fake.focus();\n this.dialog_.removeChild(fake);\n } else {\n this.dialog_.focus();\n }\n\n var redirectedEvent = document.createEvent('MouseEvents');\n redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,\n e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,\n e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);\n this.dialog_.dispatchEvent(redirectedEvent);\n e.stopPropagation();\n },\n\n /**\n * Focuses on the first focusable element within the dialog. This will always blur the current\n * focus, even if nothing within the dialog is found.\n */\n focus_: function() {\n // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.\n var target = this.dialog_.querySelector('[autofocus]:not([disabled])');\n if (!target && this.dialog_.tabIndex >= 0) {\n target = this.dialog_;\n }\n if (!target) {\n target = findFocusableElementWithin(this.dialog_);\n }\n safeBlur(document.activeElement);\n target && target.focus();\n },\n\n /**\n * Sets the zIndex for the backdrop and dialog.\n *\n * @param {number} dialogZ\n * @param {number} backdropZ\n */\n updateZIndex: function(dialogZ, backdropZ) {\n if (dialogZ < backdropZ) {\n throw new Error('dialogZ should never be < backdropZ');\n }\n this.dialog_.style.zIndex = dialogZ;\n this.backdrop_.style.zIndex = backdropZ;\n },\n\n /**\n * Shows the dialog. If the dialog is already open, this does nothing.\n */\n show: function() {\n if (!this.dialog_.open) {\n this.setOpen(true);\n this.focus_();\n }\n },\n\n /**\n * Show this dialog modally.\n */\n showModal: function() {\n if (this.dialog_.hasAttribute('open')) {\n throw new Error('Failed to execute \\'showModal\\' on dialog: The element is already open, and therefore cannot be opened modally.');\n }\n if (!isConnected(this.dialog_)) {\n throw new Error('Failed to execute \\'showModal\\' on dialog: The element is not in a Document.');\n }\n if (!dialogPolyfill.dm.pushDialog(this)) {\n throw new Error('Failed to execute \\'showModal\\' on dialog: There are too many open modal dialogs.');\n }\n\n if (createsStackingContext(this.dialog_.parentElement)) {\n console.warn('A dialog is being shown inside a stacking context. ' +\n 'This may cause it to be unusable. For more information, see this link: ' +\n 'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');\n }\n\n this.setOpen(true);\n this.openAsModal_ = true;\n\n // Optionally center vertically, relative to the current viewport.\n if (dialogPolyfill.needsCentering(this.dialog_)) {\n dialogPolyfill.reposition(this.dialog_);\n this.replacedStyleTop_ = true;\n } else {\n this.replacedStyleTop_ = false;\n }\n\n // Insert backdrop.\n this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);\n\n // Focus on whatever inside the dialog.\n this.focus_();\n },\n\n /**\n * Closes this HTMLDialogElement. This is optional vs clearing the open\n * attribute, however this fires a 'close' event.\n *\n * @param {string=} opt_returnValue to use as the returnValue\n */\n close: function(opt_returnValue) {\n if (!this.dialog_.hasAttribute('open')) {\n throw new Error('Failed to execute \\'close\\' on dialog: The element does not have an \\'open\\' attribute, and therefore cannot be closed.');\n }\n this.setOpen(false);\n\n // Leave returnValue untouched in case it was set directly on the element\n if (opt_returnValue !== undefined) {\n this.dialog_.returnValue = opt_returnValue;\n }\n\n // Triggering \"close\" event for any attached listeners on the .\n var closeEvent = new supportCustomEvent('close', {\n bubbles: false,\n cancelable: false\n });\n safeDispatchEvent(this.dialog_, closeEvent);\n }\n\n});\n\nvar dialogPolyfill = {};\n\ndialogPolyfill.reposition = function(element) {\n var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;\n var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;\n element.style.top = Math.max(scrollTop, topValue) + 'px';\n};\n\ndialogPolyfill.isInlinePositionSetByStylesheet = function(element) {\n for (var i = 0; i < document.styleSheets.length; ++i) {\n var styleSheet = document.styleSheets[i];\n var cssRules = null;\n // Some browsers throw on cssRules.\n try {\n cssRules = styleSheet.cssRules;\n } catch (e) {}\n if (!cssRules) { continue; }\n for (var j = 0; j < cssRules.length; ++j) {\n var rule = cssRules[j];\n var selectedNodes = null;\n // Ignore errors on invalid selector texts.\n try {\n selectedNodes = document.querySelectorAll(rule.selectorText);\n } catch(e) {}\n if (!selectedNodes || !inNodeList(selectedNodes, element)) {\n continue;\n }\n var cssTop = rule.style.getPropertyValue('top');\n var cssBottom = rule.style.getPropertyValue('bottom');\n if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {\n return true;\n }\n }\n }\n return false;\n};\n\ndialogPolyfill.needsCentering = function(dialog) {\n var computedStyle = window.getComputedStyle(dialog);\n if (computedStyle.position !== 'absolute') {\n return false;\n }\n\n // We must determine whether the top/bottom specified value is non-auto. In\n // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but\n // Firefox returns the used value. So we do this crazy thing instead: check\n // the inline style and then go through CSS rules.\n if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||\n (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {\n return false;\n }\n return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);\n};\n\n/**\n * @param {!Element} element to force upgrade\n */\ndialogPolyfill.forceRegisterDialog = function(element) {\n if (window.HTMLDialogElement || element.showModal) {\n console.warn('This browser already supports , the polyfill ' +\n 'may not work correctly', element);\n }\n if (element.localName !== 'dialog') {\n throw new Error('Failed to register dialog: The element is not a dialog.');\n }\n new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));\n};\n\n/**\n * @param {!Element} element to upgrade, if necessary\n */\ndialogPolyfill.registerDialog = function(element) {\n if (!element.showModal) {\n dialogPolyfill.forceRegisterDialog(element);\n }\n};\n\n/**\n * @constructor\n */\ndialogPolyfill.DialogManager = function() {\n /** @type {!Array} */\n this.pendingDialogStack = [];\n\n var checkDOM = this.checkDOM_.bind(this);\n\n // The overlay is used to simulate how a modal dialog blocks the document.\n // The blocking dialog is positioned on top of the overlay, and the rest of\n // the dialogs on the pending dialog stack are positioned below it. In the\n // actual implementation, the modal dialog stacking is controlled by the\n // top layer, where z-index has no effect.\n this.overlay = document.createElement('div');\n this.overlay.className = '_dialog_overlay';\n this.overlay.addEventListener('click', function(e) {\n this.forwardTab_ = undefined;\n e.stopPropagation();\n checkDOM([]); // sanity-check DOM\n }.bind(this));\n\n this.handleKey_ = this.handleKey_.bind(this);\n this.handleFocus_ = this.handleFocus_.bind(this);\n\n this.zIndexLow_ = 100000;\n this.zIndexHigh_ = 100000 + 150;\n\n this.forwardTab_ = undefined;\n\n if ('MutationObserver' in window) {\n this.mo_ = new MutationObserver(function(records) {\n var removed = [];\n records.forEach(function(rec) {\n for (var i = 0, c; c = rec.removedNodes[i]; ++i) {\n if (!(c instanceof Element)) {\n continue;\n } else if (c.localName === 'dialog') {\n removed.push(c);\n }\n removed = removed.concat(c.querySelectorAll('dialog'));\n }\n });\n removed.length && checkDOM(removed);\n });\n }\n};\n\n/**\n * Called on the first modal dialog being shown. Adds the overlay and related\n * handlers.\n */\ndialogPolyfill.DialogManager.prototype.blockDocument = function() {\n document.documentElement.addEventListener('focus', this.handleFocus_, true);\n document.addEventListener('keydown', this.handleKey_);\n this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});\n};\n\n/**\n * Called on the first modal dialog being removed, i.e., when no more modal\n * dialogs are visible.\n */\ndialogPolyfill.DialogManager.prototype.unblockDocument = function() {\n document.documentElement.removeEventListener('focus', this.handleFocus_, true);\n document.removeEventListener('keydown', this.handleKey_);\n this.mo_ && this.mo_.disconnect();\n};\n\n/**\n * Updates the stacking of all known dialogs.\n */\ndialogPolyfill.DialogManager.prototype.updateStacking = function() {\n var zIndex = this.zIndexHigh_;\n\n for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n dpi.updateZIndex(--zIndex, --zIndex);\n if (i === 0) {\n this.overlay.style.zIndex = --zIndex;\n }\n }\n\n // Make the overlay a sibling of the dialog itself.\n var last = this.pendingDialogStack[0];\n if (last) {\n var p = last.dialog.parentNode || document.body;\n p.appendChild(this.overlay);\n } else if (this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n};\n\n/**\n * @param {Element} candidate to check if contained or is the top-most modal dialog\n * @return {boolean} whether candidate is contained in top dialog\n */\ndialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {\n while (candidate = findNearestDialog(candidate)) {\n for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n if (dpi.dialog === candidate) {\n return i === 0; // only valid if top-most\n }\n }\n candidate = candidate.parentElement;\n }\n return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {\n var target = event.composedPath ? event.composedPath()[0] : event.target;\n\n if (this.containedByTopDialog_(target)) { return; }\n\n if (document.activeElement === document.documentElement) { return; }\n\n event.preventDefault();\n event.stopPropagation();\n safeBlur(/** @type {Element} */ (target));\n\n if (this.forwardTab_ === undefined) { return; } // move focus only from a tab key\n\n var dpi = this.pendingDialogStack[0];\n var dialog = dpi.dialog;\n var position = dialog.compareDocumentPosition(target);\n if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n if (this.forwardTab_) {\n // forward\n dpi.focus_();\n } else if (target !== document.documentElement) {\n // backwards if we're not already focused on \n document.documentElement.focus();\n }\n }\n\n return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {\n this.forwardTab_ = undefined;\n if (event.keyCode === 27) {\n event.preventDefault();\n event.stopPropagation();\n var cancelEvent = new supportCustomEvent('cancel', {\n bubbles: false,\n cancelable: true\n });\n var dpi = this.pendingDialogStack[0];\n if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) {\n dpi.dialog.close();\n }\n } else if (event.keyCode === 9) {\n this.forwardTab_ = !event.shiftKey;\n }\n};\n\n/**\n * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are\n * removed and immediately readded don't stay modal, they become normal.\n *\n * @param {!Array} removed that have definitely been removed\n */\ndialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {\n // This operates on a clone because it may cause it to change. Each change also calls\n // updateStacking, which only actually needs to happen once. But who removes many modal dialogs\n // at a time?!\n var clone = this.pendingDialogStack.slice();\n clone.forEach(function(dpi) {\n if (removed.indexOf(dpi.dialog) !== -1) {\n dpi.downgradeModal();\n } else {\n dpi.maybeHideModal();\n }\n });\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n * @return {boolean} whether the dialog was allowed\n */\ndialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {\n var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;\n if (this.pendingDialogStack.length >= allowed) {\n return false;\n }\n if (this.pendingDialogStack.unshift(dpi) === 1) {\n this.blockDocument();\n }\n this.updateStacking();\n return true;\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n */\ndialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {\n var index = this.pendingDialogStack.indexOf(dpi);\n if (index === -1) { return; }\n\n this.pendingDialogStack.splice(index, 1);\n if (this.pendingDialogStack.length === 0) {\n this.unblockDocument();\n }\n this.updateStacking();\n};\n\ndialogPolyfill.dm = new dialogPolyfill.DialogManager();\ndialogPolyfill.formSubmitter = null;\ndialogPolyfill.imagemapUseValue = null;\n\n/**\n * Installs global handlers, such as click listers and native method overrides. These are needed\n * even if a no dialog is registered, as they deal with
    .\n */\nif (window.HTMLDialogElement === undefined) {\n\n /**\n * If HTMLFormElement translates method=\"DIALOG\" into 'get', then replace the descriptor with\n * one that returns the correct value.\n */\n var testForm = document.createElement('form');\n testForm.setAttribute('method', 'dialog');\n if (testForm.method !== 'dialog') {\n var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');\n if (methodDescriptor) {\n // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything\n // and don't bother to update the element.\n var realGet = methodDescriptor.get;\n methodDescriptor.get = function() {\n if (isFormMethodDialog(this)) {\n return 'dialog';\n }\n return realGet.call(this);\n };\n var realSet = methodDescriptor.set;\n /** @this {HTMLElement} */\n methodDescriptor.set = function(v) {\n if (typeof v === 'string' && v.toLowerCase() === 'dialog') {\n return this.setAttribute('method', v);\n }\n return realSet.call(this, v);\n };\n Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);\n }\n }\n\n /**\n * Global 'click' handler, to capture the or