{"version":3,"sources":["webpack:///./app/javascript/topNavigation/utilities.js","webpack:///./app/javascript/packs/base.jsx"],"names":["closeHeaderMenu","memberMenu","menuNavButton","setAttribute","classList","remove","dataset","clicked","firstItem","document","getElementById","openHeaderMenu","add","focusFirstItem","activeElement","focus","window","requestAnimationFrame","initializeMemberMenu","memberTopMenu","navigator","userAgent","body","addEventListener","_event","contains","test","e","key","querySelector","event","stopPropagation","target","closest","secondToLastNavLink","setTimeout","toggleBurgerMenu","leftNavState","showMoreMenu","nextElementSibling","getInstantClick","waitTime","Promise","resolve","reject","failTimer","clearInterval","timer","Error","setInterval","InstantClick","clearTimeout","initializeMobileMenu","menuTriggers","moreMenus","forEach","trigger","onclick","setCurrentPageIconLink","currentPage","pageEntries","filter","page","iconLink","blur","WINDOW_MODAL_ID","initializeNav","querySelectorAll","getElementsByClassName","Object","entries","Forem","preactImport","undefined","getPreactImport","this","mentionAutoCompleteImports","getMentionAutoCompleteImports","all","initializeMentionAutocompleteTextArea","originalTextArea","parentContainer","parentElement","id","MentionAutocompleteTextArea","fetchSearch","render","h","replaceElement","fetchSuggestions","username","modalImports","getModalImports","showModal","title","contentSelector","overlay","size","onOpen","Modal","currentModalContainer","createElement","appendChild","onClose","focusTrapSelector","dangerouslySetInnerHTML","__html","innerHTML","closeModal","then","spa","on"],"mappings":"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","file":"js/base-742ceec4cc57ac8f9eab.chunk.js","sourcesContent":["function closeHeaderMenu(memberMenu, menuNavButton) {\n menuNavButton.setAttribute('aria-expanded', 'false');\n memberMenu.classList.remove('desktop', 'showing');\n delete memberMenu.dataset.clicked;\n}\n\nconst firstItem = document.getElementById('first-nav-link');\n\nfunction openHeaderMenu(memberMenu, menuNavButton) {\n menuNavButton.setAttribute('aria-expanded', 'true');\n memberMenu.classList.add('showing');\n\n if (!firstItem) {\n return;\n }\n\n // Focus on the first item in the menu\n (function focusFirstItem() {\n if (document.activeElement === firstItem) {\n // The first element has focus\n return;\n }\n\n firstItem.focus();\n // requestAnimationFrame is faster and more reliable than setTimeout\n // https://swizec.com/blog/how-to-wait-for-dom-elements-to-show-up-in-modern-browsers\n window.requestAnimationFrame(focusFirstItem);\n })();\n}\n\n/**\n * Determines whether or not a device is a touch device.\n *\n * @returns true if a touch device, otherwise false.\n */\nexport function isTouchDevice() {\n return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|DEV-Native-ios/i.test(\n navigator.userAgent,\n );\n}\n\n/**\n * Initializes the member navigation menu events.\n *\n * @param {HTMLElement} memberTopMenu The member menu in the top navigation.\n * @param {HTMLElement} menuNavButton The button to activate the member navigation menu.\n */\nexport function initializeMemberMenu(memberTopMenu, menuNavButton) {\n // Typically using CSS for hovering for the menu is the way to go. But... since we use InstantClick for\n // loading pages, the top header navigation never changes in terms of the DOM references.\n // Because of this, we're using mouse events to mouseover/mouseout on the member's avatar\n // to attach styles to get it to show the menu so that this works on desktop and mobile.\n if (navigator.userAgent === 'DEV-Native-ios') {\n document.body.classList.add('dev-ios-native-body');\n }\n const { classList } = memberTopMenu;\n menuNavButton.addEventListener('click', (_event) => {\n if (classList.contains('showing') && memberTopMenu.dataset.clicked) {\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n } else {\n openHeaderMenu(memberTopMenu, menuNavButton);\n memberTopMenu.dataset.clicked = 'clicked';\n }\n });\n\n if (isTouchDevice()) {\n memberTopMenu.addEventListener('focus', (_event) => {\n menuNavButton.setAttribute('aria-expanded', 'true');\n });\n } else {\n memberTopMenu.addEventListener('mouseover', (_event) => {\n classList.add('desktop');\n openHeaderMenu(memberTopMenu, menuNavButton);\n });\n memberTopMenu.addEventListener('mouseout', (_event) => {\n if (!memberTopMenu.dataset.clicked) {\n closeHeaderMenu(memberTopMenu, menuNavButton);\n }\n });\n\n memberTopMenu.addEventListener('keyup', (e) => {\n if (e.key === 'Escape' && classList.contains('showing')) {\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n }\n });\n }\n\n memberTopMenu\n .querySelector('.crayons-header__menu__dropdown')\n .addEventListener('click', (event) => {\n // There is a click event listener on the body and we do not want\n // this click to be caught by it\n event.stopPropagation();\n\n // Close the menu if the user clicked or touched on mobile a link in the menu.\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n });\n\n document.addEventListener('click', (event) => {\n if (event.target.closest('#member-menu-button') === menuNavButton) {\n // The menu navigation button manages it's own click event.\n return;\n }\n\n // Close the menu if the user clicked or touched on mobile a link in the menu.\n closeHeaderMenu(memberTopMenu, menuNavButton);\n });\n\n const secondToLastNavLink = document.getElementById('second-last-nav-link');\n\n document\n .getElementById('last-nav-link')\n .addEventListener('blur', (_event) => {\n // When we tab out of the last link in the member menu, close\n // the menu.\n setTimeout(() => {\n if (document.activeElement === secondToLastNavLink) {\n return;\n }\n\n closeHeaderMenu(memberTopMenu, menuNavButton);\n }, 10);\n });\n}\n\nfunction toggleBurgerMenu() {\n const { leftNavState = 'closed' } = document.body.dataset;\n document.body.dataset.leftNavState =\n leftNavState === 'open' ? 'closed' : 'open';\n}\n\nfunction showMoreMenu({ target }) {\n target.nextElementSibling.classList.remove('hidden');\n target.classList.add('hidden');\n}\n\n/**\n * Gets a reference to InstantClick\n *\n * @param {number} [waitTime=2000] The amount of time to wait\n * until giving up waiting for InstantClick to exist\n *\n * @returns {Promise} The global instance of InstantClick.\n */\nexport async function getInstantClick(waitTime = 2000) {\n return new Promise((resolve, reject) => {\n const failTimer = setTimeout(() => {\n clearInterval(timer);\n reject(new Error('Unable to resolve InstantClick'));\n }, waitTime);\n\n const timer = setInterval(() => {\n if (typeof InstantClick !== 'undefined') {\n clearTimeout(failTimer);\n clearInterval(timer);\n resolve(InstantClick);\n }\n });\n });\n}\n\n/**\n * Initializes the hamburger menu for mobile navigation\n *\n * @param {HTMLElement[]} menuTriggers\n * @param {HTMLElement[]} moreMenus\n */\nexport function initializeMobileMenu(menuTriggers, moreMenus) {\n menuTriggers.forEach((trigger) => {\n trigger.onclick = toggleBurgerMenu;\n });\n\n moreMenus.forEach((trigger) => {\n trigger.onclick = showMoreMenu;\n });\n}\n\n/**\n * Sets the icon link visually for the current page if the current page\n * is one of the main icon links of the top navigation.\n *\n * @param {string} currentPage\n * @param {[string, HTMLElement][]} pageEntries\n */\nexport function setCurrentPageIconLink(currentPage, pageEntries) {\n pageEntries\n // Filter out nulls (means the user is logged out so most icons are not in the logged out view)\n .filter(([, iconLink]) => iconLink)\n .forEach(([page, iconLink]) => {\n if (currentPage === page) {\n iconLink.blur();\n iconLink.classList.add('crayons-header__link--current');\n } else {\n iconLink.classList.remove('crayons-header__link--current');\n }\n });\n}\n","import {\n initializeMobileMenu,\n setCurrentPageIconLink,\n getInstantClick,\n initializeMemberMenu,\n} from '../topNavigation/utilities';\n\n// Unique ID applied to modals created using window.Forem.showModal\nconst WINDOW_MODAL_ID = 'window-modal';\n\n// Namespace for functions which need to be accessed in plain JS initializers\nwindow.Forem = {\n preactImport: undefined,\n getPreactImport() {\n if (!this.preactImport) {\n this.preactImport = import('preact');\n }\n return this.preactImport;\n },\n mentionAutoCompleteImports: undefined,\n getMentionAutoCompleteImports() {\n if (!this.mentionAutoCompleteImports) {\n this.mentionAutoCompleteImports = [\n import('@crayons/MentionAutocompleteTextArea'),\n import('@utilities/search'),\n this.getPreactImport(),\n ];\n }\n\n // We're still returning Promises, but if the they have already been imported\n // they will now be fulfilled instead of pending, i.e. a network request is no longer made.\n return Promise.all(this.mentionAutoCompleteImports);\n },\n initializeMentionAutocompleteTextArea: async (originalTextArea) => {\n const parentContainer = originalTextArea.parentElement;\n\n const alreadyInitialized = parentContainer.id === 'combobox-container';\n if (alreadyInitialized) {\n return;\n }\n\n const [{ MentionAutocompleteTextArea }, { fetchSearch }, { render, h }] =\n await window.Forem.getMentionAutoCompleteImports();\n\n render(\n fetchSearch('usernames', { username })}\n />,\n parentContainer,\n originalTextArea,\n );\n },\n modalImports: undefined,\n getModalImports() {\n if (!this.modalImports) {\n this.modalImports = [import('@crayons/Modal'), this.getPreactImport()];\n }\n return Promise.all(this.modalImports);\n },\n showModal: async ({\n title,\n contentSelector,\n overlay = false,\n size = 's',\n onOpen,\n }) => {\n const [{ Modal }, { render, h }] = await window.Forem.getModalImports();\n\n // Guard against two modals being opened at once\n let currentModalContainer = document.getElementById(WINDOW_MODAL_ID);\n if (currentModalContainer) {\n render(null, currentModalContainer);\n } else {\n currentModalContainer = document.createElement('div');\n currentModalContainer.setAttribute('id', WINDOW_MODAL_ID);\n document.body.appendChild(currentModalContainer);\n }\n\n render(\n {\n render(null, currentModalContainer);\n }}\n size={size}\n focusTrapSelector={`#${WINDOW_MODAL_ID}`}\n >\n \n ,\n currentModalContainer,\n );\n\n onOpen?.();\n },\n closeModal: async () => {\n const currentModalContainer = document.getElementById(WINDOW_MODAL_ID);\n if (currentModalContainer) {\n const { render } = await window.Forem.getPreactImport();\n render(null, currentModalContainer);\n }\n },\n};\n\nfunction getPageEntries() {\n return Object.entries({\n 'notifications-index': document.getElementById('notifications-link'),\n 'chat_channels-index': document.getElementById('connect-link'),\n 'moderations-index': document.getElementById('moderation-link'),\n 'stories-search': document.getElementById('search-link'),\n });\n}\n\nfunction initializeNav() {\n const { currentPage } = document.getElementById('page-content').dataset;\n const menuTriggers = [\n ...document.querySelectorAll(\n '.js-hamburger-trigger, .hamburger a:not(.js-nav-more-trigger)',\n ),\n ];\n const moreMenus = [...document.getElementsByClassName('js-nav-more-trigger')];\n\n setCurrentPageIconLink(currentPage, getPageEntries());\n initializeMobileMenu(menuTriggers, moreMenus);\n}\n\nconst memberMenu = document.getElementById('crayons-header__menu');\nconst menuNavButton = document.getElementById('member-menu-button');\n\nif (memberMenu) {\n initializeMemberMenu(memberMenu, menuNavButton);\n}\n\ngetInstantClick().then((spa) => {\n spa.on('change', () => {\n initializeNav();\n });\n});\n\ninitializeNav();\n"],"sourceRoot":""}