{"version":3,"sources":["webpack:///./app/javascript/Snackbar/index.js","webpack:///./app/javascript/common-prop-types/default-children-prop-types.js","webpack:///./app/javascript/shared/components/useKeyboardShortcuts.js","webpack:///./app/javascript/common-prop-types/article-prop-types.js","webpack:///./app/javascript/crayons/Button/index.js","webpack:///./app/javascript/crayons/Button/Button.jsx","webpack:///./app/javascript/crayons/Spinner/Spinner.jsx","webpack:///./app/javascript/crayons/Modal/Modal.jsx","webpack:///./app/javascript/shared/components/focusTrap.jsx","webpack:///./app/javascript/crayons/ButtonGroup/ButtonGroup.jsx","webpack:///./app/javascript/crayons/Dropdown/Dropdown.jsx","webpack:///./app/javascript/crayons/formElements/FormField/FormField.jsx","webpack:///./app/javascript/crayons/formElements/RadioButton/RadioButton.jsx","webpack:///./app/javascript/Snackbar/SnackbarItem.jsx","webpack:///./app/javascript/Snackbar/Snackbar.jsx","webpack:///./app/javascript/utilities/viewport.js","webpack:///./app/javascript/utilities/debounceAction.js","webpack:///./app/javascript/common-prop-types/user-prop-types.js","webpack:///./app/javascript/common-prop-types/selected-tags-prop-types.js","webpack:///./app/javascript/utilities/gist.js","webpack:///./app/javascript/utilities/dropdownUtils.js","webpack:///./app/javascript/packs/articlePage.jsx","webpack:///./app/javascript/common-prop-types/organization-prop-type.js","webpack:///./app/javascript/common-prop-types/tag-prop-types.js","webpack:///./app/javascript/utilities/codeFullscreenModeSwitcher.js"],"names":["defaultChildrenPropTypes","PropTypes","oneOfType","arrayOf","node","isFormField","element","HTMLElement","name","nodeName","toLowerCase","type","getAttribute","isContentEditable","callShortcut","e","keys","chain","shortcuts","shortcut","length","join","code","key","defaultOptions","timeout","useKeyboardShortcuts","eventTarget","window","options","useState","storedShortcuts","keyChain","setKeyChain","mergedOptions","setMergedOptions","useEffect","newOptions","setTimeout","clearTimeout","Object","keyEvent","defaultPrevented","ctrlKey","metaKey","altKey","shiftKey","target","Node","newChain","addEventListener","removeEventListener","KeyboardShortcuts","propTypes","object","isRequired","shape","number","instanceOf","Element","defaultProps","articleSnippetResultPropTypes","body_text","string","articlePropTypes","id","title","description","processed_html","path","cloudinary_video_url","video_duration_in_minutes","type_of","oneOf","class_name","flare_tag","tagPropTypes","tag_list","cached_tag_list_array","podcast","slug","image_url","user_id","user","username","organization","organizationPropType","highlight","public_reactions_count","reactions_count","comments_count","reading_time","getAdditionalClassNames","variant","className","contentType","size","inverted","disabled","additionalClassNames","Button","props","children","tagName","icon","url","buttonType","onClick","onMouseOver","onMouseOut","onFocus","onBlur","tabIndex","restOfProps","ComponentName","Icon","otherProps","href","undefined","displayName","bool","func","Spinner","width","height","viewBox","fill","xmlns","d","CloseIcon","role","Modal","overlay","onClose","closeOnClickOutside","focusTrapSelector","onDeactivate","clickOutsideDeactivates","selector","FocusTrap","focusTrap","useRef","deactivate","useCallback","useLayoutEffect","currentLocationHref","document","location","routeChangeObserver","MutationObserver","mutations","some","current","disconnect","createFocusTrap","escapeDeactivates","activate","observe","querySelector","childList","escape","ButtonGroup","Dropdown","FormField","RadioButton","value","checked","snackbarItemProps","actions","message","handler","lifespan","SnackbarItem","map","text","snackbarItems","addSnackbarItem","snackbarItem","Array","isArray","push","Snackbar","snacks","this","initializePolling","pauseLifespan","_event","paused","resumeLifespan","event","stopPropagation","pollingTime","pollingId","setInterval","newSnacks","updateSnackbarItems","forEach","snack","lifespanTimeoutId","decreaseLifespan","addCloseButton","setState","prevState","filter","potentialSnackToFilterOut","updatedSnacks","slice","currentSnack","state","ref","index","Component","isInViewport","offsetTop","allowPartialVisibility","boundingRect","getBoundingClientRect","clientHeight","innerHeight","documentElement","clientWidth","innerWidth","topIsInViewport","top","rightIsInViewport","right","bottomIsInViewport","bottom","leftIsInViewport","left","topIsOutOfViewport","bottomIsOutOfViewport","debounceAction","action","time","config","leading","configs","debounce","userPropTypes","profile_image_url","summary","selectedTagsPropTypes","tags","onKeyPress","postscribeImport","getPostScribe","postscribe","getGistTags","nodes","gistNodes","nodeType","classList","contains","querySelectorAll","loadEmbeddedGists","gistTags","gistTag","firstElementChild","outerHTML","beforeWrite","childElementCount","watchForGistTagInsertion","targetNode","observer","mutationsList","addedNodes","attributes","subtree","InstantClick","on","INTERACTIVE_ELEMENTS_QUERY","closeDropdown","triggerElementId","dropdownContentId","dropdownContent","getElementById","setAttribute","style","removeProperty","fullscreenActionElements","getElementsByClassName","addFullScreenModeControl","snackZone","render","shareDropdownButton","dataset","initialized","Runtime","isNativeAndroid","AndroidBridge","shareText","dropdownContentCloseButtonId","onOpen","triggerButton","keyUpListener","onCloseCleanupActions","focus","activeElement","clickOutsideListener","matches","display","openDropdown","initializeDropdown","hidden","link","ahoy","track","option","trim","inputValue","copyToClipboard","then","input","localName","setSelectionRange","getCsrfToken","body","userStatus","root","isLoggedIn","getCommentSubscriptionStatus","setCommentSubscriptionStatus","CommentSubscription","articleId","subscriptionType","subscriptionRequestHandler","positionType","onSubscribe","onUnsubscribe","toggleArticlePin","button","isPinButton","method","JSON","stringify","fetch","headers","Accept","csrfToken","credentials","ok","innerHTML","actionsContainer","pinTargets","includes","embedGists","profile_image_90","hotness_score","points","bg_color_hex","text_color_hex","isFullScreenModeCodeOn","screenScroll","fullScreenWindow","onPressEscape","fullScreenModeControl","listenToKeyboardForEscape","listen","toggleOverflowForDocument","overflow","elements","codeBlock","currentTarget","closest","cloneNode","codeBlockControls","scrollTo","removeFullScreenModeControl","remove","removeChild","childNodes","scrollY","add","appendChild"],"mappings":"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","file":"js/articlePage-b5eb20bd15ec96b65e1b.chunk.js","sourcesContent":["export * from './Snackbar';\nexport * from './SnackbarItem';\n","import PropTypes from 'prop-types';\n\n// Use this whenever you need the standard children prop.\nexport const defaultChildrenPropTypes = PropTypes.oneOfType([\n PropTypes.arrayOf(PropTypes.node),\n PropTypes.node,\n]);\n","import { useState, useEffect } from 'preact/hooks';\nimport PropTypes from 'prop-types';\n\n/**\n * Checker that return true if element is a form element\n *\n * @param {node} element to be checked\n *\n * @returns {boolean} isFormField\n */\nfunction isFormField(element) {\n if (element instanceof HTMLElement === false) return false;\n\n const name = element.nodeName.toLowerCase();\n const type = (element.getAttribute('type') || '').toLowerCase();\n return (\n name === 'select' ||\n name === 'textarea' ||\n (name === 'input' &&\n type !== 'submit' &&\n type !== 'reset' &&\n type !== 'checkbox' &&\n type !== 'radio') ||\n element.isContentEditable\n );\n}\n\n/**\n * Function to handle converting key presses to callback functions\n *\n * @param {KeyboardEvent} e Keyboard event\n * @param {String} keys special keys formatted in a string\n * @param {Array} chain array of past keys\n * @param {Object} shortcuts object containing callback functions\n *\n * @returns {Array} New chain\n */\nconst callShortcut = (e, keys, chain, shortcuts) => {\n const shortcut =\n chain && chain.length > 0\n ? shortcuts[`${chain.join('~')}~${e.code}`]\n : shortcuts[`${keys}${e.code}`] ||\n shortcuts[`${keys}${e.key.toLowerCase()}`];\n\n // if a valid shortcut is found call it and reset the chain\n if (shortcut) {\n shortcut(e);\n return [];\n }\n\n // if we have keys don't add to the chain\n if (keys || e.key === 'Shift') {\n return [];\n }\n\n return [...chain, e.code];\n};\n\n// Default options to be used if null\nconst defaultOptions = {\n timeout: 0, // The default is zero as we want no delays between keystrokes by default.\n};\n\n/**\n * hook that can be added to a component to listen\n * for keyboard presses\n *\n * @example\n * const shortcuts = {\n * 'ctrl+alt+KeyG': (e) => {\n * e.preventDefault();\n * alert('Control Alt G has been pressed');\n * },\n * 'KeyG~KeyH': (e) => {\n * e.preventDefault();\n * alert('G has been pressed quickly followed by H');\n * },\n * '?': (e) => {\n * setIsHelpVisible(true);\n * }\n * }\n *\n * useKeyboardShortcuts(shortcuts, someElementOrWindowObject, {timeout: 1500});\n *\n * @param {object} shortcuts List of keyboard shortcuts/event\n * @param {EventTarget} [eventTarget=window] An event target.\n * @param {object} [options = {}] An object for extra options\n *\n */\nexport function useKeyboardShortcuts(\n shortcuts,\n eventTarget = window,\n options = {},\n) {\n const [storedShortcuts] = useState(shortcuts);\n const [keyChain, setKeyChain] = useState([]);\n const [mergedOptions, setMergedOptions] = useState({\n ...defaultOptions,\n ...options,\n });\n\n // update mergedOptions if options prop changes\n useEffect(() => {\n const newOptions = {};\n if (typeof options.timeout === 'number')\n newOptions.timeout = options.timeout;\n setMergedOptions({ ...defaultOptions, ...newOptions });\n }, [options.timeout]);\n\n // clear key chain after timeout is reached\n useEffect(() => {\n if (keyChain.length <= 0) return;\n\n const timeout = window.setTimeout(() => {\n clearTimeout(timeout);\n setKeyChain([]);\n }, mergedOptions.timeout);\n\n return () => clearTimeout(timeout);\n }, [keyChain.length, mergedOptions.timeout]);\n\n // set up event listeners\n useEffect(() => {\n if (!storedShortcuts || Object.keys(storedShortcuts).length === 0) return;\n\n const keyEvent = (e) => {\n if (e.defaultPrevented) return;\n\n // Get special keys\n const keys = `${e.ctrlKey || e.metaKey ? 'ctrl+' : ''}${\n e.altKey ? 'alt+' : ''\n }${(e.ctrlKey || e.metaKey || e.altKey) && e.shiftKey ? 'shift+' : ''}`;\n\n // If no special keys, except shift, are pressed and focus is inside a field return\n if (e.target instanceof Node && isFormField(e.target) && !keys) return;\n\n const newChain = callShortcut(e, keys, keyChain, storedShortcuts);\n\n // update keychain with latest chain\n setKeyChain(newChain);\n };\n\n eventTarget.addEventListener('keydown', keyEvent);\n\n return () => eventTarget.removeEventListener('keydown', keyEvent);\n }, [keyChain, storedShortcuts, eventTarget]);\n}\n\n/**\n * A component that can be added to a component to listen\n * for keyboard presses using the useKeyboardShortcuts hook\n *\n * @example\n * const shortcuts = {\n * 'ctrl+alt+KeyG': (e) => {\n * e.preventDefault();\n * alert('Control Alt G has been pressed')\n * }\n * }\n *\n * \n * \n *\n * @param {object} shortcuts List of keyboard shortcuts/event\n * @param {EventTarget} [eventTarget=window] An event target.\n * @param {object} [options = {}] An object for extra options\n *\n */\nexport function KeyboardShortcuts({ shortcuts, eventTarget, options }) {\n useKeyboardShortcuts(shortcuts, eventTarget, options);\n\n return null;\n}\n\nKeyboardShortcuts.propTypes = {\n shortcuts: PropTypes.object.isRequired,\n options: PropTypes.shape({\n timeout: PropTypes.number,\n }),\n eventTarget: PropTypes.instanceOf(Element),\n};\n\nKeyboardShortcuts.defaultProps = {\n shortcuts: {},\n options: {},\n eventTarget: window,\n};\n","import PropTypes from 'prop-types';\nimport { tagPropTypes } from './tag-prop-types';\nimport { organizationPropType } from './organization-prop-type';\n\nexport const articleSnippetResultPropTypes = PropTypes.shape({\n body_text: PropTypes.arrayOf(PropTypes.string),\n});\n\nexport const articlePropTypes = PropTypes.shape({\n id: PropTypes.number.isRequired,\n title: PropTypes.string.isRequired,\n description: PropTypes.string.isRequired,\n processed_html: PropTypes.string.isRequired,\n path: PropTypes.string.isRequired,\n cloudinary_video_url: PropTypes.string,\n video_duration_in_minutes: PropTypes.number,\n type_of: PropTypes.oneOf(['podcast_episodes']),\n class_name: PropTypes.oneOf(['PodcastEpisode', 'User', 'Article']),\n flare_tag: tagPropTypes,\n tag_list: PropTypes.arrayOf(PropTypes.string),\n cached_tag_list_array: PropTypes.arrayOf(PropTypes.string),\n podcast: PropTypes.shape({\n slug: PropTypes.string.isRequired,\n title: PropTypes.string.isRequired,\n image_url: PropTypes.string.isRequired,\n }),\n user_id: PropTypes.string.isRequired,\n user: PropTypes.shape({\n username: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n }),\n organization: organizationPropType,\n highlight: articleSnippetResultPropTypes,\n public_reactions_count: PropTypes.number,\n reactions_count: PropTypes.number,\n comments_count: PropTypes.number,\n reading_time: PropTypes.number,\n});\n","export * from './Button';\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\n\nfunction getAdditionalClassNames({\n variant,\n className,\n contentType,\n size,\n inverted,\n disabled,\n}) {\n let additionalClassNames = '';\n\n if (variant && variant.length > 0 && variant !== 'primary') {\n additionalClassNames += ` crayons-btn--${variant}`;\n }\n\n if (size && size.length > 0 && size !== 'default') {\n additionalClassNames += ` crayons-btn--${size}`;\n }\n\n if (contentType && contentType.length > 0 && contentType !== 'text') {\n additionalClassNames += ` crayons-btn--${contentType}`;\n }\n\n if (disabled) {\n additionalClassNames += ' crayons-btn--disabled';\n }\n\n if (inverted) {\n additionalClassNames += ' crayons-btn--inverted';\n }\n\n if (className && className.length > 0) {\n additionalClassNames += ` ${className}`;\n }\n\n return additionalClassNames;\n}\n\nexport const Button = (props) => {\n const {\n children,\n variant = 'primary',\n tagName = 'button',\n inverted,\n contentType = 'text',\n size = 'default',\n className,\n icon,\n url,\n buttonType,\n disabled,\n onClick,\n onMouseOver,\n onMouseOut,\n onFocus,\n onBlur,\n tabIndex,\n title,\n ...restOfProps\n } = props;\n\n const ComponentName = tagName;\n const Icon = icon;\n const otherProps =\n tagName === 'button'\n ? { type: buttonType, disabled }\n : { href: disabled ? undefined : url };\n\n return (\n \n {contentType !== 'text' && contentType !== 'icon-right' && Icon && (\n \n )}\n {(contentType === 'text' ||\n contentType === 'icon-left' ||\n contentType === 'icon-right') &&\n children}\n {contentType !== 'text' && contentType === 'icon-right' && Icon && (\n \n )}\n \n );\n};\n\nButton.displayName = 'Button';\n\nButton.defaultProps = {\n className: undefined,\n icon: undefined,\n url: undefined,\n buttonType: 'button',\n disabled: false,\n inverted: false,\n onClick: undefined,\n onMouseOver: undefined,\n onMouseOut: undefined,\n onFocus: undefined,\n onBlur: undefined,\n tabIndex: undefined,\n title: undefined,\n};\n\nButton.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n variant: PropTypes.oneOf([\n 'primary',\n 'secondary',\n 'outlined',\n 'danger',\n 'ghost',\n 'ghost-brand',\n 'ghost-success',\n 'ghost-warning',\n 'ghost-danger',\n ]).isRequired,\n contentType: PropTypes.oneOf([\n 'text',\n 'icon-left',\n 'icon-right',\n 'icon',\n 'icon-rounded',\n ]).isRequired,\n inverted: PropTypes.bool,\n tagName: PropTypes.oneOf(['a', 'button']).isRequired,\n className: PropTypes.string,\n icon: PropTypes.node,\n url: PropTypes.string,\n buttonType: PropTypes.string,\n disabled: PropTypes.bool,\n size: PropTypes.oneOf(['default', 's', 'l', 'xl']).isRequired,\n onClick: PropTypes.func,\n onMouseOver: PropTypes.func,\n onMouseOut: PropTypes.func,\n onFocus: PropTypes.func,\n onBlur: PropTypes.func,\n tabIndex: PropTypes.number,\n title: PropTypes.string,\n};\n","import { h } from 'preact';\n\nexport const Spinner = () => (\n \n \n \n);\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { FocusTrap } from '../../shared/components/focusTrap';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\nimport { Button } from '@crayons';\n\nfunction getAdditionalClassNames({ size, className }) {\n let additionalClassNames = '';\n\n if (size && size.length > 0 && size !== 'default') {\n additionalClassNames += ` crayons-modal--${size}`;\n }\n\n if (className && className.length > 0) {\n additionalClassNames += ` ${className}`;\n }\n\n return additionalClassNames;\n}\n\nconst CloseIcon = () => (\n \n Close\n \n \n);\n\n/**\n * A modal component which can be presented with or without an overlay.\n * The modal is presented within a focus trap for accessibility purposes - please note that the selector used for the focusTrap must be unique on the given page, otherwise focus may be trapped on the wrong element.\n *\n * @param {Object} props\n * @param {Array} props.children The content to be displayed inside the Modal. Can be provided by composition (see example).\n * @param {string} props.size The desired modal size ('s', 'm' or 'default')\n * @param {string} props.className Optional additional classnames to apply to the modal container\n * @param {string} props.title The title to be displayed in the modal heading. If provided, a title bar with a close button will be displayed.\n * @param {boolean} props.overlay Whether or not to show a semi-opaque overlay behind the modal\n * @param {Function} props.onClose Callback for any function to be executed on close button click or Escape\n * @param {boolean} props.closeOnClickOutside Whether the modal should close if the user clicks outside of it\n * @param {string} props.focusTrapSelector The CSS selector for where to trap the user's focus. This should be unique to the page in which the modal is presented.\n * \n * @example\n * \n
\n

Some modal content

\n
\n \n */\nexport const Modal = ({\n children,\n size = 'default',\n className,\n title,\n overlay = true,\n onClose = () => {},\n closeOnClickOutside = false,\n focusTrapSelector = '.crayons-modal',\n}) => {\n return (\n \n \n \n {title && (\n
\n

{title}

\n \n
\n )}\n
{children}
\n \n {overlay && (\n \n )}\n \n \n );\n};\n\nModal.displayName = 'Modal';\n\nModal.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n className: PropTypes.string,\n title: PropTypes.string.isRequired,\n overlay: PropTypes.bool,\n onClose: PropTypes.func,\n size: PropTypes.oneOf(['default', 's', 'm']).isRequired,\n focusTrapSelector: PropTypes.string,\n};\n","import PropTypes from 'prop-types';\nimport { h, Fragment } from 'preact';\nimport { useLayoutEffect, useRef, useCallback } from 'preact/hooks';\nimport { createFocusTrap } from 'focus-trap';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\nimport { KeyboardShortcuts } from './useKeyboardShortcuts';\n\n/**\n * Wrapper component to create a focus trap within the HTML element found by the given selector\n *\n * @example\n * import { FocusTrap } from \"shared/components/FocusTrap\";\n *\n * const ExampleComponent = ({ onClose }) => (\n * \n *
\n * \n *
\n *
\n * )\n *\n * @param {string} selector The CSS selector for the element where focus is to be trapped\n * @param {Array} children Child element(s) passed via composition\n * @param {Function} onDeactivate Callback function to be called when the focus trap is deactivated by navigation or Escape press\n * @param {boolean} clickOutsideDeactivates If true, the focus trap will deactivate when a user clicks outside of the trap area\n */\nexport const FocusTrap = ({\n selector,\n children,\n onDeactivate,\n clickOutsideDeactivates = false,\n}) => {\n const focusTrap = useRef(null);\n const deactivate = useCallback(() => onDeactivate(), [onDeactivate]);\n\n useLayoutEffect(() => {\n const currentLocationHref = document.location.href;\n const routeChangeObserver = new MutationObserver((mutations) => {\n const hasRouteChanged = mutations.some(\n () => currentLocationHref !== document.location.href,\n );\n\n // Ensure trap deactivates if user navigates from the page\n if (hasRouteChanged) {\n focusTrap.current?.deactivate();\n routeChangeObserver.disconnect();\n }\n });\n\n focusTrap.current = createFocusTrap(selector, {\n escapeDeactivates: false,\n clickOutsideDeactivates,\n onDeactivate: deactivate,\n });\n\n focusTrap.current.activate();\n routeChangeObserver.observe(document.querySelector('body'), {\n childList: true,\n });\n\n return () => {\n focusTrap.current.deactivate();\n routeChangeObserver.disconnect();\n };\n }, [clickOutsideDeactivates, selector, deactivate]);\n\n const shortcuts = {\n escape: onDeactivate,\n };\n\n return (\n \n {children}\n \n \n );\n};\n\nFocusTrap.defaultProps = {\n selector: '.crayons-modal',\n onDeactivate: () => {},\n};\n\nFocusTrap.propTypes = {\n selector: PropTypes.string,\n children: defaultChildrenPropTypes.isRequired,\n onDeactivate: PropTypes.func,\n};\n","import { h } from 'preact';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\n\nexport const ButtonGroup = ({ children }) => (\n
\n {children}\n
\n);\n\nButtonGroup.displayName = 'ButtonGroup';\n\nButtonGroup.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\n\nexport const Dropdown = (props) => {\n const { children, className, ...restOfProps } = props;\n return (\n 0 ? ` ${className}` : ''\n }`}\n {...restOfProps}\n >\n {children}\n \n );\n};\n\nDropdown.defaultProps = {\n className: undefined,\n};\n\nDropdown.displayName = 'Dropdown';\n\nDropdown.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n className: PropTypes.string,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../../common-prop-types';\n\n// Only radio and checkboxes require an additional CSS class (variant class). Other form elements do not.\n\nexport const FormField = ({ children, variant }) => {\n return (\n 0 ? ` crayons-field--${variant}` : ''\n }`}\n >\n {children}\n \n );\n};\n\nFormField.displayName = 'FormField';\n\nFormField.defaultProps = {\n variant: undefined,\n};\n\nFormField.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n variant: PropTypes.oneOf(['radio', 'checkbox']),\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\n\nexport const RadioButton = (props) => {\n const { id, value, name, className, checked, onClick, ...otherProps } = props;\n\n return (\n 0 ? ` ${className}` : ''\n }`}\n checked={checked}\n onClick={onClick}\n type=\"radio\"\n {...otherProps}\n />\n );\n};\n\nRadioButton.displayName = 'RadioButton';\n\nRadioButton.defaultProps = {\n id: undefined,\n className: undefined,\n checked: false,\n name: undefined,\n};\n\nRadioButton.propTypes = {\n id: PropTypes.string,\n value: PropTypes.string.isRequired,\n className: PropTypes.string,\n checked: PropTypes.bool,\n name: PropTypes.string,\n onClick: PropTypes.func.isRequired,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../common-prop-types';\nimport { Button } from '@crayons';\n\nexport const snackbarItemProps = {\n children: defaultChildrenPropTypes.isRequired,\n actions: PropTypes.arrayOf(\n PropTypes.shape({\n message: PropTypes.string.isRequired,\n handler: PropTypes.func.isRequired,\n lifespan: PropTypes.number.isRequired,\n }),\n ),\n};\n\nexport const SnackbarItem = ({ message, actions = [] }) => (\n
\n
\n {message}\n
\n
\n {actions.map(({ text, handler }) => (\n \n ))}\n
\n
\n);\n\nSnackbarItem.displayName = 'SnackbarItem';\n\nSnackbarItem.propTypes = snackbarItemProps.isRequired;\n","import { h, Component } from 'preact';\nimport PropTypes from 'prop-types';\nimport { SnackbarItem } from './SnackbarItem';\n\nlet snackbarItems = [];\n\nexport function addSnackbarItem(snackbarItem) {\n if (!Array.isArray(snackbarItem.actions)) {\n snackbarItem.actions = []; // eslint-disable-line no-param-reassign\n }\n\n snackbarItems.push(snackbarItem);\n}\n\nexport class Snackbar extends Component {\n state = {\n snacks: [],\n };\n\n pollingId;\n\n paused = false;\n\n pauseLifespan;\n\n resumeLifespan;\n\n componentDidMount() {\n this.initializePolling();\n }\n\n componentDidUpdate() {\n if (!this.pauseLifespan) {\n this.pauseLifespan = (_event) => {\n this.paused = true;\n };\n\n this.resumeLifespan = (event) => {\n event.stopPropagation();\n this.paused = false;\n };\n\n this.element.addEventListener('mouseover', this.pauseLifespan);\n this.element.addEventListener('mouseout', this.resumeLifespan, true);\n }\n }\n\n componentWillUnmount() {\n if (this.element) {\n this.element.removeEventListener('mouseover', this.pauseLifespan);\n this.element.addEventListener('mouseout', this.resumeLifespan);\n }\n }\n\n initializePolling() {\n const { pollingTime, lifespan } = this.props;\n\n this.pollingId = setInterval(() => {\n if (snackbarItems.length > 0) {\n // Need to add the lifespan to snackbar items because each second that goes by, we\n // decrease the lifespan until it is no more.\n const newSnacks = snackbarItems.map((snackbarItem) => ({\n ...snackbarItem,\n lifespan,\n }));\n\n snackbarItems = [];\n\n this.updateSnackbarItems(newSnacks);\n\n // Start the lifespan countdowns for each new snackbar item.\n newSnacks.forEach((snack) => {\n // eslint-disable-next-line no-param-reassign\n snack.lifespanTimeoutId = setTimeout(() => {\n this.decreaseLifespan(snack);\n }, 1000);\n\n if (snack.addCloseButton) {\n // Adds an optional close button if addDefaultACtion is true.\n snack.actions.push({\n text: 'Dismiss',\n handler: () => {\n this.setState((prevState) => {\n return {\n prevState,\n snacks: prevState.snacks.filter(\n (potentialSnackToFilterOut) =>\n potentialSnackToFilterOut !== snack,\n ),\n };\n });\n },\n });\n }\n });\n }\n }, pollingTime);\n }\n\n updateSnackbarItems(newSnacks) {\n this.setState((prevState) => {\n let updatedSnacks = [...prevState.snacks, ...newSnacks];\n\n if (updatedSnacks.length > 3) {\n const snacksToBeDiscarded = updatedSnacks.slice(\n 0,\n updatedSnacks.length - 3,\n );\n\n snacksToBeDiscarded.forEach(({ lifespanTimeoutId }) => {\n clearTimeout(lifespanTimeoutId);\n });\n\n updatedSnacks = updatedSnacks.slice(updatedSnacks.length - 3);\n }\n\n return { ...prevState, snacks: updatedSnacks };\n });\n }\n\n decreaseLifespan(snack) {\n /* eslint-disable no-param-reassign */\n if (!this.paused && snack.lifespan === 0) {\n clearTimeout(snack.lifespanTimeoutId);\n\n this.setState((prevState) => {\n const snacks = prevState.snacks.filter(\n (currentSnack) => currentSnack !== snack,\n );\n\n return {\n ...prevState,\n snacks,\n };\n });\n\n return;\n }\n\n if (!this.paused) {\n snack.lifespan -= 1;\n }\n\n snack.lifespanTimeoutId = setTimeout(() => {\n this.decreaseLifespan(snack);\n }, 1000);\n /* eslint-enable no-param-reassign */\n }\n\n render() {\n const { snacks } = this.state;\n\n return (\n 0 ? 'crayons-snackbar' : 'hidden'}\n ref={(element) => {\n this.element = element;\n }}\n >\n {snacks.map(({ message, actions = [] }, index) => (\n \n ))}\n \n );\n }\n}\n\nSnackbar.defaultProps = {\n lifespan: 5,\n pollingTime: 300,\n};\n\nSnackbar.displayName = 'Snackbar';\n\nSnackbar.propTypes = {\n lifespan: PropTypes.number,\n pollingTime: PropTypes.number,\n};\n","/**\n * Checks if an element is visible in the viewport\n *\n * @example\n * const element = document.getElementById('element');\n * isInViewport({element, allowPartialVisibility = true}); // true or false\n *\n * @param {HTMLElement} element - The HTML element to check\n * @param {number} [offsetTop=0] - Part of the screen to ignore counting from the top\n * @param {boolean} [allowPartialVisibility=false] - A boolean to flip the check between partial or completely visible in the viewport\n * @returns {boolean} isInViewport - true if the element is visible in the viewport\n */\nexport function isInViewport({\n element,\n offsetTop = 0,\n allowPartialVisibility = false,\n}) {\n const boundingRect = element.getBoundingClientRect();\n const clientHeight =\n window.innerHeight || document.documentElement.clientHeight;\n const clientWidth = window.innerWidth || document.documentElement.clientWidth;\n const topIsInViewport =\n boundingRect.top <= clientHeight && boundingRect.top >= offsetTop;\n const rightIsInViewport =\n boundingRect.right >= 0 && boundingRect.right <= clientWidth;\n const bottomIsInViewport =\n boundingRect.bottom >= offsetTop && boundingRect.bottom <= clientHeight;\n const leftIsInViewport =\n boundingRect.left <= clientWidth && boundingRect.left >= 0;\n const topIsOutOfViewport = boundingRect.top <= offsetTop;\n const bottomIsOutOfViewport = boundingRect.bottom >= clientHeight;\n const elementSpansEntireViewport =\n topIsOutOfViewport && bottomIsOutOfViewport;\n\n if (allowPartialVisibility) {\n return (\n (topIsInViewport || bottomIsInViewport || elementSpansEntireViewport) &&\n (leftIsInViewport || rightIsInViewport)\n );\n }\n return (\n topIsInViewport &&\n bottomIsInViewport &&\n leftIsInViewport &&\n rightIsInViewport\n );\n}\n","import debounce from 'lodash.debounce';\n\n/**\n * A util function to wrap any action with lodash's `debounce` (https://lodash.com/docs/#debounce).\n * To use this util, wrap it in the util like so: debounceAction(onSearchBoxType.bind(this));\n *\n * By default, this util uses a default time of 300ms, and includes a default config of `{ leading: false }`.\n * These values can be overridden: debounceAction(this.onSearchBoxType.bind(this), { time: 100, config: { leading: true }});\n *\n *\n * @param {Function} action - The function that should be wrapped with `debounce`.\n * @param {Number} [time=300] - The number of milliseconds to wait.\n * @param {Object} [config={ leading: false }] - Any configuration for the debounce function.\n *\n * @returns {Function} A function wrapped in `debounce`.\n */\nexport function debounceAction(\n action,\n { time = 300, config = { leading: false } } = {},\n) {\n const configs = { ...config };\n return debounce(action, time, configs);\n}\n","import PropTypes from 'prop-types';\n\nexport const userPropTypes = PropTypes.shape({\n id: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n profile_image_url: PropTypes.string.isRequired,\n summary: PropTypes.string.isRequired,\n});\n","import PropTypes from 'prop-types';\n\nexport const selectedTagsPropTypes = PropTypes.shape({\n tags: PropTypes.arrayOf(PropTypes.string).isRequired,\n onClick: PropTypes.func.isRequired,\n onKeyPress: PropTypes.func.isRequired,\n});\n","let postscribeImport;\n\nasync function getPostScribe() {\n if (postscribeImport) {\n // Grab the cached import so we're not always fetching it from the network.\n return postscribeImport;\n }\n\n const { default: postscribe } = await import('postscribe');\n postscribeImport = postscribe;\n\n return postscribeImport;\n}\n\nfunction getGistTags(nodes) {\n const gistNodes = [];\n\n for (const node of nodes) {\n if (node.nodeType === 1) {\n if (node.classList.contains('ltag_gist-liquid-tag')) {\n gistNodes.push(node);\n }\n\n gistNodes.push(...node.querySelectorAll('.ltag_gist-liquid-tag'));\n }\n }\n\n return gistNodes;\n}\n\nfunction loadEmbeddedGists(postscribe, gistTags) {\n for (const gistTag of gistTags) {\n postscribe(gistTag, gistTag.firstElementChild.outerHTML, {\n beforeWrite(text) {\n return gistTag.childElementCount > 3 ? '' : text;\n },\n });\n }\n}\n\nfunction watchForGistTagInsertion(targetNode, postscribe) {\n const config = { attributes: false, childList: true, subtree: true };\n\n const callback = function (mutationsList) {\n for (const { type, addedNodes } of mutationsList) {\n if (type === 'childList' && addedNodes.length > 0) {\n loadEmbeddedGists(postscribe, getGistTags(addedNodes));\n }\n }\n };\n\n const observer = new MutationObserver(callback);\n observer.observe(targetNode, config);\n\n InstantClick.on('change', () => {\n observer.disconnect();\n });\n\n window.addEventListener('beforeunload', () => {\n observer.disconnect();\n });\n}\n\nexport async function embedGists(targetNode) {\n const postscribe = await getPostScribe();\n\n // Load gist tags that were rendered server-side\n loadEmbeddedGists(\n postscribe,\n document.querySelectorAll('.ltag_gist-liquid-tag'),\n );\n\n watchForGistTagInsertion(targetNode, postscribe);\n}","import { isInViewport } from '@utilities/viewport';\nimport { debounceAction } from '@utilities/debounceAction';\n\n/**\n * Helper function designed to be used on scroll to detect when dropdowns should switch from dropping downwards/upwards.\n * The action is debounced since scroll events are usually fired several at a time.\n *\n * @returns {Function} a debounced function that handles the repositioning of dropdowns\n * @example\n *\n * document.addEventListener('scroll', getDropdownRepositionListener());\n */\nexport const getDropdownRepositionListener = () =>\n debounceAction(handleDropdownRepositions);\n\n/**\n * Checks for all dropdowns on the page which have the attribute 'data-repositioning-dropdown', signalling\n * they should dynamically change between dropping downwards or upwards, depending on viewport position.\n *\n * Any dropdowns not fully in view when dropping down will be switched to dropping upwards.\n */\nconst handleDropdownRepositions = () => {\n // Select all of the dropdowns which should reposition\n const allRepositioningDropdowns = document.querySelectorAll(\n '[data-repositioning-dropdown]',\n );\n\n for (const element of allRepositioningDropdowns) {\n // Default to dropping downwards\n element.classList.remove('reverse');\n\n const isDropdownCurrentlyOpen = element.style.display === 'block';\n\n if (!isDropdownCurrentlyOpen) {\n // We can't determine position on an element with display:none, so we \"show\" the dropdown with 0 opacity very temporarily\n element.style.opacity = 0;\n element.style.display = 'block';\n }\n\n if (!isInViewport({ element })) {\n // If the element isn't fully visible when dropping down, reverse the direction\n element.classList.add('reverse');\n }\n\n if (!isDropdownCurrentlyOpen) {\n // Revert the temporary changes to determine position\n element.style.removeProperty('display');\n element.style.removeProperty('opacity');\n }\n }\n};\n\n/**\n * Helper query string to identify interactive/focusable HTML elements\n */\nconst INTERACTIVE_ELEMENTS_QUERY =\n 'button, [href], input:not([type=\"hidden\"]), select, textarea, [tabindex=\"0\"]';\n\n/**\n * Open the given dropdown, updating aria attributes, and focusing the first interactive element\n *\n * @param {Object} args\n * @param {string} args.triggerElementId The id of the button which activates the dropdown\n * @param {string} args.dropdownContent The id of the dropdown content element\n */\nconst openDropdown = ({ triggerElementId, dropdownContentId }) => {\n const dropdownContent = document.getElementById(dropdownContentId);\n const triggerElement = document.getElementById(triggerElementId);\n\n triggerElement.setAttribute('aria-expanded', 'true');\n\n // Style set inline to prevent specificity issues\n dropdownContent.style.display = 'block';\n\n // Send focus to the first suitable element\n dropdownContent.querySelector(INTERACTIVE_ELEMENTS_QUERY)?.focus();\n};\n\n/**\n * Close the given dropdown, updating aria attributes\n *\n * @param {Object} args\n * @param {string} args.triggerElementId The id of the button which activates the dropdown\n * @param {string} args.dropdownContent The id of the dropdown content element\n * @param {Function} args.onClose Optional function for any side-effects which should occur on dropdown close\n */\nconst closeDropdown = ({ triggerElementId, dropdownContentId, onClose }) => {\n const dropdownContent = document.getElementById(dropdownContentId);\n\n if (!dropdownContent) {\n // Component may have unmounted\n return;\n }\n\n document\n .getElementById(triggerElementId)\n ?.setAttribute('aria-expanded', 'false');\n\n // Remove the inline style added when we opened the dropdown\n dropdownContent.style.removeProperty('display');\n\n onClose?.();\n};\n\n/**\n * A helper function to initialize dropdown behaviors. This function attaches open/close click and keyup listeners,\n * and makes sure relevant aria properties and keyboard focus are updated.\n *\n * @param {Object} args\n * @param {string} args.triggerButtonElementId The ID of the button which triggers the dropdown open/close behavior\n * @param {string} args.dropdownContentId The ID of the dropdown content which should open/close on trigger button press\n * @param {string} args.dropdownContentCloseButtonId Optional ID of any button within the dropdown content which should close the dropdown\n * @param {Function} args.onClose An optional callback for when the dropdown is closed. This can be passed to execute any side-effects required when the dropdown closes.\n * @param {Function} args.onOpen An optional callback for when the dropdown is opened. This can be passed to execute any side-effects required when the dropdown opens.\n *\n * @returns {{closeDropdown: Function}} Object with callback to close the initialized dropdown\n */\nexport const initializeDropdown = ({\n triggerElementId,\n dropdownContentId,\n dropdownContentCloseButtonId,\n onClose,\n onOpen,\n}) => {\n const triggerButton = document.getElementById(triggerElementId);\n const dropdownContent = document.getElementById(dropdownContentId);\n\n if (!triggerButton || !dropdownContent) {\n // The required props haven't been provided, do nothing\n return;\n }\n\n // Ensure default values have been applied\n triggerButton.setAttribute('aria-expanded', 'false');\n triggerButton.setAttribute('aria-controls', dropdownContentId);\n triggerButton.setAttribute('aria-haspopup', 'true');\n\n const keyUpListener = ({ key }) => {\n if (key === 'Escape') {\n // Close the dropdown and return focus to the trigger button to prevent focus being lost\n const isCurrentlyOpen =\n triggerButton.getAttribute('aria-expanded') === 'true';\n if (isCurrentlyOpen) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n triggerButton.focus();\n }\n } else if (key === 'Tab') {\n // Close the dropdown if the user has tabbed away from it\n const isInsideDropdown = dropdownContent?.contains(\n document.activeElement,\n );\n if (!isInsideDropdown) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n }\n }\n };\n\n // Close the dropdown if user has clicked outside\n const clickOutsideListener = ({ target }) => {\n if (\n target !== triggerButton &&\n !dropdownContent.contains(target) &&\n !triggerButton.contains(target)\n ) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n\n // If the user did not click on another interactive item, return focus to the trigger\n if (!target.matches(INTERACTIVE_ELEMENTS_QUERY)) {\n triggerButton.focus();\n }\n }\n };\n\n // Any necessary side effects required on dropdown close\n const onCloseCleanupActions = () => {\n onClose?.();\n document.removeEventListener('keyup', keyUpListener);\n document.removeEventListener('click', clickOutsideListener);\n };\n\n // Add the main trigger button toggle funcationality\n triggerButton.addEventListener('click', () => {\n if (\n document\n .getElementById(triggerElementId)\n ?.getAttribute('aria-expanded') === 'true'\n ) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n } else {\n openDropdown({\n triggerElementId,\n dropdownContentId,\n });\n onOpen?.();\n\n document.addEventListener('keyup', keyUpListener);\n document.addEventListener('click', clickOutsideListener);\n }\n });\n\n if (dropdownContentCloseButtonId) {\n // The dropdown content has a 'close' button inside that we also need to handle\n document\n .getElementById(dropdownContentCloseButtonId)\n ?.addEventListener('click', () => {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n\n document.getElementById(triggerElementId)?.focus();\n });\n }\n\n return {\n closeDropdown: () => {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n },\n };\n};","import { h, render } from 'preact';\nimport ahoy from 'ahoy.js';\nimport { Snackbar, addSnackbarItem } from '../Snackbar';\nimport { addFullScreenModeControl } from '../utilities/codeFullscreenModeSwitcher';\nimport { embedGists } from '../utilities/gist';\nimport { initializeDropdown } from '@utilities/dropdownUtils';\n\n/* global Runtime */\n\nconst fullscreenActionElements = document.getElementsByClassName(\n 'js-fullscreen-code-action',\n);\n\nif (fullscreenActionElements) {\n addFullScreenModeControl(fullscreenActionElements);\n}\n\n// The Snackbar for the article page\nconst snackZone = document.getElementById('snack-zone');\nif (snackZone) {\n render(, snackZone);\n}\n\n// eslint-disable-next-line no-restricted-globals\ntop.addSnackbarItem = addSnackbarItem;\n\n// Dropdown accessibility\nfunction hideCopyLinkAnnouncerIfVisible() {\n document.getElementById('article-copy-link-announcer').hidden = true;\n}\n\n// Initialize the share options\nconst shareDropdownButton = document.getElementById('article-show-more-button');\n\nif (shareDropdownButton.dataset.initialized !== 'true') {\n if (Runtime.isNativeAndroid('shareText')) {\n // Android native apps have enhanced sharing capabilities for Articles and don't use our standard dropdown\n shareDropdownButton.addEventListener('click', () =>\n AndroidBridge.shareText(location.href),\n );\n } else {\n const { closeDropdown } = initializeDropdown({\n triggerElementId: 'article-show-more-button',\n dropdownContentId: 'article-show-more-dropdown',\n onClose: hideCopyLinkAnnouncerIfVisible,\n });\n\n // We want to close the dropdown on link select (since they open in a new tab)\n document\n .querySelectorAll('#article-show-more-dropdown [href]')\n .forEach((link) => {\n link.addEventListener('click', (event) => {\n closeDropdown(event)\n \n // Temporary Ahoy Stats for usage reports\n ahoy.track('Post Dropdown', { option: event.target.text.trim() });\n });\n });\n }\n\n shareDropdownButton.dataset.initialized = 'true';\n}\n\n// Initialize the copy to clipboard functionality\nfunction showAnnouncer() {\n const { activeElement } = document;\n const input =\n activeElement.localName === 'clipboard-copy'\n ? activeElement.querySelector('input')\n : document.getElementById('article-copy-link-input');\n input.focus();\n input.setSelectionRange(0, input.value.length);\n\n document.getElementById('article-copy-link-announcer').hidden = false;\n}\n\nfunction copyArticleLink() {\n const inputValue = document.getElementById('article-copy-link-input').value;\n Runtime.copyToClipboard(inputValue).then(() => {\n showAnnouncer();\n });\n}\ndocument\n .querySelector('clipboard-copy')\n ?.addEventListener('click', copyArticleLink);\n\n// Comment Subscription\ngetCsrfToken().then(async () => {\n const { user = null, userStatus } = document.body.dataset;\n const root = document.getElementById('comment-subscription');\n const isLoggedIn = userStatus === 'logged-in';\n\n try {\n const {\n getCommentSubscriptionStatus,\n setCommentSubscriptionStatus,\n CommentSubscription,\n } = await import('../CommentSubscription');\n\n const { articleId } = document.getElementById('article-body').dataset;\n\n let subscriptionType = 'not_subscribed';\n\n if (isLoggedIn && user !== null) {\n ({ config: subscriptionType } = await getCommentSubscriptionStatus(\n articleId,\n ));\n }\n\n const subscriptionRequestHandler = async (type) => {\n const message = await setCommentSubscriptionStatus(articleId, type);\n\n addSnackbarItem({ message, addCloseButton: true });\n };\n\n render(\n ,\n root,\n );\n } catch (e) {\n \n }\n});\n\n// Pin/Unpin article\n// these element are added by initializeBaseUserData.js:addRelevantButtonsToArticle\nconst toggleArticlePin = async (button) => {\n const isPinButton = button.id === 'js-pin-article';\n const { articleId, path } = button.dataset;\n const method = isPinButton ? 'PUT' : 'DELETE';\n const body = method === 'PUT' ? JSON.stringify({ id: articleId }) : null;\n\n const response = await fetch(path, {\n method,\n body,\n headers: {\n Accept: 'application/json',\n 'X-CSRF-Token': window.csrfToken,\n 'Content-Type': 'application/json',\n },\n credentials: 'same-origin',\n });\n\n // response could potentially fail if the article is draft but we don't show\n // the buttons in those cases, so I think there's no need to handle that scenario client side\n if (response.ok) {\n // replace id and label\n button.id = isPinButton ? 'js-unpin-article' : 'js-pin-article';\n button.innerHTML = `${isPinButton ? 'Unpin' : 'Pin'} Post`;\n\n const message = isPinButton\n ? 'The post has been succesfully pinned'\n : 'The post has been succesfully unpinned';\n addSnackbarItem({ message });\n }\n};\n\nconst actionsContainer = document.getElementById('action-space');\nconst pinTargets = ['js-pin-article', 'js-unpin-article'];\nactionsContainer.addEventListener('click', async (event) => {\n if (pinTargets.includes(event.target.id)) {\n toggleArticlePin(event.target);\n }\n});\n\nconst targetNode = document.querySelector('#comments');\ntargetNode && embedGists(targetNode);","import PropTypes from 'prop-types';\n\nexport const organizationPropType = PropTypes.shape({\n id: PropTypes.number.isRequired,\n name: PropTypes.string.isRequired,\n slug: PropTypes.string.isRequired,\n profile_image_90: PropTypes.string.isRequired,\n});\n","import PropTypes from 'prop-types';\n\nexport const tagPropTypes = PropTypes.shape({\n id: PropTypes.number.isRequired,\n name: PropTypes.string.isRequired,\n hotness_score: PropTypes.number.isRequired,\n points: PropTypes.number.isRequired,\n bg_color_hex: PropTypes.string.isRequired,\n text_color_hex: PropTypes.string.isRequired,\n});\n","let isFullScreenModeCodeOn = false;\nlet screenScroll = 0;\nconst fullScreenWindow = document.getElementsByClassName(\n 'js-fullscreen-code',\n)[0];\nconst body = document.body;\n\nfunction setAfterFullScreenScrollPosition() {\n window.scrollTo(0, screenScroll);\n}\n\nfunction getBeforeFullScreenScrollPosition() {\n screenScroll = window.scrollY;\n}\n\nfunction onPressEscape(event) {\n if (event.key == 'Escape') {\n fullScreenModeControl(event);\n }\n}\n\nfunction listenToKeyboardForEscape(listen) {\n if (listen) {\n document.body.addEventListener('keyup', onPressEscape);\n } else {\n document.body.removeEventListener('keyup', onPressEscape);\n }\n}\n\nfunction toggleOverflowForDocument(overflow) {\n if (overflow) {\n body.style.overflow = 'hidden';\n } else {\n body.style.overflow = '';\n }\n}\n\nexport function addFullScreenModeControl(elements) {\n if (elements) {\n for (const element of elements) {\n element.addEventListener('click', fullScreenModeControl);\n }\n }\n}\n\nfunction removeFullScreenModeControl(elements) {\n if (elements) {\n for (const element of elements) {\n element.removeEventListener('click', fullScreenModeControl);\n }\n }\n}\n\nfunction fullScreenModeControl(event) {\n const codeBlock = event.currentTarget.closest('.js-code-highlight')\n ? event.currentTarget.closest('.js-code-highlight').cloneNode(true)\n : null;\n const codeBlockControls = codeBlock\n ? codeBlock.getElementsByClassName('js-fullscreen-code-action')\n : null;\n\n if (isFullScreenModeCodeOn) {\n toggleOverflowForDocument(false);\n setAfterFullScreenScrollPosition();\n listenToKeyboardForEscape(false);\n removeFullScreenModeControl(codeBlockControls);\n\n fullScreenWindow.classList.remove('is-open');\n fullScreenWindow.removeChild(fullScreenWindow.childNodes[0]);\n\n isFullScreenModeCodeOn = false;\n } else {\n toggleOverflowForDocument(true);\n getBeforeFullScreenScrollPosition();\n listenToKeyboardForEscape(true);\n codeBlock.classList.add('is-fullscreen');\n fullScreenWindow.appendChild(codeBlock);\n fullScreenWindow.classList.add('is-open');\n\n addFullScreenModeControl(codeBlockControls);\n\n isFullScreenModeCodeOn = true;\n }\n}\n"],"sourceRoot":""}