{"version":3,"sources":["a11y-menu-button-links.js"],"names":["MenuButtonLinks","domNode","_classCallCheck","this","buttonNode","querySelector","menuNode","menuitemNodes","firstMenuitem","firstChars","addEventListener","onButtonClick","bind","nodes","lastMenuitem","querySelectorAll","i","push","menuitem","tabIndex","textContent","trim","toLowerCase","onMenuitemKeydown","onMenuitemMouseover","onFocusin","onFocusout","window","onBackgroundMousedown","newMenuitem","forEach","item","focus","setFocusToMenuitem","currentMenuitem","index","char","charLowerCase","start","indexOf","startIndex","style","display","isOpen","removeAttribute","getAttribute","classList","add","event","flag","key","openPopup","setFocusToFirstMenuitem","closePopup","stopPropagation","preventDefault","tgt","currentTarget","isPrintableCharacter","str","length","match","ctrlKey","metaKey","shiftKey","setFocusByFirstCharacter","location","href","setFocusToPreviousMenuitem","setFocusToNextMenuitem","setFocusToLastMenuitem","contains","target","menuButtons"],"mappings":"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","file":"a11y-menu-button-links.js","sourcesContent":["/*\n * This software or document includes material copied from or derived from Navigation Menu Button Example https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/examples/menu-button-links.\n * Copyright © 2023 World Wide Web Consortium.\n * https://www.w3.org/Consortium/Legal/2023/doc-license\n */\n\n/* eslint class-methods-use-this: \"off\" */\n/* eslint no-new: \"off\" */\n/* eslint no-param-reassign: \"off\" */\n\n'use strict';\n\nclass MenuButtonLinks {\n constructor(domNode) {\n this.domNode = domNode;\n this.buttonNode = domNode.querySelector('button');\n this.menuNode = domNode.querySelector('[role=\"menu\"]');\n this.menuitemNodes = [];\n this.firstMenuitem = false;\n this.lastMenuitem = false;\n this.firstChars = [];\n\n this.buttonNode.addEventListener(\n 'keydown',\n this.onButtonKeydown.bind(this)\n );\n this.buttonNode.addEventListener('click', this.onButtonClick.bind(this));\n\n var nodes = domNode.querySelectorAll('a');\n\n for (let i = 0; i < nodes.length; i++) {\n const menuitem = nodes[i];\n\n this.menuitemNodes.push(menuitem);\n menuitem.tabIndex = -1;\n this.firstChars.push(menuitem.textContent.trim()[0].toLowerCase());\n\n menuitem.addEventListener('keydown', this.onMenuitemKeydown.bind(this));\n\n menuitem.addEventListener(\n 'mouseover',\n this.onMenuitemMouseover.bind(this)\n );\n\n if (!this.firstMenuitem) {\n this.firstMenuitem = menuitem;\n }\n this.lastMenuitem = menuitem;\n }\n\n domNode.addEventListener('focusin', this.onFocusin.bind(this));\n domNode.addEventListener('focusout', this.onFocusout.bind(this));\n\n window.addEventListener(\n 'mousedown',\n this.onBackgroundMousedown.bind(this),\n true\n );\n }\n\n setFocusToMenuitem(newMenuitem) {\n this.menuitemNodes.forEach(function (item) {\n if (item === newMenuitem) {\n item.tabIndex = 0;\n newMenuitem.focus();\n } else {\n item.tabIndex = -1;\n }\n });\n }\n\n setFocusToFirstMenuitem() {\n this.setFocusToMenuitem(this.firstMenuitem);\n }\n\n setFocusToLastMenuitem() {\n this.setFocusToMenuitem(this.lastMenuitem);\n }\n\n setFocusToPreviousMenuitem(currentMenuitem) {\n let newMenuitem;\n let index;\n\n if (currentMenuitem === this.firstMenuitem) {\n newMenuitem = this.lastMenuitem;\n } else {\n index = this.menuitemNodes.indexOf(currentMenuitem);\n newMenuitem = this.menuitemNodes[index - 1];\n }\n\n this.setFocusToMenuitem(newMenuitem);\n\n return newMenuitem;\n }\n\n setFocusToNextMenuitem(currentMenuitem) {\n let newMenuitem;\n let index;\n\n if (currentMenuitem === this.lastMenuitem) {\n newMenuitem = this.firstMenuitem;\n } else {\n index = this.menuitemNodes.indexOf(currentMenuitem);\n newMenuitem = this.menuitemNodes[index + 1];\n }\n this.setFocusToMenuitem(newMenuitem);\n\n return newMenuitem;\n }\n\n setFocusByFirstCharacter(currentMenuitem, char) {\n let start;\n let index;\n\n if (char.length > 1) {\n return;\n }\n\n const charLowerCase = char.toLowerCase();\n\n // Get start index for search based on position of currentItem\n start = this.menuitemNodes.indexOf(currentMenuitem) + 1;\n if (start >= this.menuitemNodes.length) {\n start = 0;\n }\n\n // Check remaining slots in the menu\n index = this.firstChars.indexOf(charLowerCase, start);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = this.firstChars.indexOf(charLowerCase, 0);\n }\n\n // If match was found...\n if (index > -1) {\n this.setFocusToMenuitem(this.menuitemNodes[index]);\n }\n }\n\n // Utilities\n getIndexFirstChars(startIndex, char) {\n for (let i = startIndex; i < this.firstChars.length; i++) {\n if (char === this.firstChars[i]) {\n return i;\n }\n }\n\n return -1;\n }\n\n // Popup menu methods\n openPopup() {\n this.menuNode.style.display = 'block';\n this.buttonNode.setAttribute('aria-expanded', 'true');\n }\n\n closePopup() {\n if (this.isOpen()) {\n this.buttonNode.removeAttribute('aria-expanded');\n this.menuNode.style.display = 'none';\n }\n }\n\n isOpen() {\n return this.buttonNode.getAttribute('aria-expanded') === 'true';\n }\n\n // Menu event handlers\n onFocusin() {\n this.domNode.classList.add('focus');\n }\n\n onFocusout() {\n this.domNode.classList.remove('focus');\n }\n\n onButtonKeydown(event) {\n const key = event.key;\n let flag = false;\n\n switch (key) {\n case ' ':\n case 'Enter':\n case 'ArrowDown':\n case 'Down':\n this.openPopup();\n this.setFocusToFirstMenuitem();\n flag = true;\n break;\n\n case 'Esc':\n case 'Escape':\n this.closePopup();\n this.buttonNode.focus();\n flag = true;\n break;\n\n case 'Up':\n case 'ArrowUp':\n this.openPopup();\n this.setFocusToLastMenuitem();\n flag = true;\n break;\n\n default:\n break;\n }\n\n if (flag) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n onButtonClick(event) {\n if (this.isOpen()) {\n this.closePopup();\n this.buttonNode.focus();\n } else {\n this.openPopup();\n this.setFocusToFirstMenuitem();\n }\n\n event.stopPropagation();\n event.preventDefault();\n }\n\n onMenuitemKeydown(event) {\n const tgt = event.currentTarget;\n const key = event.key;\n let flag = false;\n\n function isPrintableCharacter(str) {\n return str.length === 1 && str.match(/\\S/);\n }\n\n if (event.ctrlKey || event.altKey || event.metaKey) {\n return;\n }\n\n if (event.shiftKey) {\n if (isPrintableCharacter(key)) {\n this.setFocusByFirstCharacter(tgt, key);\n flag = true;\n }\n\n if (event.key === 'Tab') {\n this.buttonNode.focus();\n this.closePopup();\n flag = true;\n }\n } else {\n switch (key) {\n case ' ':\n window.location.href = tgt.href;\n break;\n\n case 'Esc':\n case 'Escape':\n this.closePopup();\n this.buttonNode.focus();\n flag = true;\n break;\n\n case 'Up':\n case 'ArrowUp':\n this.setFocusToPreviousMenuitem(tgt);\n flag = true;\n break;\n\n case 'ArrowDown':\n case 'Down':\n this.setFocusToNextMenuitem(tgt);\n flag = true;\n break;\n\n case 'Home':\n case 'PageUp':\n this.setFocusToFirstMenuitem();\n flag = true;\n break;\n\n case 'End':\n case 'PageDown':\n this.setFocusToLastMenuitem();\n flag = true;\n break;\n\n case 'Tab':\n this.closePopup();\n break;\n\n default:\n if (isPrintableCharacter(key)) {\n this.setFocusByFirstCharacter(tgt, key);\n flag = true;\n }\n break;\n }\n }\n\n if (flag) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n onMenuitemMouseover(event) {\n const tgt = event.currentTarget;\n\n tgt.focus();\n }\n\n onBackgroundMousedown(event) {\n if (!this.domNode.contains(event.target)) {\n if (this.isOpen()) {\n this.closePopup();\n this.buttonNode.focus();\n }\n }\n }\n}\n\n// Initialize menu buttons\nwindow.addEventListener('load', function () {\n const menuButtons = document.querySelectorAll('.js-menu-button-links');\n\n for (let i = 0; i < menuButtons.length; i++) {\n new MenuButtonLinks(menuButtons[i]);\n }\n});\n"]}