PageSourceSearch

https://www.chestnet.org/assets/chestnet/js/main.nav.js?v=2.0.9762.22084

js chestnet.org collected 2026-09-24 08:36:14 UTC 29,097 bytes, 884 lines download raw bytes

1document.addEventListener("DOMContentLoaded", () => {
2
3    initStickyHeader();
4    initDesktopSearch();
5    initMobileSearch();
6    initMobileAccount();
7    initLeftNav();
8
9    document.querySelectorAll(".js-primary-nav").forEach((el, i) => {
10        try {
11            initPrimaryNav(el, i);
12        } catch (ex) { }
13    });
14
15    document.querySelectorAll(".js-mega-menu").forEach((el, i) => {
16        try {
17            initMegaMenu(el, i);
18        } catch (ex) { }
19    });
20
21    document.querySelectorAll(".js-auxiliary-nav").forEach((el, i) => {
22        try {
23            initAuxNav(el, i);
24        } catch (ex) { }
25    });
26
27    document.querySelectorAll(".js-mobile-nav-menu").forEach((el, i) => {
28        initMobileNav(el, i);
29    });
30
31});
32
33var Timer = function (callback, delay) {
34    var timerId, start, remaining = delay;
35
36    this.pause = function () {
37        window.clearTimeout(timerId);
38        remaining -= Date.now() - start;
39    };
40
41    this.run = function () {
42        start = Date.now();
43        window.clearTimeout(timerId);
44        timerId = window.setTimeout(callback, remaining);
45    };
46};
47
48const websiteSearchQueryUpdated = new CustomEvent("websiteSearchQueryUpdated", {
49    detail: {},
50    bubbles: true,
51    cancelable: true,
52    composed: false,
53});
54
55function initStickyHeader() {
56    const headerContainer = document.querySelector("header");
57    const headerNavBar = document.querySelector(".js-global-header");
58
59    if (headerContainer != null && headerNavBar != null) {
60        if (headerNavBar.classList.contains("desktop-is-expanded")) {
61            headerContainer.classList.add("has-sub-menu");
62        }
63        if (!headerNavBar.classList.contains("no-menu")) {
64            headerContainer.classList.add("has-main-menu");
65        }
66        if (headerNavBar.classList.contains("slim-header")) {
67            headerContainer.classList.add("slim-header");
68        }
69        if (headerContainer.querySelector(".js-global-page-heading") != null) {
70            headerContainer.classList.add("has-page-heading");
71        }
72        if (headerContainer.querySelector(".js-global-alert-banner") != null) {
73            headerContainer.classList.add("has-alert-banner");
74        }
75        if (!headerContainer.classList.contains("sticky")) {
76            headerContainer.classList.add("sticky");
77        }
78    }
79};
80
81//Left Nav
82function initLeftNav() {
83    const toggleButton = document.querySelector("#leftnav__nav__toggle button");
84    const container = document.querySelector(".leftnav__container");
85    const pagecontent = document.querySelector("#main-content");
86
87    if (container != null && toggleButton != null) {
88
89        initEventHandlers();
90
91        if (container.classList.contains("js-enable-popout") && !container.classList.contains("active") && window.innerWidth > 768) {
92            try {
93                autoPopout();
94            } catch (ex) { }
95        }
96    }
97
98    function initEventHandlers() {
99
100        toggleButton.addEventListener("click", (e) => {
101            container.classList.toggle("active");
102            if (!container.classList.contains("active")) {
103                container.classList.remove("hover");
104            }
105        });
106        toggleButton.addEventListener("mouseover", (e) => {
107            container.classList.add("hover");
108        });
109        toggleButton.addEventListener("mouseout", (e) => {
110            container.classList.remove("hover");
111        });
112        pagecontent.addEventListener("click", (e) => {
113            container.classList.remove("active");
114            container.classList.remove("hover");
115        });
116    }
117
118    function autoPopout() {
119        const popoutTimer = new Timer(function () {
120            container.classList.remove("active");
121            container.classList.remove("timed-popout");
122        }, 4000);
123
124        setTimeout(() => {
125            container.classList.add("active");
126            container.classList.add("timed-popout");
127            popoutTimer.run();
128        }, 1000);
129
130        container.addEventListener("mouseover", function () {
131            if (container.classList.contains("timed-popout")) {
132                popoutTimer.pause();
133            }
134        });
135
136        container.addEventListener("mouseout", function () {
137            if (container.classList.contains("timed-popout")) {
138                popoutTimer.run();
139            }
140        });
141    }
142    
143};
144
145// -----------
146// Desktop Search
147// -----------
148function initDesktopSearch() {
149    const header = document.querySelector(".js-global-header__desktop");
150    const showBtn = document.querySelector(".js-auxiliary-nav__search-button");
151    const cancelBtn = document.querySelector(".js-desktop-search__cancel-button");
152    const searchInput = document.querySelector(".js-desktop-search__input");
153    const submitSearchBtn = document.querySelector(".js-desktop-search__submit-button");
154
155    initEventHandlers();
156
157    function initEventHandlers() {
158        showBtn.addEventListener("click", (e) => {
159            e.preventDefault();
160            header.classList.add("search-open");
161        });
162
163        submitSearchBtn.addEventListener("click", (e) => {
164            e.preventDefault();
165            if (searchInput.value.trim().length > 0) {
166                var url = submitSearchBtn.getAttribute("data-search-url");
167                var term = encodeURIComponent(searchInput.value).replace("+","%20");
168                window.location = url + term;
169                submitSearchBtn.dispatchEvent(websiteSearchQueryUpdated);
170            }
171        });
172
173        cancelBtn.addEventListener("click", (e) => {
174            e.preventDefault();
175            searchInput.value = "";
176            header.classList.remove("search-open");
177        });
178
179        searchInput.addEventListener("keydown", (e) => {
180            if (e.key == "Enter") {
181                submitSearchBtn.click();
182            }
183        });
184    }
185};
186
187// -------------
188// Mobile Search
189// -------------
190function initMobileSearch() {
191    const body = document.querySelector("body");
192    const header = document.querySelector(".js-global-header__mobile");
193    const bottomNavClose = document.querySelector(".js-mobile-bottom-nav-close");
194
195    const showBtn = document.querySelector(".js-primary-nav-mobile-search");
196    const mobileSearch = document.querySelector(".js-mobile-search");    
197    const searchInput = document.querySelector(".js-mobile-search__input");
198    const submitSearchBtn = document.querySelector(".js-mobile-search__submit-button");
199
200    initEventHandlers();
201
202    function initEventHandlers() {
203        showBtn.addEventListener("click", (e) => {
204            header.classList.remove("navmenu-open");
205            header.classList.remove("account-open");
206            header.classList.add("search-open");
207            body.classList.add("mobile-header-open");
208            mobileMenuScrollToTop();
209        });
210
211        submitSearchBtn.addEventListener("click", (e) => {
212            e.preventDefault();
213            if (searchInput.value.trim().length > 0) {
214                var url = submitSearchBtn.getAttribute("data-search-url");
215                var term = encodeURIComponent(searchInput.value).replace("+", "%20");
216                window.location = url + term;
217                submitSearchBtn.dispatchEvent(websiteSearchQueryUpdated);
218                bottomNavClose.click();
219            }
220        });
221
222        searchInput.addEventListener("keydown", (e) => {
223            if (e.key == "Enter") {
224                submitSearchBtn.click();
225            }
226        });
227
228        // duplication from mobile nav component:
229        bottomNavClose.addEventListener("click", (e) => {
230            header.classList.remove("search-open");
231            body.classList.remove("mobile-header-open");
232        });
233    }
234
235
236};
237
238// -------------
239// Desktop Aux + My Account dropdown and Sign-in popup
240// -------------
241function initAuxNav(el, index) {
242    const auxNavButtons = document.querySelectorAll(".js-auxiliary-nav-button");
243    const loginPopup = document.querySelector(".js-log-in-popup-wrapper");
244    const globalHeader = document.querySelector(".js-global-header");
245
246    auxNavButtons.forEach((el, i) => {
247        el.addEventListener("keydown", (e) => {
248            handleAuxKeyDown(e, i);
249        });
250    });
251
252    let openMenuID = -1;
253
254    const onOpen = (i, margin) => {
255        if (openMenuID !== i) {
256            openMenuID = i;
257        }
258    };
259
260    const onClose = (i) => {
261        if (openMenuID === i) {
262            openMenuID = -1;
263        }
264    };
265
266    el.querySelectorAll(".js-auxiliary-nav-item").forEach((el, i) => {
267        const contentEl = el.querySelector(".js-auxiliary-nav-dropdown");
268        if (contentEl != null) {
269            NavMenu({
270                animationDelay: 500,
271                closed: true,
272                direction: "vertical",
273                menuButton: ".js-auxiliary-nav-button",
274                menuContainer: ".js-auxiliary-nav-dropdown",
275                navigationLink: ".js-auxiliary-nav-link",
276                openClass: "is-open",
277                onClose: () => onClose(i),
278                onOpen: () => onOpen(i, contentEl.offsetHeight),
279                parent: el,
280            });
281        }
282    });
283
284    function handleAuxKeyDown(event, index) {
285        switch (event.key) {
286            case "Left":
287            case "ArrowLeft":
288                if (index > 0) {
289                    event.preventDefault();
290                    auxNavButtons[index - 1].focus();
291                }
292                break;
293            case "Right":
294            case "ArrowRight":
295                if (index < auxNavButtons.length - 1) {
296                    event.preventDefault();
297                    auxNavButtons[index + 1].focus();
298                }
299                break;
300            default:
301        }
302    }
303
304    if (loginPopup != null && globalHeader.classList.contains("js-enable-signin") && window.location.pathname.toLowerCase().replace("%20", "-").indexOf("/my-account/") === -1) {
305
306        const popupTimer = new Timer(function () {
307            loginPopup.classList.remove("show-popup");
308        }, 10000);
309
310        setTimeout(() => {
311            loginPopup.classList.add("show-popup");
312            popupTimer.run();
313        }, 1000);
314
315        loginPopup.addEventListener("mouseover", function () {
316            popupTimer.pause();
317        });
318
319        loginPopup.addEventListener("mouseout", function () {
320            popupTimer.run();
321        });
322    }
323
324};
325
326// --------------
327// Mobile Account
328// --------------
329function initMobileAccount() {
330    const body = document.querySelector("body");
331    const header = document.querySelector(".js-global-header__mobile");
332    const bottomNavClose = document.querySelector(".js-mobile-bottom-nav-close");
333
334    const accountButton = document.querySelector(".js-primary-nav-mobile-account");
335
336
337    accountButton.addEventListener("click", (e) => {
338        header.classList.remove("navmenu-open");
339        header.classList.remove("search-open");
340        header.classList.add("account-open");
341        body.classList.add("mobile-header-open");
342        mobileMenuScrollToTop();
343    });
344
345    bottomNavClose.addEventListener("click", (e) => {
346        header.classList.remove("account-open");
347        body.classList.remove("mobile-header-open");
348    });
349};
350
351// -----------
352// Primary Nav
353// -----------
354function initPrimaryNav(el, index) {
355    const globalHeaderEl = document.querySelector(".js-global-header");
356    const primaryNavEl = document.querySelector(".js-primary-nav-menu");
357    const megaMenuHeight = document.querySelector(".js-mega-menu").offsetHeight;
358
359    //if (primaryNavEl.classList.contains("is-open")) {
360    //    globalHeaderEl.style.marginBottom = `${megaMenuHeight}px`;
361    //    globalHeaderEl.classList.add("is-expanded");
362    //} else {
363    //    globalHeaderEl.classList.remove("is-expanded");
364    //    globalHeaderEl.style.marginBottom = 0;
365    //}
366
367    const primaryLinks = el.querySelectorAll(".js-primary-nav-top-link");
368
369    primaryLinks.forEach((el, i) => {
370        el.addEventListener("keydown", (e) => {
371            handleKeyDown(e, i);
372        });
373    });
374
375    function handleKeyDown(event, index) {
376        switch (event.key) {
377            case "Left":
378            case "ArrowLeft":
379                if (index > 0) {
380                    event.preventDefault();
381                    primaryLinks[index - 1].focus();
382                }
383                break;
384            case "Right":
385            case "ArrowRight":
386                if (index < primaryLinks.length - 1) {
387                    event.preventDefault();
388                    primaryLinks[index + 1].focus();
389                }
390                break;
391            default:
392        }
393    }
394};
395
396// ---------
397// Mega Menu
398// ---------
399function initMegaMenu(el, index) {
400    let openMenuID = -1;
401
402    const onOpen = (i, margin) => {
403        if (openMenuID !== i) {
404            openMenuID = i;
405        }
406    };
407
408    const onClose = (i) => {
409        if (openMenuID === i) {
410            openMenuID = -1;
411        }
412    };
413
414    const megaLinks = el.querySelectorAll(".js-mega-menu-submenu-button");
415
416    megaLinks.forEach((el, i) => {
417        el.addEventListener("keydown", (e) => {
418            handleKeyDown(e, i);
419        });
420    });
421
422    el.querySelectorAll(".js-mega-menu-menu").forEach((el, i) => {
423        const contentEl = el.querySelector(".js-mega-menu-submenu");
424        if (contentEl != null) {
425            NavMenu({
426                animationDelay: 500,
427                closed: true,
428                direction: "vertical",
429                menuButton: ".js-mega-menu-submenu-button",
430                menuContainer: ".js-mega-menu-submenu",
431                navigationLink: ".js-primary-nav-sub-link",
432                openClass: "is-open",
433                onClose: () => onClose(i),
434                onOpen: () => onOpen(i, contentEl.offsetHeight),
435                parent: el,
436            });
437        }
438    });
439
440    function handleKeyDown(event, index) {
441        switch (event.key) {
442            case "Left":
443            case "ArrowLeft":
444                if (index > 0) {
445                    event.preventDefault();
446                    primaryLinks[index - 1].focus();
447                }
448                break;
449            case "Right":
450            case "ArrowRight":
451                if (index < primaryLinks.length - 1) {
452                    event.preventDefault();
453                    primaryLinks[index + 1].focus();
454                }
455                break;
456            default:
457        }
458    }
459};
460
461// ----------
462// Mobile Nav
463// ----------
464function initMobileNav(el, index) {
465    const body = document.querySelector("body");
466    const header = document.querySelector(".js-global-header__mobile");
467    const navBack = document.querySelector(".js-mobile-bottom-nav-back");
468
469    const submenuLink = el.querySelector(".js-mobile-nav-top-link");
470    const submenu = submenuLink.parentNode.querySelector(".js-mobile-nav-submenu");
471    const submenuLinks = document.querySelectorAll(".js-mobile-nav-top-link");
472    const submenus = document.querySelectorAll(".js-mobile-nav-submenu");
473
474    const tertmenus = document.querySelectorAll(".js-mobile-nav-tertmenu");
475    const tertmenuLinks = el.querySelectorAll(".js-mobile-nav-sub-link");
476
477    const bottomMenu = document.querySelector(".js-mobile-bottom-menu");
478    const bottomNav = document.querySelector(".js-mobile-bottom-nav");
479    const mobileNav = document.querySelector(".js-mobile-nav");
480    const menuBtn = document.querySelector(".js-primary-nav-mobile-menu");
481
482    initBottomMenu();
483    initBottomNav();
484
485    submenuLink.addEventListener("click", (e) => {
486        navBack.style.visibility = "visible";
487
488        // add submenu
489        handleSubmenu(submenuLinks, submenu);
490    });
491
492    function handleSubmenu(submenuLinks, submenu) {
493        if (submenu.classList.contains("mobile-nav__submenu-container--display")) {
494            // remove submenu
495            submenu.classList.remove("mobile-nav__submenu-container--display");
496            ariaExpandToggle(null, submenu);
497        } else {
498            // add submenu
499            submenu.classList.add("mobile-nav__submenu-container--display");
500            ariaExpandToggle(null, submenu);
501        }
502
503        submenuLinks.forEach((link, i) => {
504            if (link.classList.contains("mobile-nav__top-link--hidden")) {
505                // add main menu links back
506                link.classList.remove("mobile-nav__top-link--hidden");
507            } else {
508                // remove main menu links
509                link.classList.add("mobile-nav__top-link--hidden");
510            }
511        });
512
513        mobileMenuScrollToTop();
514    }
515
516    tertmenuLinks.forEach((link, i) => {
517        const tertmenu = link.parentNode.querySelector(".js-mobile-nav-tertmenu");
518        const parentSubmenu = link.parentNode.parentNode.parentNode;
519
520        link.addEventListener("click", (e) => {
521            // add tertiary menu
522            handleTertmenu(parentSubmenu, tertmenu);
523        });
524    });
525
526    function handleTertmenu(parentSubmenu, tertmenu) {
527        const tertmenuLink = tertmenu.previousElementSibling;
528
529        if (tertmenu.classList.contains("mobile-nav__tertmenu-container--display")) {
530            tertmenu.classList.remove("mobile-nav__tertmenu-container--display");
531            parentSubmenu.classList.add("mobile-nav__submenu-container--display");
532            ariaExpandToggle(null, parentSubmenu)
533        } else {
534            tertmenu.classList.add("mobile-nav__tertmenu-container--display");
535            parentSubmenu.classList.remove("mobile-nav__submenu-container--display");
536            ariaExpandToggle(null, parentSubmenu)
537        }
538
539        ariaExpandToggle(tertmenuLink);
540
541        mobileMenuScrollToTop();
542    }
543
544    function ariaExpandToggle(menuLinkIn, menuIn) {
545        const menu = menuIn || menuLinkIn.nextElementSibling;
546        const menuLink = menuLinkIn || menuIn.previousElementSibling;
547
548        if (menuLink.hasAttribute("aria-expanded")) {
549            if (menuLink.getAttribute("aria-expanded") === "false") {
550                menuLink.setAttribute("aria-expanded", true);
551            }
552            else {
553                menuLink.setAttribute("aria-expanded", false);
554            }
555        }
556
557        if (menu.hasAttribute("aria-hidden")) {
558            if (menu.getAttribute("aria-hidden") === "true") {
559                menu.setAttribute("aria-hidden", false)
560            }
561            else {
562                menu.setAttribute("aria-hidden", true)
563            }
564        }
565    }
566
567    function initBottomMenu() {
568        if (!bottomMenu.classList.contains("click-handler")) {
569            bottomMenu.classList.add("click-handler");
570            //mobileNav.classList.add("mobile-nav--hidden");
571            navBack.style.visibility = "hidden";
572
573            menuBtn.addEventListener("click", (e) => {
574                header.classList.remove("search-open");
575                header.classList.remove("account-open");
576                header.classList.add("navmenu-open");
577                body.classList.add("mobile-header-open");
578                //mobileNav.classList.remove("mobile-nav--hidden");
579                mobileMenuScrollToTop();
580            });
581        }
582    }
583
584    function initBottomNav() {
585        if (!bottomNav.classList.contains("click-handler")) {
586            bottomNav.classList.add("click-handler");
587            const bottomNavBack = document.querySelector(".js-mobile-bottom-nav-back");
588            const bottomNavClose = document.querySelector(".js-mobile-bottom-nav-close");
589
590            bottomNavBack.addEventListener("click", (e) => {
591                const activeSubmenu = document.querySelector(".mobile-nav__submenu-container--display");
592                const activeTertmenu = document.querySelector(".mobile-nav__tertmenu-container--display");
593
594                // if user is on submenu
595                if (activeSubmenu) {
596                    navBack.style.visibility = "hidden";
597
598                    // remove submenu
599                    handleSubmenu(submenuLinks, activeSubmenu);
600
601                    // if user is on tertiary menu
602                } else if (activeTertmenu) {
603                    const tertmenuParentSubmenu = activeTertmenu.parentNode.parentNode.parentNode;
604
605                    // remove tertiary menu
606                    handleTertmenu(tertmenuParentSubmenu, activeTertmenu);
607                } else {
608                    mobileMenuScrollToTop();
609                }
610            });
611
612            bottomNavClose.addEventListener("click", (e) => {
613                //mobileNav.classList.add("mobile-nav--hidden");
614                header.classList.remove("navmenu-open");
615                body.classList.remove("mobile-header-open");
616            });
617
618            //bottomNav.style.display = "none";
619        };
620    }
621};
622
623function mobileMenuScrollToTop() {
624    document.querySelector(".sub-nav__mobile-container").scrollTop = 0;
625};
626
627// --------
628// Nav Menu
629// --------
630let timeoutId = window.setTimeout(() => { }, 0);
631
632function NavMenu(initProps) {
633    let propsFail = false;
634
635    if (!initProps.menuButton || initProps.menuButton.length === 0) {
636        console.error("'menuButton' selector is required");
637        propsFail = true;
638    }
639    if (!initProps.menuContainer || initProps.menuContainer.length === 0) {
640        console.error("'menuContainer' selector is required");
641        propsFail = true;
642    }
643    if (!initProps.navigationLink || initProps.navigationLink.length === 0) {
644        console.error("'navigationLink' selector is required");
645        propsFail = true;
646    }
647    if (!initProps.parent) {
648        console.error("'parent' node element is required");
649        propsFail = true;
650    }
651
652    if (propsFail) {
653        return;
654    }
655
656    const defaultProps = {
657        animationDelay: 600,
658        closed: true,
659        direction: "vertical",
660        reversed: false,
661        openClass: "is-open",
662    };
663
664    const props = Object.assign({}, defaultProps, initProps);
665
666    let closedState = props.closed;
667    let direction = props.direction;
668    let animationDelay = props.animationDelay;
669
670    const elParent = props.parent;
671
672    const elMenuButton = elParent.querySelector(props.menuButton);
673    const elMenuContainer = elParent.querySelector(props.menuContainer);
674    const elMenuLinks = elMenuContainer.querySelectorAll(props.navigationLink);
675
676    bindEvents();
677    init();
678
679    function init() {
680        const controlId = elMenuContainer.id;
681        const buttonId = elMenuButton.id;
682
683        elMenuButton.id = buttonId;
684        elMenuButton.setAttribute("aria-haspopup", true);
685        elMenuButton.setAttribute("aria-controls", controlId);
686
687        elMenuContainer.id = controlId;
688        elMenuContainer.setAttribute("aria-labelledby", buttonId);
689
690        if (closedState) {
691            closeMenu(false);
692        } else {
693            openMenu(false);
694        }
695    }
696
697    function bindEvents() {
698        // need to run this on mousedown to properly allow the focus state to move.
699        elMenuButton.addEventListener("mousedown", (e) => {
700            e.preventDefault();
701            toggleMenu();
702        });
703
704        elMenuButton.addEventListener("keydown", handleMenuKeyEvent);
705
706        [...elMenuLinks].forEach((link, index) => {
707            link.addEventListener("keydown", (e) => {
708                handleLinkKeyEvent(e, link, index);
709            });
710        });
711    }
712
713    function setDirection(newDirection) {
714        direction = newDirection;
715    }
716
717    function clickAway(e) {
718        if (!elParent.contains(e.target) && !closedState) {
719            closeMenu(false);
720        }
721    }
722
723    function toggleMenu(moveFocus) {
724        if (closedState) {
725            openMenu(moveFocus);
726        } else {
727            closeMenu(moveFocus);
728        }
729    }
730
731    function openMenu(moveFocus = true) {
732        closedState = false;
733        elMenuButton.setAttribute("aria-expanded", true);
734        elParent.classList.add(props.openClass);
735        elMenuContainer.removeAttribute("aria-hidden");
736
737        if (moveFocus) {
738            window.clearTimeout(timeoutId);
739
740            timeoutId = setTimeout(() => {
741                elMenuLinks[0] && elMenuLinks[0].focus();
742            }, animationDelay);
743        }
744
745        if (props.onOpen) {
746            props.onOpen();
747        }
748
749        window.addEventListener("click", clickAway);
750        window.addEventListener("resize", closeMenu);
751
752        if (moveFocus) {
753            window.dataLayer = window.dataLayer || [];
754            window.dataLayer.push({
755                type: "Menu Opened",
756                event: "dropdownClick",
757                dropdownSelection: elMenuButton.innerText,
758            });
759        }
760    }
761
762    function closeMenu(moveFocus = true) {
763        closedState = true;
764        if (moveFocus) {
765            elMenuButton && elMenuButton.focus();
766        }
767        elMenuButton.setAttribute("aria-expanded", false);
768        elParent.classList.remove(props.openClass);
769        elMenuContainer.setAttribute("aria-hidden", true);
770
771        if (props.onClose) {
772            props.onClose();
773        }
774
775        window.removeEventListener("click", clickAway);
776        window.removeEventListener("resize", closeMenu);
777
778        if (moveFocus) {
779            window.dataLayer = window.dataLayer || [];
780            window.dataLayer.push({
781                type: "Menu Closed",
782                event: "dropdownClick",
783                dropdownSelection: elMenuButton.innerText,
784            });
785        }
786    }
787
788    function handleMenuKeyEvent(e) {
789        switch (e.key) {
790            case "Escape":
791                e.preventDefault();
792                closeMenu();
793                break;
794            case "Enter":
795            case " ":
796                e.preventDefault();
797                toggleMenu(true);
798                break;
799            default:
800        }
801    }
802
803    function handleLinkKeyEvent(e, link, index) {
804        let target = index;
805
806        switch (e.key) {
807            case "Escape":
808                e.preventDefault();
809                closeMenu(true);
810                break;
811            case "Tab":
812                if (e.shiftKey) {
813                    if (index === 0) {
814                        if (!props.reversed) {
815                            e.preventDefault();
816                        }
817                        closeMenu(!props.reversed);
818                    }
819                } else {
820                    if (index === elMenuLinks.length - 1) {
821                        if (props.reversed) {
822                            e.preventDefault();
823                        }
824                        closeMenu(props.reversed);
825                    }
826                }
827                break;
828            default:
829        }
830
831        if (direction === "vertical") {
832            switch (e.key) {
833                case "Up":
834                case "ArrowUp":
835                    e.preventDefault();
836                    focusPrevious(elMenuLinks, index, target);
837                    break;
838                case "Down":
839                case "ArrowDown":
840                    e.preventDefault();
841                    focusNext(elMenuLinks, index, target);
842                    break;
843                default:
844            }
845        }
846        if (direction === "horizontal") {
847            switch (e.key) {
848                case "Left":
849                case "ArrowLeft":
850                    e.preventDefault();
851                    focusPrevious(elMenuLinks, index, target);
852                    break;
853                case "Right":
854                case "ArrowRight":
855                    e.preventDefault();
856                    focusNext(elMenuLinks, index, target);
857                    break;
858                default:
859            }
860        }
861
862        function focusPrevious(elList, index, target) {
863            if (index === 0) {
864                target = elMenuLinks.length;
865            }
866            elMenuLinks[target - 1] && elMenuLinks[target - 1].focus();
867        }
868
869        function focusNext(elList, index, target) {
870            if (index === elMenuLinks.length - 1) {
871                target = -1;
872            }
873            elMenuLinks[target + 1] && elMenuLinks[target + 1].focus();
874        }
875    }
876
877    return {
878        init,
879        toggleMenu,
880        openMenu,
881        closeMenu,
882        setDirection,
883    };
884}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.