{"version":3,"file":"js/modal-conversion.bundle.min.js","mappings":"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","sources":["webpack://sciencenews/./node_modules/js-component-framework/es/domContentLoaded.js","webpack://sciencenews/./node_modules/aria-components/src/AriaComponent.js","webpack://sciencenews/./node_modules/aria-components/src/lib/keyCodes.js","webpack://sciencenews/./node_modules/aria-components/src/lib/rovingTabIndex.js","webpack://sciencenews/./client/src/js/utils/ProgrammaticDialog.js","webpack://sciencenews/./node_modules/aria-components/src/lib/interactiveChildren.js","webpack://sciencenews/./node_modules/aria-components/src/lib/getFirstAndLastItems.js","webpack://sciencenews/./client/src/js/utils/toArray.js","webpack://sciencenews/./node_modules/aria-components/src/lib/uniqueId.js","webpack://sciencenews/./node_modules/js-component-framework/es/componentProvider.js","webpack://sciencenews/./node_modules/js-component-framework/es/componentLoader.js","webpack://sciencenews/./client/src/components/modal-conversion/index.js","webpack://sciencenews/./client/src/components/modal-conversion/modal-conversion.js"],"sourcesContent":["/* eslint consistent-return: [\"error\", { \"treatUndefinedAsUnspecified\": true }] */\n\n/**\n * Executes the given callback when DOMContentLoaded is ready.\n *\n * @param {function} callback Callback to execute once DOMContentLoaded completes.\n */\nfunction domContentLoaded(callback) {\n if (document.readyState === 'complete' || document.readyState === 'interactive') {\n return void callback(); // eslint-disable-line no-void\n }\n\n document.addEventListener('DOMContentLoaded', callback, {\n once: true\n });\n}\nexport default domContentLoaded;","/**\n * Class for facilitating accessible components.\n */\nexport default class AriaComponent {\n /**\n * Create an AriaComponent.\n * @constructor\n */\n constructor() {\n /**\n * Component state.\n *\n * @type {object}\n */\n this.state = {};\n\n /**\n * Save search characters\n *\n * @type {string}\n */\n this.searchString = '';\n\n /**\n * Saved reference elements.\n *\n * @type {Array}\n */\n this.referenceElements = [];\n\n // Bind class methods.\n this.setState = this.setState.bind(this);\n this.getState = this.getState.bind(this);\n this.setSelfReference = this.setSelfReference.bind(this);\n this.warnDeprecated = this.warnDeprecated.bind(this);\n }\n\n /**\n * Set component state.\n *\n * @param {object} newState The new state to merge with existing state.\n */\n setState(newState) {\n Object.assign(this.state, newState);\n\n if ('function' === typeof this.stateWasUpdated) {\n this.stateWasUpdated();\n }\n }\n\n /**\n * Set a reference to the class instance on the element upon which the class\n * is instantiated.\n *\n * @param {array} elements An array of elements upon which to add a reference to `this`.\n */\n setSelfReference(elements) {\n const referenceElements = [...elements].map((element) => {\n Object.defineProperty(\n element,\n this.componentName.toLowerCase(),\n { value: this, configurable: true }\n );\n\n return element;\n });\n\n this.referenceElements = [...this.referenceElements, ...referenceElements];\n }\n\n /**\n * Delete self references from component elements.\n */\n deleteSelfReferences() {\n this.referenceElements.forEach((element) => {\n delete element[this.componentName.toLowerCase()];\n });\n }\n\n /**\n * Return the current component state.\n *\n * @return {object}\n */\n getState() {\n return this.state;\n }\n\n /**\n * Warn about deprecated config properties.\n *\n * @param {string} name The name of the class instance.\n * @param {string} unsupported The deprecated config value.\n * @param {string} supported The newly supported config value, if any.\n */\n warnDeprecated(unsupported, supported = false) {\n const use = supported ? `Use ${supported} instead.` : '';\n // eslint-disable-next-line no-console, max-len\n console.warn(\n `${this.componentName}:`,\n `${unsupported} is deprecated.`,\n `${use}`\n );\n }\n}\n","/**\n * Event keyCode values mapped to a key name.\n */\nconst keyCodes = Object.freeze({\n TAB: 9,\n RETURN: 13,\n ESC: 27,\n SPACE: 32,\n END: 35,\n HOME: 36,\n LEFT: 37,\n UP: 38,\n RIGHT: 39,\n DOWN: 40,\n});\n\nexport default keyCodes;\n","/**\n * Create an array from a value.\n *\n * @param {Mixed} maybeAnArray An HTMLElement, NodeList, or Array of elements.\n */\nfunction toArray(maybeAnArray) {\n if (Array.isArray(maybeAnArray)) {\n return maybeAnArray;\n }\n\n let shouldBeAnArray = [];\n\n if (maybeAnArray instanceof HTMLElement) {\n // Convert element(s) to an Array.\n shouldBeAnArray = new Array(maybeAnArray);\n } else if (\n maybeAnArray instanceof NodeList\n || maybeAnArray instanceof HTMLCollection\n ) {\n // Array.from(maybeAnArray);\n shouldBeAnArray = Array.prototype.slice.call(maybeAnArray, 0);\n }\n\n return shouldBeAnArray;\n}\n\n/**\n * Remove the tabIndex attribute from all elements.\n *\n * @param {Mixed} items An HTMLElement, NodeList, or array of elements.\n */\nfunction tabIndexAllow(items) {\n const allowedElements = Array.isArray(items) ? items : toArray(items);\n\n allowedElements.forEach((item) => {\n item.removeAttribute('tabindex');\n });\n}\n\n/**\n * Add a negative tabIndex attribute to all elements.\n *\n * @param {Mixed} items An HTMLElement, NodeList, or Array of elements.\n */\nfunction tabIndexDeny(items) {\n const deniedElements = Array.isArray(items) ? items : toArray(items);\n\n deniedElements.forEach((item) => {\n item.setAttribute('tabindex', '-1');\n });\n}\n\n/**\n * Manage items' tabindex.\n *\n * @param {NodeList|Array} items The items whose tabindex may need updating.\n * @param {HTMLElement|NodeList|Array} allow The item to which we'll allow tabbing.\n */\nfunction rovingTabIndex(items, allow) {\n const allowedElements = Array.isArray(allow) ? allow : toArray(allow);\n const allItems = Array.isArray(items) ? items : toArray(items);\n\n tabIndexAllow(allowedElements);\n\n if (0 < allItems.length) {\n const deniedElements = allItems.filter((item) => (\n ! allowedElements.includes(item)\n ));\n tabIndexDeny(deniedElements);\n }\n}\n\nexport {\n rovingTabIndex,\n tabIndexAllow,\n tabIndexDeny,\n toArray,\n};\n","import { AriaComponent } from 'aria-components';\nimport {\n interactiveChildren,\n keyCodes,\n getFirstAndLastItems,\n tabIndexDeny,\n tabIndexAllow,\n setUniqueId,\n} from 'aria-components/utils';\nimport toArray from 'js/utils/toArray';\n\n/**\n * Class to set up a programmatic Dialog element to be displayed without user interaction.\n */\nexport default class ProgrammaticDialog extends AriaComponent {\n /**\n * Create a Dialog.\n * @constructor\n *\n * @param {object} config The config object.\n */\n constructor(target, config) {\n super(config);\n\n // A target element is required.\n if (null == target) {\n throw new Error('Configuration error: A target element is required.');\n }\n\n /**\n * The dialog element.\n *\n * @type {HTMLElement}\n */\n this.target = target;\n\n /**\n * The component name.\n *\n * @type {string}\n */\n this.componentName = 'ProgrammaticDialog';\n\n /**\n * Options shape.\n *\n * @type {object}\n */\n const options = {\n /**\n * The element(s) to be hidden when the Dialog is visible. The elements\n * wrapping all site content with the sole exception of the dialog element.\n *\n * @type {HTMLElement|NodeList|Array}\n */\n content: [],\n\n /**\n * Callback to run after the component initializes.\n *\n * @callback initCallback\n */\n onInit: () => {},\n\n /**\n * Callback to run after component state is updated.\n *\n * @callback stateChangeCallback\n */\n onStateChange: () => {},\n\n /**\n * Callback to run after the component is destroyed.\n *\n * @callback destroyCallback\n */\n onDestroy: () => {},\n };\n\n // Merge config options with defaults and save all as instance properties.\n Object.assign(this, options, config);\n\n // Bind class methods\n this.init = this.init.bind(this);\n this.handleTargetKeydown = this.handleTargetKeydown.bind(this);\n this.hideOnOutsideClick = this.hideOnOutsideClick.bind(this);\n this.handleKeydownEsc = this.handleKeydownEsc.bind(this);\n this.show = this.show.bind(this);\n this.hide = this.hide.bind(this);\n this.destroy = this.destroy.bind(this);\n this.stateWasUpdated = this.stateWasUpdated.bind(this);\n this.setInteractiveChildren = this.setInteractiveChildren.bind(this);\n this.setDialogController = this.setDialogController.bind(this);\n this.controllerClick = this.controllerClick.bind(this);\n\n /*\n * Initialize the component.\n */\n this.init();\n }\n\n /**\n * Set the Dialog controller.\n *\n * By default, the Dialog will not have a controller; use this method on an\n * instance to add a controller.\n */\n setDialogController(controller) {\n this.controller = controller;\n super.setSelfReference([this.controller]);\n\n /**\n * Check if the controller is a button, but only if it doesn't already have\n * a role attribute, since we'll be adding the role and allowing focus.\n *\n * @type {bool}\n */\n this.controllerIsNotAButton = (\n 'BUTTON' !== this.controller.nodeName\n && null === this.controller.getAttribute('role')\n );\n\n /*\n * Use the button role on non-button elements.\n */\n if (this.controllerIsNotAButton) {\n // https://www.w3.org/TR/wai-aria-1.1/#button\n this.controller.setAttribute('role', 'button');\n this.controller.setAttribute('tabindex', '0');\n }\n\n this.controller.setAttribute('aria-expanded', 'false');\n\n this.controller.addEventListener('click', this.controllerClick);\n this.controller.addEventListener('keydown', this.controllerKeydown);\n }\n\n /**\n * Collect the Dialog's interactive child elements.\n */\n setInteractiveChildren() {\n this.interactiveChildElements = interactiveChildren(this.target);\n\n const [\n firstInteractiveChild,\n lastInteractiveChild,\n ] = getFirstAndLastItems(this.interactiveChildElements);\n\n // Save as instance properties.\n this.firstInteractiveChild = firstInteractiveChild;\n this.lastInteractiveChild = lastInteractiveChild;\n }\n\n /**\n * Set the component's DOM attributes and event listeners.\n */\n init() {\n // Get the content items if none are provided.\n if (0 === this.content.length || null == this.content) {\n this.content = Array.from(document.body.children)\n .filter((child) => ! child.contains(this.target));\n } else {\n this.content = toArray(this.content);\n }\n\n // If no content is found.\n if (0 === this.content.length) {\n // eslint-disable-next-line max-len\n throw new Error('Configuration error: The Dialog target should not be within the main site content.');\n }\n\n /*\n * A reference to the class instance added to the target element to enable\n * external interactions with this instance.\n */\n super.setSelfReference([this.target]);\n\n /*\n * Collect the Dialog's interactive child elements. This is an initial pass\n * to ensure values exists, but the interactive children will be collected\n * each time the dialog opens, in case the dialog's contents change.\n */\n this.setInteractiveChildren();\n\n // Focusable content should initially have tabindex='-1'.\n tabIndexDeny(this.interactiveChildElements);\n\n // Add target attribute.\n setUniqueId(this.target);\n\n /*\n * Set the taget as hidden by default. Using the `aria-hidden` attribute,\n * rather than the `hidden` attribute, means authors must hide the target\n * element via CSS.\n */\n this.target.setAttribute('aria-hidden', 'true');\n this.target.setAttribute('hidden', '');\n\n // Set additional attributes.\n this.target.setAttribute('role', 'dialog');\n this.target.setAttribute('aria-modal', 'true');\n\n // Add event listeners.\n this.target.addEventListener('keydown', this.handleTargetKeydown);\n\n /**\n * Set initial state.\n *\n * @type {object}\n */\n this.state = { expanded: false };\n\n /* Run {initCallback} */\n this.onInit.call(this);\n }\n\n /**\n * Update element attributes and event listeners when the Popup's state changes.\n * @todo Set target attributes and update roving tabindex.\n *\n * @param {Object} state The component state.\n */\n stateWasUpdated() {\n const { expanded } = this.state;\n const contentLength = this.content.length;\n\n this.setInteractiveChildren();\n\n if (expanded) {\n // Allow focus on the target element.\n this.target.setAttribute('tabindex', '0');\n\n // Hide the outside content.\n for (let i = 0; i < contentLength; i += 1) {\n this.content[i].setAttribute('aria-hidden', 'true');\n }\n\n // Update target element.\n this.target.setAttribute('aria-hidden', 'false');\n this.target.removeAttribute('hidden');\n\n if (null != this.controller) {\n this.controller.setAttribute('aria-expanded', 'true');\n }\n\n tabIndexAllow(this.interactiveChildElements);\n\n this.target.focus();\n } else {\n // Disallow focus on the target element.\n this.target.setAttribute('tabindex', '-1');\n\n // Un-hide the outside content.\n for (let i = 0; i < contentLength; i += 1) {\n this.content[i].removeAttribute('aria-hidden');\n }\n\n // Update target element.\n this.target.setAttribute('aria-hidden', 'true');\n this.target.setAttribute('hidden', '');\n\n // Focusable content should have tabindex='-1' or be removed from the DOM.\n tabIndexDeny(this.interactiveChildElements);\n\n if (null != this.controller) {\n this.controller.setAttribute('aria-expanded', 'false');\n this.controller.focus();\n }\n }\n\n /* Run {stateChangeCallback} */\n this.onStateChange.call(this, this.state);\n }\n\n /**\n * Close the dialog on when users click outside of the Dialog element.\n *\n * By default, the Dialog will not close on outside click. This is by design,\n * but can be enabled manually by including an event listener with this method\n * as the callback.\n * document.body.addEventListener('click', dialog.hideOnOutsideClick)\n *\n * @param {Event} event The Event object.\n */\n hideOnOutsideClick(event) {\n const { expanded } = this.state;\n const { target: clicked } = event;\n\n if (\n expanded && ! this.target.contains(clicked)\n && (null != this.controller && ! this.controller.contains(clicked))\n ) {\n this.hide();\n }\n }\n\n /**\n * Close the dialog on ESC key press.\n *\n * By default, the Dialog will not close with the ESC key. This is by design,\n * but can be enabled manually by including an event listener with this method\n * as the callback.\n * document.body.addEventListener('keydown', dialog.handleKeydownEsc)\n *\n * @param {Event} event The Event object.\n */\n handleKeydownEsc(event) {\n const { ESC } = keyCodes;\n const { keyCode } = event;\n\n if (ESC === keyCode) {\n this.hide();\n }\n }\n\n /**\n * Trap key tabs within the dialog.\n *\n * @param {Event} event The Event object.\n */\n handleTargetKeydown(event) {\n const { TAB } = keyCodes;\n const { keyCode, shiftKey } = event;\n const { expanded } = this.state;\n\n if (expanded && keyCode === TAB) {\n const { activeElement } = document;\n\n if (shiftKey && this.firstInteractiveChild === activeElement) {\n event.preventDefault();\n /*\n * Move back from the first interactive child element to the last\n * interactive child element\n */\n this.lastInteractiveChild.focus();\n } else if (! shiftKey && this.lastInteractiveChild === activeElement) {\n event.preventDefault();\n /*\n * Move forward from the last interactive child element to the first\n * interactive child element.\n */\n this.firstInteractiveChild.focus();\n }\n }\n }\n\n /**\n * Handle controller keydown events to patch in button behaviors for\n * non-button elements.\n *\n * @param {Event} event The event object.\n */\n controllerKeydown(event) {\n const {\n SPACE,\n RETURN,\n } = keyCodes;\n const { keyCode } = event;\n\n if ([SPACE, RETURN].includes(keyCode)) {\n event.preventDefault();\n\n /*\n * Treat the Spacebar and Return keys as clicks in case the controller is\n * not a