PageSourceSearch

https://alpaswellnesscenters.org/wp-content/plugins/megamenu/js/maxmegamenu.js?ver=3.10.5

js alpaswellnesscenters.org collected 2026-09-28 10:45:11 UTC 47,795 bytes, 1,078 lines download raw bytes

1/*jslint browser: true, white: true, this: true, long: true */
2/*global console,jQuery,megamenu,window,navigator*/
3
4/*! Max Mega Menu jQuery Plugin */
5(function ( $ ) {
6    "use strict";
7
8    let instanceCounter = 0;
9
10    $.maxmegamenu = function(menu, options) {
11
12        // --- DOM references ---
13        const plugin       = this;
14        const $menu        = $(menu);
15        const $wrap        = $menu.parent();
16        const $toggle_bar  = $menu.siblings(".mega-menu-toggle");
17
18        // --- Instance identifiers ---
19        const menuId            = $menu.attr("id");
20        const instanceId        = menuId + '-' + (++instanceCounter);
21        const docEventNamespace = '.megamenu-' + instanceId;
22
23        // --- Cached element sets (captured at init) ---
24        const items_with_submenus = $([
25            "li.mega-menu-megamenu.mega-menu-item-has-children",
26            "li.mega-menu-flyout.mega-menu-item-has-children",
27            "li.mega-menu-tabbed > ul.mega-sub-menu > li.mega-menu-item-has-children",
28            "li.mega-menu-flyout li.mega-menu-item-has-children"
29        ].join(","), $menu);
30
31        const collapse_children_parents = $("li.mega-menu-megamenu li.mega-menu-item-has-children.mega-collapse-children > a.mega-menu-link", $menu);
32
33        // --- Keyboard key identifiers (KeyboardEvent.key values) ---
34        const tab_key         = "Tab";
35        const escape_key      = "Escape";
36        const enter_key       = "Enter";
37        const space_key       = " ";
38        const left_arrow_key  = "ArrowLeft";
39        const up_arrow_key    = "ArrowUp";
40        const right_arrow_key = "ArrowRight";
41        const down_arrow_key  = "ArrowDown";
42
43        // --- Settings defaults (read from data attributes) ---
44        const defaults = {
45            event:                $menu.attr("data-event"),
46            effect:               $menu.attr("data-effect"),
47            effect_speed:         parseInt($menu.attr("data-effect-speed")),
48            effect_mobile:        $menu.attr("data-effect-mobile"),
49            effect_speed_mobile:  parseInt($menu.attr("data-effect-speed-mobile")),
50            panel_width:          $menu.attr("data-panel-width"),
51            panel_inner_width:    $menu.attr("data-panel-inner-width"),
52            mobile_force_width:   $menu.attr("data-mobile-force-width"),
53            mobile_overlay:       $menu.attr("data-mobile-overlay"),
54            mobile_state:         $menu.attr("data-mobile-state"),
55            mobile_direction:     $menu.attr("data-mobile-direction"),
56            second_click:         $menu.attr("data-second-click"),
57            vertical_behaviour:   $menu.attr("data-vertical-behaviour"),
58            document_click:       $menu.attr("data-document-click"),
59            breakpoint:           $menu.attr("data-breakpoint"),
60            unbind_events:        $menu.attr("data-unbind"),
61            hover_intent_timeout: $menu.attr("data-hover-intent-timeout") ?? 300,
62            hover_intent_interval: $menu.attr("data-hover-intent-interval") ?? 100
63        };
64
65        // --- Mutable state ---
66        plugin.settings = {};
67        let html_body_class_timeout;
68
69        plugin.addAnimatingClass = function(element) {
70            if (plugin.settings.effect === "disabled") {
71                return;
72            }
73
74            $(".mega-animating", $wrap).removeClass("mega-animating");
75
76            const timeout = plugin.settings.effect_speed + parseInt(plugin.settings.hover_intent_timeout, 10);
77
78            element.addClass("mega-animating");
79
80            setTimeout(function() {
81               element.removeClass("mega-animating");
82            }, timeout );
83        };
84
85        plugin.hideAllPanels = function() {
86            $(".mega-toggle-on > a.mega-menu-link", $menu).each(function() {
87                plugin.hidePanel($(this), false);
88            });
89        };
90
91        plugin.expandMobileSubMenus = function() {
92            if (plugin.settings.mobile_direction !== 'vertical') {
93                return;
94            }
95            
96            $(".mega-menu-item-has-children.mega-expand-on-mobile > a.mega-menu-link", $menu).each(function() {
97                plugin.showPanel($(this), true);
98            });
99
100            if ( plugin.settings.mobile_state === 'expand_all' ) {
101                $(".mega-menu-item-has-children:not(.mega-toggle-on) > a.mega-menu-link", $menu).each(function() {
102                    plugin.showPanel($(this), true);
103                });
104            }
105
106            if ( plugin.settings.mobile_state === 'expand_active' ) {
107                const activeItemSelectors = [
108                    "li.mega-current-menu-ancestor.mega-menu-item-has-children > a.mega-menu-link",
109                    "li.mega-current-menu-item.mega-menu-item-has-children > a.mega-menu-link",
110                    "li.mega-current-menu-parent.mega-menu-item-has-children > a.mega-menu-link",
111                    "li.mega-current_page_ancestor.mega-menu-item-has-children > a.mega-menu-link",
112                    "li.mega-current_page_item.mega-menu-item-has-children > a.mega-menu-link"
113                ];
114
115                $menu.find(activeItemSelectors.join(', ')).each(function() {
116                    plugin.showPanel($(this), true);
117                });
118            }
119        };
120
121        plugin.hideSiblingPanels = function(anchor, immediate) {
122            anchor.parent().parent().find(".mega-toggle-on").children("a.mega-menu-link").each(function() { // all open children of open siblings
123                plugin.hidePanel($(this), immediate);
124            });
125        };
126
127        plugin.isDesktopView = function() {
128            const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
129            return width > plugin.settings.breakpoint;
130        };
131
132        plugin.isMobileView = function() {
133            return !plugin.isDesktopView();
134        };
135
136        plugin.isHorizontalMobileSubmenuMode = function() {
137            return plugin.isMobileView() && plugin.isMobileOffCanvas() && plugin.settings.mobile_direction === "horizontal";
138        };
139
140        plugin.getFocusableItemsInSubmenu = function($submenu, include_back_link = true) {
141            let $focusable = $submenu
142                .find("a.mega-menu-link, button.mega-menu-link, .mega-search span[role=button]")
143                .filter(":visible");
144
145            if (!include_back_link) {
146                $focusable = $focusable.not(".mega-mobile-back-link");
147            }
148
149            return $focusable;
150        };
151
152        plugin.focusFirstItemInOpenedSubmenu = function($item) {
153            if ( ! plugin.isHorizontalMobileSubmenuMode() || ! $wrap.hasClass("mega-keyboard-navigation")) {
154                return;
155            }
156
157            const $submenu = $item.children("ul.mega-sub-menu");
158
159            if (!$submenu.length) {
160                return;
161            }
162
163            // Search all descendants, not just direct li.mega-menu-item children, so that
164            // megamenu panels (where links are nested inside li.mega-menu-row > li.mega-menu-column)
165            // are handled the same as flat flyout panels.
166            const $firstFocusable = $submenu
167                .find("a.mega-menu-link, button.mega-menu-link, .mega-search span[role=button]")
168                .not(".mega-mobile-back-link")
169                .filter(":visible")
170                .first();
171
172            if ($firstFocusable.length) {
173                $firstFocusable.trigger("focus");
174            } else {
175                $submenu.find("> li.mega-mobile-back:visible > button.mega-menu-link.mega-mobile-back-link").first().trigger("focus");
176            }
177        };
178
179        plugin.deferFocusFirstItemInOpenedSubmenu = function($item) {
180            const delay = Math.min(120, parseInt(plugin.settings.effect_speed_mobile, 10) || 0);
181
182            setTimeout(function() {
183                plugin.focusFirstItemInOpenedSubmenu($item);
184
185                // Retry once after the first paint in case CSS visibility/transform has not applied yet.
186                setTimeout(function() {
187                    const focusedInSubmenu = $item.find("ul.mega-sub-menu").has(document.activeElement).length !== 0;
188
189                    if (!focusedInSubmenu) {
190                        plugin.focusFirstItemInOpenedSubmenu($item);
191                    }
192                }, 40);
193            }, delay);
194        };
195
196        plugin.showPanel = function(anchor, immediate) {
197            if ( typeof anchor === 'number' || ( typeof anchor === 'string' && anchor.trim() !== '' && !isNaN(anchor) ) ) {
198                anchor = $("li.mega-menu-item-" + anchor, $menu).find("a.mega-menu-link").first();
199            } else if ( anchor.is("li.mega-menu-item") ) {
200                anchor = anchor.find("a.mega-menu-link").first();
201            }
202
203            const $item = anchor.parent();
204            const isDesktop = plugin.isDesktopView();
205            const isMobile = !isDesktop;
206
207            $item.triggerHandler("before_open_panel");
208
209            $item.find("[aria-expanded]").first().attr("aria-expanded", "true");
210
211            $(".mega-animating", $wrap).removeClass("mega-animating");
212
213            if (isMobile && $item.hasClass("mega-hide-sub-menu-on-mobile")) {
214                return;
215            }
216
217            if (isDesktop && ( $menu.hasClass("mega-menu-horizontal") || $menu.hasClass("mega-menu-vertical") ) && !$item.hasClass("mega-collapse-children")) {
218                plugin.hideSiblingPanels(anchor, true);
219            }
220
221            if ((isMobile && $wrap.hasClass("mega-keyboard-navigation")) || plugin.settings.vertical_behaviour === "accordion") {
222                plugin.hideSiblingPanels(anchor, false);
223            }
224
225            plugin.calculateDynamicSubmenuWidths(anchor);
226
227            // apply jQuery transition (only if the effect is set to "slide", other transitions are CSS based)
228            if ( plugin.shouldUseSlideAnimation(anchor, immediate) ) {
229                const speed = isMobile ? plugin.settings.effect_speed_mobile : plugin.settings.effect_speed;
230
231                anchor.siblings(".mega-sub-menu").css("display", "none").animate({"height":"show", "paddingTop":"show", "paddingBottom":"show", "minHeight":"show"}, speed, function() {
232                    $(this).css("display", "");
233                });
234            }
235
236            $item.addClass("mega-toggle-on").triggerHandler("open_panel");
237            plugin.deferFocusFirstItemInOpenedSubmenu($item);
238        };
239        
240
241        plugin.shouldUseSlideAnimation = function(anchor, immediate) {
242
243            if (immediate === true) {
244                return false;
245            }
246
247            if (anchor.parent().hasClass("mega-collapse-children")) {
248                return true;
249            }
250
251            const isDesktop = plugin.isDesktopView();
252
253            if (isDesktop && plugin.settings.effect === "slide") {
254                return true;
255            }
256
257            if (!isDesktop) {
258                if (plugin.settings.effect_mobile === "slide") {
259                    return true;
260                }
261
262                if ( plugin.isMobileOffCanvas() ) {
263                    return plugin.settings.mobile_direction !== "horizontal";
264                }
265            }
266
267            return false;
268        };
269
270
271        plugin.hidePanel = function(anchor, immediate) {
272            if ( typeof anchor === 'number' || ( typeof anchor === 'string' && anchor.trim() !== '' && !isNaN(anchor) ) ) {
273                anchor = $("li.mega-menu-item-" + anchor, $menu).find("a.mega-menu-link").first();
274            } else if ( anchor.is("li.mega-menu-item") ) {
275                anchor = anchor.find("a.mega-menu-link").first();
276            }
277
278            const $item = anchor.parent();
279            const $submenu = anchor.siblings(".mega-sub-menu");
280            const isMobile = plugin.isMobileView();
281
282            $item.triggerHandler("before_close_panel");
283
284            $item.find("[aria-expanded]").first().attr("aria-expanded", "false");
285
286            if ( plugin.shouldUseSlideAnimation(anchor) ) {
287                const speed = isMobile ? plugin.settings.effect_speed_mobile : plugin.settings.effect_speed;
288
289                $submenu.animate({"height":"hide", "paddingTop":"hide", "paddingBottom":"hide", "minHeight":"hide"}, speed, function() {
290                    $submenu.css("display", "");
291                    $item.removeClass("mega-toggle-on").triggerHandler("close_panel");
292                });
293
294                return;
295            }
296
297            if (immediate) {
298                $submenu.css("display", "none").delay(plugin.settings.effect_speed).queue(function(){
299                    $(this).css("display", "").dequeue();
300                });
301            }
302
303            // pause video widget videos
304            $submenu.find(".widget_media_video video").each(function() {
305                if ( this.player ) {
306                    this.player.pause();
307                }
308            });
309
310            $item.removeClass("mega-toggle-on").triggerHandler("close_panel");
311            plugin.addAnimatingClass($item);
312        };
313
314        plugin.calculateDynamicSubmenuWidths = function(anchor) {
315            const $item = anchor.parent();
316            const $submenu = anchor.siblings(".mega-sub-menu");
317            const isDesktop = plugin.isDesktopView();
318            const isTopLevelMegamenu = $item.hasClass("mega-menu-megamenu") && $item.parent().hasClass("max-mega-menu");
319
320            // apply dynamic width and sub menu position (only to top level mega menus)
321            if (isTopLevelMegamenu && plugin.settings.panel_width) {
322                if (isDesktop) {
323                    const submenu_offset = $menu.offset();
324
325                    if ( plugin.settings.panel_width === '100vw' ) {
326                        const target_offset = $('body').offset();
327
328                        $submenu.css({
329                            left: (target_offset.left - submenu_offset.left) + "px"
330                        });
331                    } else {
332                        const $panel_width_el = plugin.settings.panel_width === '$menu' ? $menu : plugin.settings.panel_width === '$wrap' ? $wrap : $(plugin.settings.panel_width);
333
334                        if ( $panel_width_el.length > 0 ) {
335                            $submenu.css({
336                                width: $panel_width_el.outerWidth(),
337                                left: ($panel_width_el.offset().left - submenu_offset.left) + "px"
338                            });
339                        }
340                    }
341                } else {
342                    $submenu.css({
343                        width: "",
344                        left: ""
345                    });
346                }
347            }
348
349            // apply inner width to sub menu by adding padding to the left and right of the mega menu
350            if (isTopLevelMegamenu && plugin.settings.panel_inner_width) {
351                const $panel_inner_width_el = plugin.settings.panel_inner_width === '$menu' ? $menu : plugin.settings.panel_inner_width === '$wrap' ? $wrap : $(plugin.settings.panel_inner_width);
352
353                if ($panel_inner_width_el.length > 0) {
354                    const target_width = parseInt($panel_inner_width_el.width(), 10);
355
356                    $submenu.css({
357                        "paddingLeft": "",
358                        "paddingRight": ""
359                    });
360
361                    const submenu_width = parseInt($submenu.innerWidth(), 10);
362
363                    if (isDesktop && target_width > 0 && target_width < submenu_width) {
364                        $submenu.css({
365                            "paddingLeft": (submenu_width - target_width) / 2 + "px",
366                            "paddingRight": (submenu_width - target_width) / 2 + "px"
367                        });
368                    }
369                }
370            }
371        };
372
373        plugin.bindClickEvents = function() {
374
375            plugin.unbindClickEvents();
376
377            let dragging = false;
378
379            $(document).on({
380                ["touchmove" + docEventNamespace]: function() { dragging = true; },
381                ["touchstart" + docEventNamespace]: function() { dragging = false; }
382            });
383
384            $(document).on("click" + docEventNamespace + " touchend" + docEventNamespace, function(e) { // hide menu when clicked away from
385                if (!dragging && plugin.settings.document_click === "collapse" && ! $(e.target).closest(".mega-menu-wrap").length ) {
386                    plugin.hideAllPanels();
387                    plugin.hideMobileMenu();
388                }
389                dragging = false;
390            });
391
392            collapse_children_parents.off("click.megamenu touchend.megamenu");
393            const clickable_parents = $("> a.mega-menu-link", items_with_submenus).add(collapse_children_parents);
394
395            clickable_parents.on("touchend.megamenu", function(e) {
396                if (plugin.settings.event === "hover_intent") {
397                    plugin.unbindHoverIntentEvents();
398                }
399
400                if (plugin.settings.event === "hover") {
401                    plugin.unbindHoverEvents();
402                }
403            });
404
405            clickable_parents.on("click.megamenu", function(e) {
406                if ( $(e.target).hasClass('mega-indicator') ) {
407                    return;
408                }
409
410                if (plugin.isDesktopView() && $(this).parent().hasClass("mega-toggle-on") && $(this).closest("ul.mega-sub-menu").parent().hasClass("mega-menu-tabbed") ) {
411                    if (plugin.settings.second_click === "go") {
412                        return;
413                    } else {
414                        e.preventDefault();
415                        return;
416                    }
417                }
418                if (dragging) {
419                    return;
420                }
421                if (plugin.isMobileView() && $(this).parent().hasClass("mega-hide-sub-menu-on-mobile")) {
422                    return; // allow all clicks on parent items when sub menu is hidden on mobile
423                }
424                if ((plugin.settings.second_click === "go" || $(this).parent().hasClass("mega-click-click-go")) && $(this).attr("href") !== undefined) { // check for second click
425                    if (!$(this).parent().hasClass("mega-toggle-on")) {
426                        e.preventDefault();
427                        plugin.showPanel($(this));
428                    }
429                } else {
430                    e.preventDefault();
431
432                    if ($(this).parent().hasClass("mega-toggle-on")) {
433                        plugin.hidePanel($(this), false);
434                    } else {
435                        plugin.showPanel($(this));
436                    }
437                }
438            });
439
440            if ( plugin.settings.second_click === "disabled" ) {
441                clickable_parents.off("click.megamenu");
442            }
443
444            $(".mega-close-after-click:not(.mega-menu-item-has-children) > a.mega-menu-link", $menu).on("click.megamenu", function() {
445                plugin.hideAllPanels();
446                plugin.hideMobileMenu();
447            });
448
449            $("button.mega-close", $wrap).on("click.megamenu", function(e) {
450                plugin.hideMobileMenu();
451            });
452        };
453
454        plugin.bindHoverEvents = function() {
455            items_with_submenus.on({
456                "mouseenter.megamenu" : function() {
457                    plugin.unbindClickEvents();
458                    if (! $(this).hasClass("mega-toggle-on")) {
459                        plugin.showPanel($(this).children("a.mega-menu-link"));
460                    }
461                },
462                "mouseleave.megamenu" : function() {
463                    if ($(this).hasClass("mega-toggle-on") && ! $(this).hasClass("mega-disable-collapse") && ! $(this).parent().parent().hasClass("mega-menu-tabbed")) {
464                        plugin.hidePanel($(this).children("a.mega-menu-link"), false);
465                    }
466                }
467            });
468        };
469
470        plugin.bindHoverIntentEvents = function() {
471            items_with_submenus.hoverIntent({
472                over: function () {
473                    plugin.unbindClickEvents();
474                    if (! $(this).hasClass("mega-toggle-on")) {
475                        plugin.showPanel($(this).children("a.mega-menu-link"));
476                    }
477                },
478                out: function () {
479                    if ($(this).hasClass("mega-toggle-on") && ! $(this).hasClass("mega-disable-collapse") && ! $(this).parent().parent().hasClass("mega-menu-tabbed")) {
480                        plugin.hidePanel($(this).children("a.mega-menu-link"), false);
481                    }
482                },
483                timeout: plugin.settings.hover_intent_timeout,
484                interval: plugin.settings.hover_intent_interval
485            });
486        };
487
488        plugin.isMobileOffCanvas = function() {
489            return plugin.settings.effect_mobile === 'slide_left' || plugin.settings.effect_mobile === 'slide_right';
490        };
491
492        plugin.shouldGoToNextTopLevelItem = function(key) {
493            return ( ( key === right_arrow_key && plugin.isDesktopView() ) || ( key === down_arrow_key && plugin.isMobileView() ) ) && $menu.hasClass("mega-menu-horizontal");
494        };
495
496        plugin.shouldGoToPreviousTopLevelItem = function(key) {
497            return ( ( key === left_arrow_key && plugin.isDesktopView() ) || ( key === up_arrow_key && plugin.isMobileView() ) ) && $menu.hasClass("mega-menu-horizontal");
498        };
499
500        plugin.bindKeyboardEvents = function() {
501            const $firstFocusable = $menu.find("a.mega-menu-link").first();
502            const $lastFocusable  = $wrap.find("button.mega-close").first();
503            // Matches the focusable link element inside a visible menu list item.
504            // Must stay in sync with getFocusableItemsInSubmenu.
505            const focusableLinkSelector = "> a.mega-menu-link, > button.mega-menu-link, > .mega-search span[role=button]";
506
507            const getActiveSubmenuBackLink = function() {
508                const $activeSubmenu = $("li.mega-toggle-on:not(.mega-collapse-children) > ul.mega-sub-menu", $menu).last();
509                return $activeSubmenu.find("> li.mega-mobile-back:visible > button.mega-menu-link.mega-mobile-back-link").first();
510            };
511
512            const togglePanelForAnchor = function(anchor) {
513                if ( !anchor || !anchor.length ) return;
514                if ( anchor.parent().hasClass("mega-toggle-on") && !anchor.closest("ul.mega-sub-menu").parent().hasClass("mega-menu-tabbed") ) {
515                    plugin.hidePanel(anchor);
516                } else {
517                    plugin.showPanel(anchor);
518                }
519            };
520
521            const closeNearestOpenPanelAndRefocus = function() {
522                const $focused = $menu[0].contains(document.activeElement) ? $(document.activeElement) : $();
523                const $parentAnchor = $("> a.mega-menu-link", $focused.closest(".mega-toggle-on"));
524                if ( $parentAnchor.length ) {
525                    plugin.hidePanel($parentAnchor);
526                    $parentAnchor.trigger("focus");
527                    return true;
528                }
529                return false;
530            };
531
532            // ── Key Handlers ─────────────────────────────────────────────────────────
533
534            const handleTabKey = function(e, $active, isOffCanvasHorizontal, isMobileOffCanvas) {
535                // Close button: hand focus off to the menu, or receive it back from the menu
536                if ( $active.is($lastFocusable) && isMobileOffCanvas ) {
537                    if ( !e.shiftKey ) {
538                        // Tab → forward to back link (if a flyout is open) or first menu item
539                        e.preventDefault();
540                        const $backLink = isOffCanvasHorizontal ? getActiveSubmenuBackLink() : $();
541                        ( $backLink.length ? $backLink : $firstFocusable ).trigger('focus');
542                    } else if ( isOffCanvasHorizontal ) {
543                        // Shift+Tab → last item in the active submenu
544                        const $last = plugin.getFocusableItemsInSubmenu(
545                            $("li.mega-toggle-on:not(.mega-collapse-children) > ul.mega-sub-menu", $menu).last(), false
546                        ).last();
547                        if ( $last.length ) { e.preventDefault(); $last.trigger('focus'); }
548                    }
549                    return;
550                }
551
552                // First top-level item: Shift+Tab wraps around to the close button
553                if ( isMobileOffCanvas && e.shiftKey && $active.is($firstFocusable) ) {
554                    e.preventDefault();
555                    $lastFocusable.trigger('focus');
556                    return;
557                }
558
559                // Inside an offcanvas horizontal submenu: keep focus trapped within the panel
560                if ( isOffCanvasHorizontal ) {
561                    // Walk up to the nearest ul.mega-sub-menu whose parent li has mega-toggle-on.
562                    // For mega menus, $active may be inside a column ul rather than the panel ul itself.
563                    const $submenu = $active.parentsUntil($menu, "ul.mega-sub-menu")
564                        .filter(function() {
565                            const $p = $(this).parent();
566                            return $p.hasClass("mega-toggle-on") && !$p.hasClass("mega-collapse-children");
567                        })
568                        .first();
569                    if ( $submenu.length ) {
570                        if ( !e.shiftKey ) {
571                            // Tab forward: when on the last item, hand off to the close button
572                            const $items = plugin.getFocusableItemsInSubmenu($submenu, false);
573                            if ( $items.length && $active.is($items.last(
573)) ) {
574                                e.preventDefault();
575                                $lastFocusable.trigger('focus');
576                            }
577                        } else if ( $active.hasClass("mega-mobile-back-link") ) {
578                            // Shift+Tab on back link → close button
579                            e.preventDefault();
580                            $lastFocusable.trigger('focus');
581                        } else {
582                            // Shift+Tab on first item → back link
583                            const $items   = plugin.getFocusableItemsInSubmenu($submenu, false);
584                            const $backLink = $submenu.find("> li.mega-mobile-back:visible > button.mega-menu-link.mega-mobile-back-link").first();
585                            if ( $items.length && $backLink.length && $active.is($items.first()) ) {
586                                e.preventDefault();
587                                $backLink.trigger('focus');
588                            }
589                        }
590                    }
591                }
592            };
593
594            const handleToggleBarTrigger = function(e) {
595                e.preventDefault();
596                if ( $toggle_bar.hasClass("mega-menu-open") ) {
597                    plugin.hideMobileMenu();
598                } else {
599                    plugin.showMobileMenu();
600                    html_body_class_timeout = setTimeout(function() {
601                        $menu.find("a.mega-menu-link").first().trigger('focus');
602                    }, plugin.settings.effect_speed_mobile);
603                }
604            };
605
606            const handleSpaceKey = function(e, $active) {
607                if ( $active.is("a.mega-menu-link") ) {
608                    e.preventDefault(); // prevent page scroll on any menu link
609                    if ( $active.parent().is(items_with_submenus) ) togglePanelForAnchor($active);
610                } else if ( $active.is(".mega-indicator") ) {
611                    e.preventDefault();
612                    togglePanelForAnchor($active.parent());
613                }
614            };
615
616            const handleEscapeKey = function() {
617                const submenu_open = $(".mega-toggle-on", $menu).length !== 0;
618                if ( submenu_open && closeNearestOpenPanelAndRefocus() ) return;
619                if ( plugin.isMobileView() && !submenu_open ) plugin.hideMobileMenu();
620            };
621
622            const handleEnterKey = function(e, $active) {
623                if ( $active.is(".mega-indicator") ) {
624                    togglePanelForAnchor($active.parent());
625                    return;
626                }
627                if ( $active.parent().is(items_with_submenus) ) {
628                    if ( plugin.isMobileView() && $active.parent().is(".mega-hide-sub-menu-on-mobile") ) return;
629                    if ( !$active.is("[href]") ) { togglePanelForAnchor($active); return; }
630                    if ( $active.parent().hasClass("mega-toggle-on") && !$active.closest("ul.mega-sub-menu").parent().hasClass("mega-menu-tabbed") ) return;
631                    e.preventDefault();
632                    plugin.showPanel($active);
633                }
634            };
635
636            const handleArrowUpDown = function(e, goingDown) {
637                e.preventDefault();
638                const $activeSubmenu = $("li.mega-toggle-on:not(.mega-collapse-children) > ul.mega-sub-menu", $menu).last();
639
640                if ( $activeSubmenu.length ) {
641                    // Inside a flyout panel: cycle through the same sequence as Tab/Shift+Tab —
642                    // back-link → items → close-button — using an explicit ordered array so
643                    // the back button (li.mega-mobile-back, not li.mega-menu-item) is included.
644                    const $backLink  = $activeSubmenu.find("> li.mega-mobile-back:visible > button.mega-menu-link.mega-mobile-back-link").first();
645                    const $items     = plugin.getFocusableItemsInSubmenu($activeSubmenu, false);
646                    const focusOrder = [];
647                    if ( $backLink.length ) focusOrder.push($backLink[0]);
648                    $items.each(function() { focusOrder.push(this); });
649                    if ( $lastFocusable.length ) focusOrder.push($lastFocusable[0]);
650
651                    const idx     = focusOrder.indexOf(document.activeElement);
652                    const nextIdx = (idx < 0)
653                        ? (goingDown ? 0 : focusOrder.length - 1)
654                        : (idx + (goingDown ? 1 : -1) + focusOrder.length) % focusOrder.length;
655                    $(focusOrder[nextIdx]).trigger('focus');
656                } else {
657                    // Top level: cycle through the visible top-level links.
658                    const $topLinks = $menu.children("li.mega-menu-item:visible").find(focusableLinkSelector);
659                    const idx       = $topLinks.index(document.activeElement);
660                    if ( idx >= 0 ) {
661                        const nextIdx = (idx + (goingDown ? 1 : -1) + $topLinks.length) % $topLinks.length;
662                        $topLinks.eq(nextIdx).trigger('focus');
663                    }
664                }
665            };
666
667            const handleArrowLeftRight = function(e, goingToNext) {
668                e.preventDefault();
669                // Use find(focusableLinkSelector) with > so only direct-child links are
670                // matched — prevents picking up nested submenu links as top-level items.
671                const $topLinks = $menu.children("li.mega-menu-item:visible").find(focusableLinkSelector);
672                const $topLink  = $(document.activeElement)
673                    .closest($menu.children("li.mega-menu-item"))
674                    .find(focusableLinkSelector).first();
675                const idx     = $topLinks.index($topLink);
676                const nextIdx = goingToNext ? idx + 1 : idx - 1;
677                // Guard: nextIdx < 0 would let jQuery's eq(-1) silently wrap to the last item
678                if ( idx >= 0 && nextIdx >= 0 ) {
679                    plugin.hideAllPanels();
680                    $topLinks.eq(nextIdx).trigger('focus'); // eq(length) = empty set = no-op at boundary
681                }
682            };
683
684            // ── Event Bindings ───────────────────────────────────────────────────────
685
686            $wrap.on("keyup.megamenu", ".max-mega-menu, .mega-menu-toggle", function(e) {
687                if ( e.key !== tab_key ) return;
688                $wrap.addClass("mega-keyboard-navigation");
689                plugin.bindClickEvents(); // Windows Narrator ignores Enter, so ensure click events are bound on tab
690                const $target = $(e.target);
691                if ( plugin.isDesktopView() && $target.is(".mega-menu-link") && $target.parent().parent().hasClass('max-mega-menu') ) {
692                    plugin.hideAllPanels();
693                }
694            });
695
696            $wrap.on("keydown.megamenu", "a.mega-menu-link, button.mega-menu-link, .mega-indicator, .mega-menu-toggle-block, .mega-menu-toggle-animated-block button, button.mega-close", function(e) {
697                if ( !$wrap.hasClass("mega-keyboard-navigation") ) return;
698
699                const key = e.key;
700                const $active = $(e.target);
701                const isOffCanvasHorizontal = plugin.isHorizontalMobileSubmenuMode();
702                const isMobileOffCanvas = plugin.isMobileView() && plugin.isMobileOffCanvas();
703
704                switch (key) {
705                    case tab_key:
706                        handleTabKey(e, $active, isOffCanvasHorizontal, isMobileOffCanvas);
707                        break;
708
709                    case space_key:
710                        if ( $active.is(".mega-menu-toggle-block button, .mega-menu-toggle-animated-block button") ) {
711                            handleToggleBarTrigger(e);
712                        } else {
713                            handleSpaceKey(e, $active);
714                        }
715                        break;
716
717                    case enter_key:
718                        if ( $active.is(".mega-menu-toggle-block button, .mega-menu-toggle-animated-block button") ) {
719                            handleToggleBarTrigger(e);
720                        } else {
721                            handleEnterKey(e, $active);
722                        }
723                        break;
724
725                    case escape_key:
726                        handleEscapeKey();
727                        break;
728
729                    case up_arrow_key:
730                    case down_arrow_key:
731                        if ( isOffCanvasHorizontal ) {
732                            handleArrowUpDown(e, key === down_arrow_key);
733                        }
734                        break;
735
736                    case left_arrow_key:
737                    case right_arrow_key:
738                        const goingToNext = plugin.shouldGoToNextTopLevelItem(key);
739                        const goingToPrev = plugin.shouldGoToPreviousTopLevelItem(key);
740                        if ( goingToNext || goingToPrev ) {
741                            handleArrowLeftRight(e, goingToNext);
742                        }
743                        break;
744                }
745            });
746
747            $wrap.on("focusout.megamenu", function(e) {
748                if ( $wrap.hasClass("mega-keyboard-navigation") ) {
749                    setTimeout(function() {
750                        if ( !$wrap[0].contains(document.activeElement) ) {
751                            $wrap.removeClass("mega-keyboard-navigation");
752                            plugin.hideAllPanels();
753                            plugin.hideMobileMenu();
754                        }
755                    }, 10);
756                }
757            });
758        };
759
760        plugin.unbindAllEvents = function() {
761            $(document).off(docEventNamespace);
762            $("ul.mega-sub-menu, li.mega-menu-item, li.mega-menu-row, li.mega-menu-column, a.mega-menu-link, .mega-indicator", $menu).off();
763        };
764
765        plugin.unbindClickEvents = function() {
766            if ( $wrap.hasClass('mega-keyboard-navigation') ) {
767                return;
768            }
769
770            $(document).off(docEventNamespace);
771
772            // collapsable parents always have a click event
773            $("> a.mega-menu-link", items_with_submenus).not(collapse_children_parents).off("click.megamenu touchend.megamenu");
774        };
775
776        plugin.unbindHoverEvents = function() {
777            items_with_submenus.off("mouseenter.megamenu mouseleave.megamenu");
778        };
779
780        plugin.unbindHoverIntentEvents = function() {
781            items_with_submenus.off("mouseenter mouseleave").removeProp("hoverIntent_t").removeProp("hoverIntent_s"); // hoverintent does not allow namespaced events
782        };
783
784        plugin.unbindKeyboardEvents = function() {
785            $wrap.off("keyup.megamenu keydown.megamenu focusout.megamenu");
786        };
787
788        plugin.unbindMegaMenuEvents = function() {
789            if (plugin.settings.event === "hover_intent") {
790                plugin.unbindHoverIntentEvents();
791            }
792
793            if (plugin.settings.event === "hover") {
794                plugin.unbindHoverEvents();
795            }
796
797            plugin.unbindClickEvents();
798            plugin.unbindKeyboardEvents();
799        };
800
801        plugin.bindMegaMenuEvents = function() {
802            plugin.unbindMegaMenuEvents();
803
804            const isDesktop = plugin.isDesktopView();
805
806            if (isDesktop && plugin.settings.event === "hover_intent") {
807                plugin.bindHoverIntentEvents();
808            }
809
810            if (isDesktop && plugin.settings.event === "hover") {
811                plugin.bindHoverEvents();
812            }
813
814            plugin.bindClickEvents(); // always bind click events for touch screen devices
815            plugin.bindKeyboardEvents();
816        };
817
818        plugin.checkWidth = function() {
819            if ( plugin.isMobileView() && $menu.data("view") === "desktop" ) {
820                plugin.switchToMobile();
821            }
822
823            if ( plugin.isDesktopView() && $menu.data("view") === "mobile" ) {
824                plugin.switchToDesktop();
825            }
826
827            plugin.calculateDynamicSubmenuWidths($("> li.mega-menu-megamenu > a.mega-menu-link", $menu));
828        };
829
830        plugin.reverseRightAlignedItems = function() {
831            if ( ! $("body").hasClass("rtl") && $menu.hasClass("mega-menu-horizontal") && $menu.css("display") !== 'flex' ) {
832                $menu.append($menu.children("li.mega-item-align-right").get().reverse());
833            }
834        };
835
836        plugin.addClearClassesToMobileItems = function() {
837            $(".mega-menu-row", $menu).each(function() {
838                $("> .mega-sub-menu > .mega-menu-column:not(.mega-hide-on-mobile)", $(this)).filter(":even").addClass("mega-menu-clear"); // :even is 0 based
839            });
840        };
841
842        plugin.initDesktop = function() {
843            $menu.data("view", "desktop");
844            plugin.bindMegaMenuEvents();
845            plugin.initIndicators();
846            $menu.trigger("mmm:switchToDesktop");
847        };
848
849        plugin.initMobile = function() {
850            plugin.switchToMobile();
851        };
852
853        plugin.switchToDesktop = function() {
854            $menu.data("view", "desktop");
855            plugin.bindMegaMenuEvents();
856            plugin.reverseRightAlignedItems();
857            plugin.hideAllPanels();
858            plugin.hideMobileMenu(true);
859            $menu.removeAttr('role');
860            $menu.removeAttr('aria-modal');
861            $menu.removeAttr('aria-hidden');
862            $menu.trigger("mmm:switchToDesktop");
863        };
864
865        plugin.switchToMobile = function() {
866            $menu.data("view", "mobile");
867
868            if (plugin.isMobileOffCanvas() && $toggle_bar.is(":visible") ) {
869                $menu.attr('role', 'dialog');
870                $menu.attr('aria-modal', 'true');
871                $menu.attr('aria-hidden', 'true');
872            }
873
874            plugin.bindMegaMenuEvents();
875            plugin.initIndicators();
876            plugin.reverseRightAlignedItems();
877            plugin.addClearClassesToMobileItems();
878            plugin.hideAllPanels();
879            plugin.expandMobileSubMenus();
880
881            $menu.trigger("mmm:switchToMobile");
882
883        };
884
885        plugin.initToggleBar = function() {
886            $toggle_bar.on("click", function(e) {
887                const isToggleTrigger = $(e.target).closest(".mega-menu-toggle-block, button.mega-toggle-animated, .mega-menu-toggle-custom-block", this).length;
888
889                if ( isToggleTrigger ) {
890                    e.preventDefault();
891                    if ($(this).hasClass("mega-menu-open")) {
892                        plugin.hideMobileMenu();
893                    } else {
894                        plugin.showMobileMenu();
895                    }
896                } else if ( e.target === this && plugin.isMobileOffCanvas() ) {
897                    plugin.hideMobileMenu();
898                }
899            });
900        };
901
902        plugin.initIndicators = function() {
903             $menu.off('click.megamenu', '.mega-indicator');
904
905             $menu.on('click.megamenu', '.mega-indicator', function(e) {
906                e.preventDefault();
907                e.stopPropagation();
908
909                if ( $(this).closest(".mega-menu-item").hasClass("mega-toggle-on") ) {
910                    if ( ! $(this).closest("ul.mega-sub-menu").parent().hasClass("mega-menu-tabbed") || plugin.isMobileView() ) {
911                        plugin.hidePanel($(this).parent(), false);
912                    }
913                } else {
914                    plugin.showPanel($(this).parent(), false);
915                }
916             });
917        };
918
919        plugin.hideMobileMenu = function(force = false) {
920
921            if ( ! $toggle_bar.is(":visible") && ! force ) {
922                return;
923            }
924
925            $menu.attr("aria-hidden", "true");
926
927            clearTimeout(html_body_class_timeout);
928            html_body_class_timeout = setTimeout(function() {
929                $("body").removeClass(menuId + "-mobile-open");
930                $("html").removeClass(menuId + "-off-canvas-open");
931            }, plugin.settings.effect_speed_mobile);
932
933            if ($wrap.hasClass("mega-keyboard-navigation")) {
934                $(".mega-menu-toggle-block button, button.mega-toggle-animated", $toggle_bar).first().trigger('focus');
935            }
936
937            $(".mega-toggle-label, .mega-toggle-animated", $toggle_bar).attr("aria-expanded", "false");
938
939            if (plugin.settings.effect_mobile === "slide" && ! force ) {
940                $menu.animate({"height":"hide"}, plugin.settings.effect_speed_mobile, function() {
941                    $menu.css({
942                        width: "",
943                        left: "",
944                        display: ""
945                    });
946
947                    $toggle_bar.removeClass("mega-menu-open");
948                });
949            } else {
950                $menu.css({
951                    width: "",
952                    left: "",
953                    display: ""
954                });
955                    
956                $toggle_bar.removeClass("mega-menu-open");
957            }
958
959            
960            $menu.triggerHandler("mmm:hideMobileMenu");
961        };
962
963        plugin.showMobileMenu = function() {
964            if ( ! $toggle_bar.is(":visible") ) {
965                return;
966            }
967
968            clearTimeout(html_body_class_timeout);
969
970            $("body").addClass(menuId + "-mobile-open");
971
972            plugin.expandMobileSubMenus();
973
974            if ( plugin.isMobileOffCanvas() ) {
975                $("html").addClass(menuId + "-off-canvas-open");
976            }
977
978            if (plugin.settings.effect_mobile === "slide") {
979                $menu.animate({"height":"show"}, plugin.settings.effect_speed_mobile, function() {
980                    $(this).css("display", "");
981                });
982            }
983
984            $(".mega-toggle-label, .mega-toggle-animated", $toggle_bar).attr("aria-expanded", "true");
985
986            $toggle_bar.addClass("mega-menu-open");
987
988            plugin.toggleBarForceWidth();
989
990            $menu.attr("aria-hidden", "false");
991            $menu.triggerHandler("mmm:showMobileMenu");
992        };
993
994        plugin.toggleBarForceWidth = function() {
995            const $force_width_el = $(plugin.settings.mobile_force_width);
996
997            if ($force_width_el.length && ( plugin.settings.effect_mobile === "slide" || plugin.settings.effect_mobile === "disabled" ) ) {
998                const submenu_offset = $toggle_bar.offset();
999                const target_offset = $force_width_el.offset();
1000
1001                $menu.css({
1002                    width: $force_width_el.outerWidth(),
1003                    left: (target_offset.left - submenu_offset.left) + "px"
1004                });
1005            }
1006        };
1007
1008        plugin.doConsoleChecks = function() {
1009            if (plugin.settings.mobile_force_width !== "false" && ! $(plugin.settings.mobile_force_width).length && ( plugin.settings.effect_mobile === "slide" || plugin.settings.effect_mobile === "disabled" ) ) {
1010                console.warn('Max Mega Menu #' + $wrap.attr('id') + ': Mobile Force Width element (' + plugin.settings.mobile_force_width + ') not found');
1011            }
1012
1013            const cssWidthRegex = /^((\d+(\.\d+)?(px|%|em|rem|vw|vh|ch|ex|cm|mm|in|pt|pc))|auto)$/i;
1014
1015            const resolveWidthEl = (val) => val === '$menu' ? $menu : val === '$wrap' ? $wrap : $(val);
1016
1017            if (plugin.settings.panel_width !== undefined && ! cssWidthRegex.test(plugin.settings.panel_width) && ! resolveWidthEl(plugin.settings.panel_width).length ) {
1018                console.warn('Max Mega Menu #' + $wrap.attr('id') + ': Panel Width (Outer) element (' + plugin.settings.panel_width + ') not found');
1019            }
1020
1021            if (plugin.settings.panel_inner_width !== undefined && ! cssWidthRegex.test(plugin.settings.panel_inner_width) && ! resolveWidthEl(plugin.settings.panel_inner_width).length ) {
1022                console.warn('Max Mega Menu #' + $wrap.attr('id') + ': Panel Width (Inner) element (' + plugin.settings.panel_inner_width + ') not found');
1023            }
1024        };
1025
1026        plugin.init = function() {
1027            $menu.triggerHandler("before_mega_menu_init");
1028            plugin.settings = $.extend({}, defaults, options);
1029
1030            if (window.console) {
1031                plugin.doConsoleChecks();
1032            }
1033
1034            $menu.removeClass("mega-no-js");
1035
1036            plugin.initToggleBar();
1037            
1038            if (plugin.settings.unbind_events === "true") {
1039                plugin.unbindAllEvents();
1040            }
1041
1042            if ( document.readyState === 'complete' ) {
1043                plugin.calculateDynamicSubmenuWidths($("> li.mega-menu-megamenu > a.mega-menu-link", $menu));
1044            } else {
1045                $(window).on("load", function() {
1046                    plugin.calculateDynamicSubmenuWidths($("> li.mega-menu-megamenu > a.mega-menu-link", $menu));
1047                });
1048            }
1049
1050            if ( plugin.isDesktopView() ) {
1051                plugin.initDesktop();
1052            } else {
1053                plugin.initMobile();
1054            }
1055
1056            $(window).on("resize", function() {
1057                plugin.checkWidth();
1058            });
1059
1060            $menu.triggerHandler("after_mega_menu_init");
1061        };
1062
1063        plugin.init();
1064    };
1065
1066    $.fn.maxmegamenu = function(options) {
1067        return this.each(function() {
1068            if (undefined === $(this).data("maxmegamenu")) {
1069                const plugin = new $.maxmegamenu(this, options);
1070                $(this).data("maxmegamenu", plugin);
1071            }
1072        });
1073    };
1074
1075    $(function() {
1076        $(".max-mega-menu").maxmegamenu();
1077    });
1078}( jQuery ));

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.