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.