PageSourceSearch

https://www.curotec.com/_static/??/wp-includes/js/hoverIntent.min.…/megamenu/js/maxmegamenu.js?m=1790088692j

js curotec.com collected 2026-10-03 23:52:49 UTC 49,836 bytes, 1,107 lines download raw bytes

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