{"version":3,"file":"front.js","sources":["../../sources/javascripts/utils/extend.js","../../sources/javascripts/utils/FocusTrapper.js","../../sources/javascripts/utils/Toggler.js","../../sources/javascripts/front/components/Header.js","../../node_modules/throttle-debounce/esm/index.js","../../sources/javascripts/utils/getCSSCustomProp.js","../../sources/javascripts/front/modules/header.js","../../sources/javascripts/front/components/Form/Field.js","../../sources/javascripts/utils/arrayAreIdenticals.js","../../sources/javascripts/utils/getFragmentFromString.js","../../sources/javascripts/front/modules/print.js","../../sources/javascripts/front/modules/fields.js","../../node_modules/focus-visible/dist/focus-visible.js"],"sourcesContent":["/**\n * Merge objects function\n *\n * Credit: https://gomakethings.com/merging-objects-with-vanilla-javascript/\n *\n * Usage: extend(obj1, obj2);\n * Support deep merge: extend(true, obj1, obj2);\n */\n\nexport default function extend() {\n\n // Variables\n let extended = {};\n let deep = false;\n let i = 0;\n\n // Check if a deep merge\n if (typeof (arguments[0]) === 'boolean') {\n deep = arguments[0];\n i++;\n }\n\n // Merge the object into the extended object\n const merge = function (obj) {\n for (let prop in obj) {\n if (obj.hasOwnProperty(prop)) {\n if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {\n // If we're doing a deep merge and the property is an object\n extended[prop] = extend(true, extended[prop], obj[prop]);\n } else {\n // Otherwise, do a regular merge\n extended[prop] = obj[prop];\n }\n }\n }\n };\n\n // Loop through each object and conduct a merge\n for (; i < arguments.length; i++) {\n merge(arguments[i]);\n }\n\n return extended;\n\n};\n","import extend from './extend';\n\n/**\n * FOCUS TRAPPER\n *\n * Loop focus in a container (in a modal for example).\n */\n\nexport default class FocusTrapper {\n constructor(container, settings) {\n const _ = this;\n\n _.settings = extend(true, {\n selector: 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])', // https://gomakethings.com/how-to-get-the-first-and-last-focusable-elements-in-the-dom/\n }, settings || {});\n\n _.container = container;\n _.lastFocusableBeforeCatch = null;\n _.firstFocusable = null;\n _.lastFocusable = null;\n // Global status, catching : have trapped focus, resting : focus is free\n _.state = 'resting';\n\n // We ensure that the target container can be focus\n if (_.container.getAttribute('tabindex') === null) {\n _.container.setAttribute('tabindex', '-1');\n }\n\n _.handleKeydown = _.handleKeydown.bind(_);\n }\n\n // Update observables elements\n update() {\n const _ = this;\n\n const focusables = _.container.querySelectorAll(_.settings.selector);\n const visibleFocusables = [];\n\n for (let i = 0; i < focusables.length; i++) {\n if (focusables[i].offsetWidth > 0 && focusables[i].offsetHeight > 0) {\n visibleFocusables.push(focusables[i])\n }\n }\n\n [_.firstFocusable] = visibleFocusables;\n _.lastFocusable = visibleFocusables[visibleFocusables.length - 1];\n }\n\n // Activate trapping\n catch() {\n const _ = this;\n\n _.lastFocusableBeforeCatch = document.activeElement;\n\n _.update();\n\n _.container.focus();\n _.container.addEventListener('keydown', _.handleKeydown);\n\n _.state = 'catching';\n }\n\n // Deactivate trapping\n release() {\n const _ = this;\n\n if (_.lastFocusableBeforeCatch) {\n _.lastFocusableBeforeCatch.focus();\n _.lastFocusableBeforeCatch = null;\n }\n\n _.container.removeEventListener('keydown', _.handleKeydown);\n\n _.state = 'resting';\n }\n\n // Detect if we need to loop forward or back\n handleKeydown(e) {\n const _ = this;\n\n if (e.which === 9) {\n if (e.shiftKey && (e.target === _.firstFocusable || e.target === _.container)) {\n e.preventDefault();\n _.lastFocusable.focus();\n }\n\n if (!e.shiftKey && e.target === _.lastFocusable) {\n e.preventDefault();\n _.firstFocusable.focus();\n }\n }\n }\n}\n","import extend from './extend';\n\n/**\n* TOGGLER\n*\n* Helps you to create a11y driven toggable (open / close) scripts.\n*/\nexport default class Toggler {\n constructor(elements, settings) {\n const _ = this;\n\n _.settings = extend(true, {\n // Set it to rue if you want only one item active at a time\n oneAtATime: false,\n // Set it to true if you want that a click outside of current element closes it\n outsideClose: false,\n // Set it to true if you want that escape key closes the current element\n escClose: false,\n // Callbacks methods for more control on wanted behaviors\n callbacks: {\n beforeOpen: () => { },\n afterOpen: () => { },\n beforeClose: () => { },\n afterClose: () => { },\n beforeAttach: () => { },\n afterAttach: () => { },\n beforeDetach: () => { },\n afterDetach: () => { },\n },\n }, settings || {});\n\n // Colection of items arranged like [content to toggle, togglers]\n _.items = [];\n // Detect non native togglers for adding a11y features\n _.nonNativeTogglers = [];\n // Current ID of open content\n _.currentId = null;\n // Global status, enabled : toggling is activated, disabled : toggling is deactivated\n _.status = 'disabled';\n\n // Parse received elements\n for (let i = 0; i < elements.length; i++) {\n const item = {\n content: elements[i][0],\n togglers: elements[i][1],\n nonNativeTogglers: [],\n };\n\n for (let j = 0; j < item.togglers.length; j++) {\n if (!item.togglers[j].matches('button, [href], [tabindex]:not([tabindex=\"-1\"])')) {\n _.nonNativeTogglers.push(item.togglers[j]);\n }\n }\n\n // Add it a custom id if they don't have one\n // https://gist.github.com/gordonbrander/2230317\n // In addition we always prefix the custom ID by the character i to avoid querySelector\n // issues, see : https://stackoverflow.com/questions/37270787/uncaught-syntaxerror-failed-to-execute-queryselector-on-document\n const id = item.content.hasAttribute('id') ? item.content.getAttribute('id') : `i${Math.random().toString(36).substr(2, 9)}`;\n\n item.content.setAttribute('id', id);\n item.id = id;\n\n _.items.push(item);\n }\n\n _.handleTogglerClick = _.handleTogglerClick.bind(_);\n _.handleNonNativeTogglersKeyup = _.handleNonNativeTogglersKeyup.bind(_);\n _.handleNonNativeTogglersKeydown = _.handleNonNativeTogglersKeydown.bind(_);\n _.handleOutsideClick = _.handleOutsideClick.bind(_);\n _.handleEscKey = _.handleEscKey.bind(_);\n }\n\n // Activate toggling\n attach() {\n const _ = this;\n\n _.settings.callbacks.beforeAttach(_);\n\n for (let i = 0; i < _.items.length; i++) {\n _.items[i].content.setAttribute('aria-hidden', true);\n for (let j = 0; j < _.items[i].togglers.length; j++) {\n _.items[i].togglers[j].setAttribute('aria-controls', _.items[i].id);\n _.items[i].togglers[j].setAttribute('aria-expanded', false);\n _.items[i].togglers[j].addEventListener('click', _.handleTogglerClick);\n }\n }\n\n if (_.nonNativeTogglers) {\n for (let i = 0; i < _.nonNativeTogglers.length; i++) {\n _.nonNativeTogglers[i].setAttribute('tabindex', '0');\n _.nonNativeTogglers[i].addEventListener('keyup', _.handleNonNativeTogglersKeyup);\n }\n\n document.addEventListener('keydown', _.handleNonNativeTogglersKeydown);\n }\n\n if (_.settings.outsideClose) {\n document.addEventListener('click', _.handleOutsideClick);\n }\n\n if (_.settings.escClose) {\n document.addEventListener('keyup', _.handleEscKey);\n }\n\n _.status = 'enabled';\n\n _.settings.callbacks.afterAttach(_);\n }\n\n // Deactivate toggling\n detach() {\n const _ = this;\n\n _.settings.callbacks.beforeDetach(_);\n\n for (let i = 0; i < _.items.length; i++) {\n _.items[i].content.removeAttribute('aria-hidden');\n for (let j = 0; j < _.items[i].togglers.length; j++) {\n _.items[i].togglers[j].removeAttribute('aria-controls');\n _.items[i].togglers[j].removeAttribute('aria-expanded');\n _.items[i].togglers[j].removeEventListener('click', _.handleTogglerClick);\n }\n }\n\n if (_.nonNativeTogglers) {\n for (let i = 0; i < _.nonNativeTogglers.length; i++) {\n _.nonNativeTogglers[i].removeAttribute('tabindex');\n _.nonNativeTogglers[i].removeEventListener('keyup', _.handleNonNativeTogglersKeyup);\n }\n\n document.removeEventListener('keydown', _.handleNonNativeTogglersKeydown);\n }\n\n if (_.settings.outsideClose) {\n document.removeEventListener('click', _.handleOutsideClick);\n }\n\n if (_.settings.escClose) {\n document.removeEventListener('keyup', _.handleEscKey);\n }\n\n _.status = 'disabled';\n\n _.settings.callbacks.afterDetach(_);\n }\n\n // Open or close\n toggle(id) {\n const _ = this;\n\n if (document.querySelector(`#${id}`).getAttribute('aria-hidden') === 'true') {\n _.open(id);\n } else {\n _.close(id);\n }\n }\n\n // Open content\n open(id) {\n const _ = this;\n\n if (_.settings.oneAtATime && _.currentId) {\n _.close(_.currentId);\n }\n\n const content = document.querySelector(`#${id}`);\n const togglers = document.querySelectorAll(`[aria-controls=\"${id}\"]`);\n\n _.settings.callbacks.beforeOpen(content, togglers);\n\n for (let i = 0; i < togglers.length; i++) {\n togglers[i].setAttribute('aria-expanded', true);\n }\n content.setAttribute('aria-hidden', false);\n\n _.currentId = id;\n\n _.settings.callbacks.afterOpen(content, togglers);\n }\n\n // Close content\n close(id) {\n const _ = this;\n\n const content = document.querySelector(`#${id}`);\n const togglers = document.querySelectorAll(`[aria-controls=\"${id}\"]`);\n\n _.settings.callbacks.beforeClose(content, togglers);\n\n for (let i = 0; i < togglers.length; i++) {\n togglers[i].setAttribute('aria-expanded', false);\n }\n content.setAttribute('aria-hidden', true);\n\n _.currentId = null;\n\n _.settings.callbacks.afterClose(content, togglers);\n }\n\n // Click on toggler\n handleTogglerClick(e) {\n const _ = this;\n\n e.preventDefault();\n\n _.toggle(e.currentTarget.getAttribute('aria-controls'));\n }\n\n // Prevent space key to scroll the page if we are on a non native toggler\n handleNonNativeTogglersKeydown(e) {\n const _ = this;\n\n if (_.nonNativeTogglers.includes(e.target) && e.which === 32) {\n e.preventDefault();\n }\n }\n\n // Toggle from a non native toggler\n handleNonNativeTogglersKeyup(e) {\n const _ = this;\n\n if (e.which === 32 || e.which === 13) {\n e.preventDefault();\n _.toggle(e.target.getAttribute('aria-controls'));\n }\n }\n\n // Close when clicking outside of current element\n handleOutsideClick(e) {\n const _ = this;\n\n if (!_.currentId) return;\n\n let currentItem;\n\n for (let i = 0; i < _.items.length; i++) {\n if (_.items[i].id === _.currentId) {\n currentItem = _.items[i];\n }\n }\n\n let clickOnContent = false;\n if (!currentItem.content.contains(e.target)) {\n clickOnContent = true;\n }\n\n let clickOnToggler = false;\n for (let i = 0; i < currentItem.togglers.length; i++) {\n if (!currentItem.togglers[i].contains(e.target)) {\n clickOnToggler = true;\n }\n }\n\n if (!clickOnContent && !clickOnToggler) {\n _.close(_.currentId);\n }\n }\n\n // Close when escape key is pressed\n handleEscKey(e) {\n const _ = this;\n\n if (_.currentId && e.which === 27) {\n _.close(_.currentId);\n }\n }\n}\n","import { throttle } from 'throttle-debounce';\nimport FocusTrapper from '../../utils/FocusTrapper';\nimport Toggler from '../../utils/Toggler';\nimport getCSSCustomProp from '../../utils/getCSSCustomProp';\n\nexport default class Header {\n constructor(root) {\n // Elements\n this.$root = root;\n this.$burger = this.$root.querySelectorAll('.js-header-burger');\n this.$navigation = this.$root.querySelector('.js-header-nav');\n this.$navigationInner = this.$root.querySelector('.js-header-nav-inner');\n this.$submenuRoots = this.$root.querySelectorAll('.js-header-submenu-root');\n\n // Throttled functions\n this.throttledWatchResize = throttle(250, this.watchResize.bind(this));\n\n // Variables\n this.nav2ColBP = getCSSCustomProp('--header-nav-2col-bp', undefined, 'int');\n this.navIsOn2Col = () => window.matchMedia(`(min-width: ${this.nav2ColBP}em)`).matches;\n\n // Focus trapper for navigation\n this.navFocusTrapper = new FocusTrapper(this.$navigationInner);\n\n // Togglers syndication\n this.togglers = {\n nav: null,\n submenus: null,\n };\n\n // Nav toggler\n this.togglers.nav = new Toggler([[this.$navigation, this.$burger]], {\n escClose: true,\n callbacks: {\n afterOpen: () => {\n this.navFocusTrapper.catch();\n\n document.body.classList.add('nav-is-open');\n document.body.classList.add('no-scroll');\n },\n afterClose: () => {\n this.navFocusTrapper.release();\n\n document.body.classList.remove('nav-is-open');\n document.body.classList.remove('no-scroll');\n\n // Ensure submenus or subrubrics are also closed\n if (this.togglers.submenus.currentId) {\n this.togglers.submenus.close(this.togglers.submenus.currentId);\n }\n },\n },\n });\n\n // Submenus toggler\n const submenuTogglerItems = [];\n\n for (let i = 0; i < this.$submenuRoots.length; i++) {\n submenuTogglerItems.push([\n this.$submenuRoots[i].querySelector('.js-header-submenu-menu'),\n this.$submenuRoots[i].querySelectorAll('.js-header-submenu-parent'),\n ]);\n }\n\n this.togglers.submenus = new Toggler(submenuTogglerItems, {\n oneAtATime: true,\n callbacks: {\n afterOpen: ($content) => {\n this.navFocusTrapper.update();\n document.body.classList.add('submenu-is-open');\n\n if (this.navIsOn2Col()) {\n this.$navigationInner.style.minHeight = `${$content.offsetHeight}px`;\n }\n },\n afterClose: () => {\n this.navFocusTrapper.update();\n\n document.body.classList.remove('submenu-is-open');\n\n this.$navigationInner.style.minHeight = '';\n },\n },\n });\n }\n\n mount() {\n this.togglers.nav.attach();\n this.togglers.submenus.attach();\n\n this.watchResize();\n\n window.addEventListener('resize', this.throttledWatchResize);\n }\n\n unmount() {\n this.togglers.nav.detach();\n this.togglers.submenus.detach();\n\n window.removeEventListener('resize', this.throttledWatchResize);\n }\n\n watchResize() {\n if (this.navIsOn2Col()) {\n if (this.togglers.submenus.currentId) {\n this.$navigationInner.style.minHeight = `${document.querySelector(`#${this.togglers.submenus.currentId}`).offsetHeight}px`;\n }\n } else {\n this.$navigationInner.style.minHeight = '';\n }\n }\n}\n","/* eslint-disable no-undefined,no-param-reassign,no-shadow */\n\n/**\n * Throttle execution of a function. Especially useful for rate limiting\n * execution of handlers on events like resize and scroll.\n *\n * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.\n * @param {boolean} [noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds while the\n * throttled-function is being called. If noTrailing is false or unspecified, callback will be executed one final time\n * after the last throttled-function call. (After the throttled-function has not been called for `delay` milliseconds,\n * the internal counter is reset).\n * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,\n * to `callback` when the throttled-function is executed.\n * @param {boolean} [debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end),\n * schedule `callback` to execute after `delay` ms.\n *\n * @returns {Function} A new, throttled, function.\n */\nfunction throttle (delay, noTrailing, callback, debounceMode) {\n /*\n * After wrapper has stopped being called, this timeout ensures that\n * `callback` is executed at the proper times in `throttle` and `end`\n * debounce modes.\n */\n var timeoutID;\n var cancelled = false; // Keep track of the last time `callback` was executed.\n\n var lastExec = 0; // Function to clear existing timeout\n\n function clearExistingTimeout() {\n if (timeoutID) {\n clearTimeout(timeoutID);\n }\n } // Function to cancel next exec\n\n\n function cancel() {\n clearExistingTimeout();\n cancelled = true;\n } // `noTrailing` defaults to falsy.\n\n\n if (typeof noTrailing !== 'boolean') {\n debounceMode = callback;\n callback = noTrailing;\n noTrailing = undefined;\n }\n /*\n * The `wrapper` function encapsulates all of the throttling / debouncing\n * functionality and when executed will limit the rate at which `callback`\n * is executed.\n */\n\n\n function wrapper() {\n for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {\n arguments_[_key] = arguments[_key];\n }\n\n var self = this;\n var elapsed = Date.now() - lastExec;\n\n if (cancelled) {\n return;\n } // Execute `callback` and update the `lastExec` timestamp.\n\n\n function exec() {\n lastExec = Date.now();\n callback.apply(self, arguments_);\n }\n /*\n * If `debounceMode` is true (at begin) this is used to clear the flag\n * to allow future `callback` executions.\n */\n\n\n function clear() {\n timeoutID = undefined;\n }\n\n if (debounceMode && !timeoutID) {\n /*\n * Since `wrapper` is being called for the first time and\n * `debounceMode` is true (at begin), execute `callback`.\n */\n exec();\n }\n\n clearExistingTimeout();\n\n if (debounceMode === undefined && elapsed > delay) {\n /*\n * In throttle mode, if `delay` time has been exceeded, execute\n * `callback`.\n */\n exec();\n } else if (noTrailing !== true) {\n /*\n * In trailing throttle mode, since `delay` time has not been\n * exceeded, schedule `callback` to execute `delay` ms after most\n * recent execution.\n *\n * If `debounceMode` is true (at begin), schedule `clear` to execute\n * after `delay` ms.\n *\n * If `debounceMode` is false (at end), schedule `callback` to\n * execute after `delay` ms.\n */\n timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);\n }\n }\n\n wrapper.cancel = cancel; // Return the wrapper function.\n\n return wrapper;\n}\n\n/* eslint-disable no-undefined */\n/**\n * Debounce execution of a function. Debouncing, unlike throttling,\n * guarantees that a function is only executed a single time, either at the\n * very beginning of a series of calls, or at the very end.\n *\n * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.\n * @param {boolean} [atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds\n * after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.\n * (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).\n * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,\n * to `callback` when the debounced-function is executed.\n *\n * @returns {Function} A new, debounced function.\n */\n\nfunction debounce (delay, atBegin, callback) {\n return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false);\n}\n\nexport { debounce, throttle };\n//# sourceMappingURL=index.js.map\n","/**\n * Pass in an element and its CSS Custom Property that you want the value of.\n * Optionally, you can determine what datatype you get back.\n *\n * Credit : https://piccalil.li/tutorial/get-css-custom-property-value-with-javascript/\n *\n * @param {String} propKey\n * @param {HTMLELement} element=document.documentElement\n * @param {String} castAs='string'\n * @returns {*}\n */\nexport default function(propKey, element = document.documentElement, castAs = 'string') {\n let response = getComputedStyle(element).getPropertyValue(propKey);\n\n // Tidy up the string if there's something to work with\n if (response.length) {\n response = response.replace(/\\'|\"/g, '').trim();\n }\n\n // Convert the response into a whatever type we wanted\n switch (castAs) {\n case 'number':\n case 'int':\n return parseInt(response, 10);\n case 'float':\n return parseFloat(response, 10);\n case 'boolean':\n case 'bool':\n return response === 'true' || response === '1';\n }\n\n // Return the string response by default\n return response;\n};\n","import Header from '../components/Header';\n\nconst $header = document.querySelector('.js-header');\n\nif ($header) {\n const header = new Header($header);\n header.mount();\n}\n","import arrayAreIdenticals from '../../../utils/arrayAreIdenticals';\nimport extend from '../../../utils/extend';\nimport getFragmentFromString from '../../../utils/getFragmentFromString';\n\nexport default class Field {\n constructor($element, options) {\n this.$element = $element;\n\n this.options = extend({\n errorTemplate: (text, id) => `\n
${text}
\n