1/* 2* ubermenu.js 3* 4* http://wpmegamenu.com 5* 6* Copyright Chris Mavricos, SevenSpark https://sevenspark.com 7*/ 8 9//Define dependencies first because deferred scripts don't respect document ready order 10; var uber_supports = (function () { 11 var div = document.createElement('div'), 12 vendors = 'Khtml Ms O Moz Webkit'.split(' '); 13 return function (prop) { 14 var len = vendors.length; 15 if (prop in div.style) return true; 16 prop = prop.replace(/^[a-z]/, function (val) { 17 return val.toUpperCase(); 18 }); 19 while (len--) { 20 if (vendors[len] + prop in div.style) { 21 return true; 22 } 23 } 24 return false; 25 }; 26})(); 27 28function uber_op(id, args, def) { 29 //If no id 30 if (!ubermenu_data.hasOwnProperty(id)) { 31 return def; 32 } 33 var val = ubermenu_data[id]; 34 if (args.hasOwnProperty('datatype')) { 35 switch (args['datatype']) { 36 case 'numeric': 37 val = parseInt(val); 38 break; 39 40 case 'boolean': 41 if (val == 'on' || val == 1 || val == '1') { 42 val = true; 43 } 44 else { 45 val = false; 46 } 47 break; 48 } 49 } 50 return val; 51} 52 53; (function ($, sr) { 54 // debouncing function from John Hann 55 // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 56 var debounce = function (func, threshold, execAsap) { 57 var timeout; 58 return function debounced() { 59 var obj = this, args = arguments; 60 function delayed() { 61 if (!execAsap) 62 func.apply(obj, args); 63 timeout = null; 64 }; 65 if (timeout) 66 clearTimeout(timeout); 67 else if (execAsap) 68 func.apply(obj, args); 69 timeout = setTimeout(delayed, threshold || 100); 70 }; 71 } 72 jQuery.fn[sr] = function (fn) { return fn ? this.on('resize', debounce(fn)) : this.trigger(sr); }; 73 74})(jQuery, 'ubersmartresize'); 75 76; (function ($, window, document, undefined) { 77 'use strict'; 78 var pluginName = "ubermenu", 79 defaults = { 80 breakpoint: uber_op('responsive_breakpoint', { datatype: 'numeric' }, 959), 81 touchEvents: true, 82 mouseEvents: true, 83 retractors: true, 84 touchOffClose: uber_op('touch_off_close', { datatype: 'boolean' }, true), //true, 85 submenuIndicatorCloseMobile: uber_op('submenu_indicator_close_mobile', { datatype: 'boolean' }, true), 86 moveThreshold: 10, //Distance until tap is cancelled in deference to move/scroll 87 submenuAnimationDuration: 500, //ms duration or submenu close time 88 ignoreDummies: true, 89 clicktest: false, 90 windowstest: false, 91 debug: false, 92 debug_onscreen: false, 93 94 remove_conflicts: uber_op('remove_conflicts', { datatype: 'boolean' }, true), 95 reposition_on_load: uber_op('reposition_on_load', { datatype: 'boolean' }, false), 96 accessible: uber_op('accessible', { datatype: 'boolean' }, true), 97 retractor_display_strategy: uber_op('retractor_display_strategy', { datatype: 'string' }, 'responsive'), 98 mobile_menu_collapse_on_navigate: uber_op('mobile_menu_collapse_on_navigate', { datatype: 'boolean' }, true), 99 100 intent_delay: uber_op('intent_delay', { datatype: 'numeric' }, 300), //delay before the menu closes 101 intent_interval: uber_op('intent_interval', { datatype: 'numeric' }, 100), //polling interval for mouse comparisons 102 intent_threshold: uber_op('intent_threshold', { datatype: 'numeric' }, 300), //maximum number of pixels mouse can move to be considered intent 103 104 scrollto_offset: uber_op('scrollto_offset', { datatype: 'numeric' }, 0), 105 scrollto_duration: uber_op('scrollto_duration', { datatype: 'numeric' }, 1000), 106 collapse_after_scroll: uber_op('collapse_after_scroll', { datatype: 'boolean' }, true), 107 108 aria_role_navigation: uber_op('aria_role_navigation', { datatype: 'boolean' }, false), 109 aria_nav_label: uber_op('aria_nav_label', { datatype: 'boolean' }, false), 110 aria_expanded: uber_op('aria_expanded', { datatype: 'boolean' }, false), 111 aria_hidden: uber_op('aria_hidden', { datatype: 'boolean' }, false), 112 aria_responsive_toggle: uber_op('aria_responsive_toggle', { datatype: 'boolean' }, false), 113 icon_tag: uber_op('icon_tag', { datatype: 'string' }, 'i'), 114 use_core_svgs: uber_op('use_core_svgs', { datatype: 'boolean' }, false), 115 esc_close_mobile: uber_op('esc_close_mobile', { datatype: 'boolean' }, true), 116 117 keyboardSubmenuTrigger: uber_op('keyboard_submenu_trigger', { datatype: 'string' }, 'enter'), // focus, spacebar, enter 118 119 }/*, 120 keys = { 121 BACKSPACE: 8, 122 COMMA: 188, 123 DELETE: 46, 124 DOWN: 40, 125 END: 35, 126 ENTER: 13, 127 ESCAPE: 27, 128 HOME: 36, 129 LEFT: 37, 130 PAGE_DOWN: 34, 131 PAGE_UP: 33, 132 PERIOD: 190, 133 RIGHT: 39, 134 SPACE: 32, 135 TAB: 9, 136 UP: 38 137 }*/; 138 139 // instantiate variables 140 // cX, cY = current X and Y position of mouse, updated by mousemove event 141 // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval 142 var cX, cY, pX, pY; 143 144 function Plugin(element, options) { 145 146 var plugin = this; //reference for all 147 this.element = element; 148 this.$ubermenu = $(this.element); 149 150 this.orientation = this.$ubermenu.hasClass('ubermenu-vertical') ? 'v' : 'h'; 151 this.mobileAccordion = this.$ubermenu.hasClass('ubermenu-mobile-accordion'); 152 this.mobileAccordionFolding = this.$ubermenu.hasClass('ubermenu-mobile-accordion-single') ? 'single' : 'multiple'; 153 this.modalMobile = this.$ubermenu.hasClass('ubermenu-mobile-modal'); 154 this.interactionMode = null; //hover or press 155 this.vstretch = this.$ubermenu.hasClass('ubermenu-items-vstretch'); 156 this.inverted = this.$ubermenu.hasClass('ubermenu-invert'); 157 158 this.settings = $.extend({}, defaults, options); 159 this._defaults = defaults; 160 this._name = pluginName; 161 this.settings.responsive = this.$ubermenu.hasClass('ubermenu-responsive') ? true : false;
162 163 if (this.settings.debug && this.settings.debug_onscreen) { 164 $('body').append('<div id="uber-onscreen-debug" style="color:#eee;z-index:10000;background:#222;position:fixed;left:0; bottom:0; width:100%; height:50%; padding:10px;overflow:scroll;"> '); 165 this.debug_target = $('#uber-onscreen-debug'); 166 this.debug_target.on('click', function () { if ($(this).height() < 100) $(this).height('50%'); else $(this).height('50px'); }); 167 } 168 this.log('-- START UBERMENU DEBUG --'); 169 170 this.events_disabled = false; 171 this.suppress_clicks = false; //Set to true if the mouse events support 'pointerup', which handles both touch and click (but not hover) 172 173 this.touchenabled = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0); 174 if (this.touchenabled) { 175 this.$ubermenu.addClass('ubermenu-touch'); 176 } 177 else this.$ubermenu.addClass('ubermenu-notouch'); 178 179 if (window.navigator.pointerEnabled) { 180 this.touchStart = 'pointerdown'; 181 this.touchEnd = 'pointerup'; 182 this.touchMove = 'pointermove'; 183 184 this.suppress_clicks = true; 185 } 186 else if (window.navigator.msPointerEnabled) { 187 this.touchStart = 'MSPointerDown'; 188 this.touchEnd = 'MSPointerUp'; 189 this.touchMove = 'MSPointerMove'; 190 191 this.suppress_clicks = true; 192 } 193 else { 194 this.touchStart = 'touchstart'; 195 this.touchEnd = 'touchend'; 196 this.touchMove = 'touchmove'; 197 } 198 199 this.toggleevent = 'click'; // this.touchEnd == 'touchend' ? this.touchEnd + ' click' : this.touchEnd; //add click except for IE 200 this.transitionend = 'transitionend.ubermenu webkitTransitionEnd.ubermenu msTransitionEnd.ubermenu oTransitionEnd.ubermenu'; 201 202 //Are transitions supported? 203 this.transitions = uber_supports('transition') && !this.$ubermenu.hasClass('ubermenu-transition-none'); 204 if (!this.transitions) this.$ubermenu.addClass('ubermenu-no-transitions'); 205 206 //Detect crappy Android & Windows browsers and disable transitions 207 var ua = navigator.userAgent.toLowerCase(); 208 this.log(ua); 209 210 this.allow_trigger_overrides = true; 211 this.noTouchEnd = false; 212 213 var android = this.settings.android = /android/.test(ua); 214 var windowsmobile = this.settings.windowsmobile = /iemobile/.test(ua); 215 if (android || windowsmobile) { 216 //this.log( 'android or windows' ); 217 //alert( ua ); 218 if ((android && !(/chrome/.test(ua) || /firefox/.test(ua) || /opera/.test(ua))) || 219 (windowsmobile) 220 ) { 221 this.settings.touchOffClose = false; 222 this.disableTransitions(); 223 224 //Crap Android browsers don't fire touchend properly 225 if (android && !windowsmobile) { //adjust to better determine android UA string instead 226 this.$ubermenu 227 .removeClass('ubermenu-trigger-hover_intent') 228 .removeClass('ubermenu-trigger-hover') 229 .addClass('ubermenu-trigger-click'); 230 this.settings.touchEvents = false; 231 this.allow_trigger_overrides = false; 232 } 233 } 234 } 235 236 if (windowsmobile) { 237 this.log('disable touchoff close and accessibility'); 238 this.settings.touchOffClose = false; //improper event delegation in windows 239 this.settings.accessible = false; //WTF Microsoft 240 this.settings.mouseEvents = false; 241 } 242 243 var safari5 = (!/chrome/.test(ua)) && (/safari/.test(ua)) && (/version\/5/.test(ua)); 244 if (safari5) { 245 this.disableTransitions(); 246 } 247 248 249 //Reflow right items 250 this.last_width = window.innerWidth; 251 var cur_width = this.last_width; 252 var $right_items = plugin.$ubermenu.find('.ubermenu-item-level-0.ubermenu-align-right'); 253 if ($right_items.length) { 254 $(window).ubersmartresize(function () { 255 cur_width = window.innerWidth; 256 if (plugin.last_width <= plugin.settings.breakpoint && 257 cur_width >= plugin.settings.breakpoint) { 258 $right_items.hide(); $right_items[0].offsetHeight; //$right_items.show(); //reflow 259 $right_items.css('display', ''); 260 } 261 plugin.last_width = cur_width; 262 }); 263 } 264 265 //TESTING 266 if (this.settings.clicktest) this.touchEnd = 'click'; 267 268 this.init(); 269 270 } 271 272 Plugin.prototype = { 273 274 init: function () { 275 276 this.log('Initializing UberMenu'); 277 278 this.$ubermenu.removeClass('ubermenu-nojs'); //We're off and running 279 280 this.removeConflicts();
280 //Stop other JS from interfering when possible 281 282 //Initialize user interaction events 283 this.initializeSubmenuToggleTouchEvents(); 284 this.initializeSubmenuToggleMouseEvents(); 285 this.initializeRetractors(); 286 this.initializeResponsiveToggle(); 287 this.initializeMobileViewClasses(); 288 this.initializeMobileModal(); 289 this.initializeTouchoffClose(); 290 this.initializeTabs(); 291 this.initializeSubmenuPositioning(); 292 this.initializeSegmentCurrentStates(); 293 this.initializeAccessibilityOnTab(); 294 this.initializeAccessibilityStates(); 295 this.initializeImageLazyLoad(); 296 this.initializeImagePortal(); 297 }, 298 299 removeConflicts: function () { 300 if (this.settings.remove_conflicts) { 301 this.$ubermenu.find('.ubermenu-item, .ubermenu-target, .ubermenu-submenu').add(this.$ubermenu) 302 .removeAttr('style') 303 .unbind().off(); 304 } 305 }, 306 307 308 309 /* Initalizers */ 310 311 initializeAccessibilityStates: function () { 312 var plugin = this; 313 314 //aria role="navigation" on nav element 315 if (this.settings.aria_role_navigation) { 316 plugin.$ubermenu.attr('role', 'navigation'); 317 } 318 319 //aria-label="{menu title}" on nav element 320 if (this.settings.aria_nav_label) { 321 plugin.$ubermenu.attr('aria-label', this.$ubermenu.find('> .ubermenu-nav').attr('data-title')); 322 } 323 324 //aria-expanded on menu items with dropdowns 325 if (plugin.settings.aria_expanded) { 326 plugin.$ubermenu.find('.ubermenu-item.ubermenu-has-submenu-drop > .ubermenu-target').attr('aria-expanded', false); 327 } 328 329 //aria-hidden on dropdowns 330 if (plugin.settings.aria_hidden) { 331 plugin.$ubermenu.find('.ubermenu-submenu-drop').attr('aria-hidden', true); 332 } 333 334 335 var $mobile_only_content = plugin.$ubermenu.find('.ubermenu-mobile-header, .ubermenu-mobile-footer'); 336 $mobile_only_content.attr('aria-hidden', !plugin.isMobile()); 337 $(window).on('ubermenu-resize-mobile', function () { 338 $mobile_only_content.attr('aria-hidden', false); 339 }); 340 $(window).on('ubermenu-resize-desktop', function () { 341 $mobile_only_content.attr('aria-hidden', true); 342 }); 343 344 }, 345 346 initializeAccessibilityOnTab: function () { 347 348 if (!this.settings.accessible) return; 349 350 var plugin = this; 351 352 //Initialize 353 $('body').on('keydown.ubermenu', function (e) { 354 var keyCode = e.keyCode || e.which; 355 if (keyCode == 9) { 356 $('body').off('keydown.ubermenu'); 357 plugin.initializeAccessibility(); 358 } 359 }); 360 361 //Esc to close on mobile - whether tabbed or not 362 if (plugin.settings.esc_close_mobile) { 363 $(window).on('keyup', function (e) { 364 if (plugin.isMobile() && !plugin.$ubermenu.hasClass('ubermenu-responsive-collapse')) { 365 if (e.which === 27) { 366 plugin.toggleMenuCollapse(); 367 } 368 } 369 }); 370 } 371 372 }, 373 374 initializeImageLazyLoad: function () { 375 var plugin = this; 376 plugin.$ubermenu.find('.ubermenu-item-level-0').one('ubermenuopen', function () { 377 $(this).find('.ubermenu-image-lazyload').each(function () { 378 //Responsive images - add srcset and sizes if present 379 if ($(this).data('srcset')) { 380 $(this) 381 .attr('srcset', $(this).data('srcset')) 382 .attr('sizes', $(this).data('sizes')) 383 } 384 //Whether responsive images (4.4+) or not, set src attribute 385 $(this) 386 .attr('src', $(this).data('src')) 387 .removeClass('ubermenu-image-lazyload'); 388 }); 389 setTimeout(function () { 390 plugin.clearTabSizes(); 391 plugin.sizeTabs(); 392 }, 300); 393 }); 394 }, 395 396 initializeImagePortal: function () { 397 this.$ubermenu.find('.ubermenu-image-portal').each(function () { 398 var $imageportal = $(this); 399 var selector = $imageportal.data('ubermenu-portal-select'); 400 var $images = []; // Collect the images 401 402 $(selector).each(function () { 403 var $menuitem = $(this); 404 var $img = $menuitem.find('> .ubermenu-target > .ubermenu-image'); 405 406 $img.addClass('ubermenu-image-portal-hidden'); 407 $img.attr('data-ubermenu-item-ref', $menuitem.attr('id')); 408 $menuitem.find('> .ubermenu-target').on('mouseenter', function () { 409 $img.removeClass('ubermenu-image-portal-hidden'); 410 }).on('mouseleave', function () { 411 $img.addClass('ubermenu-image-portal-hidden'); 412 }); 413 414 // console.log($img); 415 $images.push($img); 416 }); 417 // console.log($images); 418 $imageportal.append($images); 419 }); 420 }, 421 422 initializeAccessibility: function () { 423 424 var plugin = this; 425 var tabbables = '.ubermenu-target, a, input, select, textarea'; 426 427 plugin.$current_focus = false;
428 plugin.mousedown = false; 429 plugin.$ubermenu.addClass('ubermenu-accessible'); 430 431 //Focus 432 plugin.$ubermenu.on('focus', tabbables, function () { 433 434 // Ignore if focused by mouse 435 if (!plugin.mousedown) { 436 437 var $target = $(this); 438 439 plugin.$current_focus = $target; 440 var $item = $target.parent('.ubermenu-item'); //get the LI parent of A 441 442 if ($item.length) { 443 //Top level items - just close everything else 444 if ($item.is('.ubermenu-item-level-0')) { 445 plugin.closeAllSubmenus(); 446 } 447 448 if (plugin.settings.keyboardSubmenuTrigger === 'focus') { 449 //Items with submenus - open the submenus 450 if ($item.is('.ubermenu-has-submenu-drop')) { 451 //Delay .5s so that if we want we can tab right through 452 setTimeout(function () { 453 if (!$target.is(':focus')) return; //skip if item no longer focused 454 455 //Close the submenus of all siblings 456 $item.siblings('.ubermenu-has-submenu-drop').each(function () { 457 plugin.closeSubmenu($(this), 'umac', plugin); 458 }); 459 460 //Open this item's submenu 461 plugin.openSubmenu($item, 'umac', plugin); 462 463 }, 500); 464 } 465 } 466 467 //Focusout - any time an element is blurred, check to see if the 468 //xUse focusout because it handles blur on all types of child elements 469 //plugin.$ubermenu.on( 'focusout' , '.ubermenu-item-level-0' , function(){ 470 //plugin.$ubermenu.on( 'blur' , tabbables , function(e){ 471 $target.on('blur.ubermenu', tabbables, function (e) { 472 if (!plugin.mousedown) { 473 plugin.$current_focus = false; 474 $(this).off('blur.ubermenu'); 475 476 //If a new focus within the menu isn't set within .5s, assume we've left the menu focus 477 setTimeout(function () { 478 if (!plugin.$current_focus) { 479 //console.log( 'abandon ' , plugin.$current_focus ); 480 plugin.closeAllSubmenus(); 481 } 482 }, 500); 483 } 484 plugin.mousedown = false; 485 }); 486 } 487 } 488 489 plugin.mousedown = false; 490 491 }); 492 493 //Focus leaves UberMenu, close any open submenus 494 plugin.$ubermenu.on('focusout', function () { 495 setTimeout(function () { 496 if (!$(document.activeElement).closest(plugin.$ubermenu).length) { 497 plugin.closeAllSubmenus(); 498 } 499 }, 10); 500 }); 501 502 var openViaSpacebar = plugin.settings.keyboardSubmenuTrigger === 'spacebar'; 503 var openViaEnter = plugin.settings.keyboardSubmenuTrigger === 'enter'; 504 var openViaKey = openViaSpacebar || openViaEnter; 505 506 507 // Potentially override tab, enter, spacebar 508 if (openViaKey) { 509 plugin.$ubermenu.find('.ubermenu-item').on('keydown', function (e) { 510 511 var $li = $(this); 512 var hasDrop = $li.hasClass('ubermenu-has-submenu-drop'); 513 var active = $li.hasClass('ubermenu-active'); 514 515 // Override normal tabbing if we're opening with spacebar or enter 516 if (e.which === 9) { 517 // If there is a submenu and the submenu is not open, 518 // tab to other top level items 519 // otherwise, let normal tabbing work 520 if (hasDrop && !active) { 521 if (e.shiftKey) { 522 var $prev = $li.prev('.ubermenu-item'); 523 if ($prev.length) { 524 console.log($prev); 525 e.preventDefault(); 526 $prev.find('>.ubermenu-target').focus(); 527 } 528 } 529 else { 530 var $next = $li.next('.ubermenu-item'); 531 if ($next.length) { 532 e.preventDefault(); 533 $next.find('>.ubermenu-target').focus(); 534 } 535 } 536 } 537 } 538 if (openViaEnter) { 539 switch (e.which) { 540 // Enter - open submenu first time, follow link second time 541 case 13: 542 if (hasDrop && !active) { 543 e.preventDefault(); 544 e.stopPropagation(); 545 //Close the submenus of all siblings 546 $li.siblings('.ubermenu-has-submenu-drop').each(function () { 547 plugin.closeSubmenu($(this), 'umac', plugin); 548 }); 549 plugin.openSubmenu($(this)); 550 } 551 break; 552 } 553 } 554 if (openViaSpacebar) { 555 switch (e.which) { 556 // // Override the default Enter if using spacebar 557 // case 13: 558 // e.preventDefault(); 559 // $(this).find('>.ubermenu-target').click(); // Follow the link 560 // break; 561 // Spacebar open sub 562 case 32: 563 e.preventDefault(); // Prevent the scroll 564 e.stopPropagation(); // don't bubble up to parents 565 $li.siblings('.ubermenu-has-submenu-drop').each(function () { 566 plugin.closeSubmenu($(this), 'umac', plugin); 567 }); 568 if (active) plugin.closeSubmenu($(this)); // Close the submenus 569 else if (hasDrop) plugin.openSubmenu($(this)); // Open the submenu 570 break; 571 } 572 } 573 }); 574 } 575 //Top Level Keyboard interactions - left, right, escape 576 plugin.$ubermenu.find('.ubermenu-item-level-0').on('keyup', function (e) { 577 //var $uber = jQuery( '.ubermenu' ); 578 switch (e.which) { 579 case 39: //right 580 plugin.closeAllSubmenus(); 581 //console.log( 'right ' + jQuery( this ).next().attr('id') ); 582 $(this).next().find('>.ubermenu-target').focus(); 583 break; 584 585 case 37: //left 586 plugin.closeAllSubmenus(); 587 //console.log( 'left '+ jQuery( this ).prev().attr('id') ); 588 $(this).prev().find('>.ubermenu-target').focus(); 589 break; 590 591 case 27: //escape 592 // If we close this submenu, don't propagate the event which would close the whole menu 593 if ($(this).hasClass('ubermenu-active')) { 594 e.stopPropagation(); 595 } 596 plugin.closeAllSubmenus(); 597 $(this).find('>.ubermenu-target').focus(); 598 // $(this).next().find('>.ubermenu-target').focus(); 599 break; 600 } 601 }); 602 // Tabs 603 plugin.$ubermenu.find('.ubermenu-tab-layout-left > .ubermenu-tabs-group > .ubermenu-tab').on('keydown', function (e) { 604 switch (e.which) { 605 case 40: // Down Arrow 606 e.preventDefault(); 607 $(this).next().find('>.ubermenu-target').focus(); 608 break; 609 case 38: // Up arrow 610 e.preventDefault(); 611 $(this).prev().find('>.ubermenu-target').focus(); 612 break; 613 // default: console.log(e.which); 614 } 615 }); 616 617 618
619 //Mousedown - flag a mouse interaction - focus event above will be ignored if focus is result of mouse 620 plugin.$ubermenu.on('mousedown', function (e) { 621 plugin.mousedown = true; 622 setTimeout(function () { plugin.mousedown = false; }, 100); 623 }); 624 625 }, 626 627 628 initializeSubmenuPositioning: function () { 629 var plugin = this; 630 plugin.positionSubmenus(); 631 $(window).ubersmartresize(function () { 632 plugin.positionSubmenus(); 633 }); 634 635 if (this.settings.reposition_on_load) { 636 $(window).on('load', function () { 637 plugin.positionSubmenus(); 638 }); 639 } 640 641 // Reposition when menu resize occurs 642 if ('ResizeObserver' in window) { 643 var obs = new ResizeObserver(function () { 644 plugin.positionSubmenus(); 645 }); 646 obs.observe(plugin.$ubermenu[0]); 647 } 648 }, 649 650 initializeSubmenuToggleTouchEvents: function () { 651 652 if (!this.settings.touchEvents) return; 653 654 var plugin = this; 655 656 //Touch Events 657 //this.$ubermenu.on( this.touchStart , '.ubermenu-item' , function(e){ plugin.handleTouchInteraction( e , this , plugin ); } ); 658 659 this.$ubermenu.on(this.touchStart, '.ubermenu-target:not(.shiftnav-toggle)', function (e) { plugin.handleTouchInteraction(e, this, plugin); }); 660 //this.$ubermenu.on( this.touchEnd , '.ubermenu-item' , this.handleSubmenuToggle ); //Added only on touchStart 661 //this.$ubermenu.on( this.touchMove , '.ubermenu-item' , this.preventInteractionOnScroll ); 662 663 //Prevent "Ghost Clicks" 664 this.$ubermenu.on('click', '.ubermenu-has-submenu-drop > .ubermenu-target, .ubermenu-tab.ubermenu-item-has-children > .ubermenu-target', function (e) { plugin.handleClicks(e, this, plugin); }); 665 666 }, 667 668 initializeSubmenuToggleMouseEvents: function (plugin) { 669 670 plugin = plugin || this; //Assign this to plugin if not passed 671 672 673 //Don't initialize if mouse events are disabled 674 if (!plugin.settings.mouseEvents) return; 675 if (plugin.settings.clicktest) return; 676 if (plugin.settings.windowstest) return; 677 678 679 plugin.log('initializeSubmenuToggleMouseEvents'); 680 681 682 var evt = ''; 683 var trigger = 'hover'; 684 if (plugin.$ubermenu.hasClass('ubermenu-trigger-click')) { 685 trigger = 'click'; 686 } 687 else if (plugin.$ubermenu.hasClass('ubermenu-trigger-hover_intent')) { 688 trigger = 'hover_intent'; 689 } 690 691 if (trigger == 'click') { 692 //In the event this device supports 'pointerup', let that event handle the interactions rather than the click event 693 if (!this.suppress_clicks) { 694 evt = 'click.ubermenu-submenu-toggle'; 695 //evt = 'mouseup.ubermenu-submenu-toggle'; 696 this.$ubermenu.on(evt, '.ubermenu-item.ubermenu-has-submenu-drop:not([data-ubermenu-trigger]) > .ubermenu-target', function (e) { plugin.handleMouseClick(e, this, plugin); }); 697 this.$ubermenu.on('click.ubermenu-click-target', '.ubermenu-item:not(.ubermenu-has-submenu-drop):not([data-ubermenu-trigger]) > .ubermenu-target', function (e) { plugin.handleLink(e, this, plugin); }); 698 } 699 } 700 else if (trigger == 'hover_intent') { 701 evt = 'mouseenter.mouse_intent'; // > .ubermenu-target 702 this.$ubermenu.on(evt, '.ubermenu-item.ubermenu-has-submenu-drop:not([data-ubermenu-trigger])', function (e) { plugin.handleMouseIntent(e, this, plugin); }); 703 this.$ubermenu.on('click.ubermenu-click-target', '.ubermenu-item:not([data-ubermenu-trigger]) > .ubermenu-target', function (e) { plugin.handleLink(e, this, plugin); }); 704 } 705 else { 706 evt = 'mouseenter.ubermenu-submenu-toggle'; 707 this.$ubermenu.on(evt, '.ubermenu-item.ubermenu-has-submenu-drop:not([data-ubermenu-trigger]) > .ubermenu-target', function (e) { plugin.handleMouseover(e, this, plugin); }); 708 this.$ubermenu.on('click.ubermenu-click-target', '.ubermenu-item:not([data-ubermenu-trigger]) > .ubermenu-target', function (e) { plugin.handleLink(e, this, plugin); }); 709 } 710 711 //Now find divergents 712 if (this.allow_trigger_overrides) { 713 plugin.$ubermenu.find('.ubermenu-item[data-ubermenu-trigger]').each(function () { 714 715 var $li = $(this); 716 trigger = $li.data('ubermenu-trigger'); 717 718 if (trigger == 'click') { 719 if (!this.suppress_clicks) { //hmm 720 $li.on('click.ubermenu-submenu-toggle', '> .ubermenu-target', function (e) { plugin.handleMouseClick(e, this, plugin); }); 721 } 722 } 723 else if (trigger == 'hover_intent') { 724 $li.on('mouseenter.mouse_intent', function (e) { plugin.handleMouseIntent(e, this, plugin); }); 725 //$li.find( '> .ubermenu-target' ).on( 'click.ubermenu-click-target' , function(e){ plugin.handleLink( e , this , plugin ); } ); 726 } 727 //Hover 728 else { 729 $li.on('mouseenter.ubermenu-submenu-toggle', '> .ubermenu-target', function (e) { plugin.handleMouseover(e, this, plugin); }); 730 } 731 }); 732 } 733 else { 734 //If trigger overrides aren't allowed, default tabs to click 735 plugin.$ubermenu.find('.ubermenu-tab').on('click.ubermenu-submenu-toggle', '.ubermenu-target', function (e) { plugin.handleMouseClick(e, this, plugin); }); 736 } 737 738 //this.$ubermenu.on( 'mouseout.ubermenu-submenu-toggle' , '.ubermenu-item' , this.handleMouseout ); //now only added on mouseover 739 }, 740 741 disableSubmenuToggleMouseEvents: function () { 742 this.log('disableSubmenuToggleMouseEvents'); 743 this.events_disabled = true; 744 }, 745 746 747 reenableSubmenuToggleMouseEvents: function (plugin) { 748 plugin = plugin || this; 749 750 plugin.log('reenableSubmenuToggleMouseEvents'); 751 plugin.events_disabled = false;
752 }, 753 754 755 initializeRetractors: function () { 756 757 if (!this.settings.retractors) return; //Don't initialize if retractors are disabled 758 759 var plugin = this; 760 761 this.$ubermenu.on('click', '.ubermenu-retractor', function (e) { plugin.handleSubmenuRetractorEnd(e, this, plugin); }); 762 763 //set up the retractors 764 if (this.settings.touchEvents) this.$ubermenu.on(this.touchStart, '.ubermenu-retractor', function (e) { plugin.handleSubmenuRetractorStart(e, this, plugin); }); // > .ubermenu-target 765 766 //If this device does not support touch interactions, and the strategy is touch, remove the mobile retractors 767 if (!this.touchenabled && plugin.settings.retractor_display_strategy == 'touch') { 768 this.$ubermenu.find('.ubermenu-retractor-mobile').remove(); 769 this.$ubermenu.find('.ubermenu-submenu-retractor-top').removeClass('ubermenu-submenu-retractor-top').removeClass('ubermenu-submenu-retractor-top-2'); 770 } 771 772 //Indicator toggles 773 if (this.settings.submenuIndicatorCloseMobile) { 774 var close_icon = !plugin.settings.use_core_svgs 775 ? '<' + plugin.settings.icon_tag + ' class="fas fa-times"></' + plugin.settings.icon_tag + '>' 776 : '<span class="ubermenu-icon ubermenu-icon-essential ubermenu-icon-essential-times"><svg class="ubermenu-icon-svg-times"><use xlink:href="#ubermenu-icon-times"></use></svg></span>'; 777 var $target_subs = this.$ubermenu.find('.ubermenu-has-submenu-drop > .ubermenu-target') 778 .append('<span class="ubermenu-sub-indicator-close">' + close_icon + '</span>'); 779 var $indicator_toggles = $target_subs.find('>.ubermenu-sub-indicator-close'); 780 $indicator_toggles.on('click', function (e) { 781 e.preventDefault(); 782 e.stopPropagation(); 783 plugin.closeSubmenuInstantly($(this).closest('.ubermenu-item'), 'toggleUberMenuSubmenuClosed', plugin); 784 return false; 785 }); 786 if (this.settings.touchEvents) { 787 $indicator_toggles.on(this.touchStart, function (e) { 788 e.preventDefault(); 789 e.stopPropagation(); 790 plugin.closeSubmenuInstantly($(this).closest('.ubermenu-item'), 'toggleUberMenuSubmenuClosed', plugin); 791 return false; 792 }); 793 } 794 } 795 796 }, 797 798 799 initializeResponsiveToggle: function () { 800 var plugin = this; 801 var toggle_selector = '.ubermenu-responsive-toggle[data-ubermenu-target=' + plugin.$ubermenu.attr('id') + '], .ubermenu-responsive-toggle[data-ubermenu-target=_any_], #' + plugin.$ubermenu.attr('id') + ' .ubermenu-mobile-close-button'; 802 var $toggle = $(toggle_selector); 803 804 plugin.log('initializeResponsiveToggle ' + this.toggleevent); 805 806 //When the window is resized, adjust the toggle visibility ARIA 807 if (plugin.settings.aria_responsive_toggle) { 808 809 //aria-hidden for toggle 810 var toggle_hidden = window.innerWidth > plugin.settings.breakpoint; 811 $toggle.attr('aria-hidden', toggle_hidden); 812 $(window).ubersmartresize(function () { 813 $toggle.attr('aria-hidden', window.innerWidth > plugin.settings.breakpoint); 814 }); 815 var toggle_expanded = $toggle.hasClass('ubermenu-responsive-toggle-open'); 816 $toggle.attr('aria-expanded', toggle_expanded); 817 } 818 819 $(document).on(this.toggleevent, toggle_selector, 820 function (e) { plugin.handleResponsiveToggle(e, this, plugin); } 821 ); 822 823 // If the Collapse On Navigate setting is enabled and the menu is collapsed by default 824 if ( 825 plugin.settings.mobile_menu_collapse_on_navigate && // Setting enabled 826 !plugin.$ubermenu.hasClass('ubermenu-responsive-nocollapse') // Menu should be collapsed 827 ) { 828 $(window).on('pageshow', function () { 829 if (!plugin.$ubermenu.hasClass('ubermenu-responsive-collapse')) { 830 plugin.toggleMenuCollapse('close', false, plugin); 831 } 832 }); 833 } 834 835 //IE11 Keyboard accessibility 836 var isIE11 = /Trident.*rv[ :]*11\./.test(navigator.userAgent); 837 if (isIE11) { 838 $toggle.on('keypress', function (e) { 839 if (e.keyCode === 13 || e.keyCode === 32) { 840 plugin.handleResponsiveToggle(e, this, plugin); 841 } 842 }); 843 } 844 }, 845 846 initializeMobileViewClasses: function () { 847 var plugin = this; 848 plugin.toggleMobileClass(); 849 $(window).on('ubermenu-resize-mobile', function () { 850 plugin.toggleMobileClass(true); 851 }); 852 $(window).on('ubermenu-resize-desktop', function () { 853 plugin.toggleMobileClass(false); 854 }); 855 }, 856 857 initializeMobileModal: function () { 858 var plugin = this; 859 860 if (plugin.modalMobile) { 861 862 // If we initialize on mobile 863 if (window.innerWidth <= plugin.settings.breakpoint) {
864 plugin.swapModal(true); 865 } 866 867 // If window is resized 868 $(window).on('ubermenu-resize-mobile', function () { 869 plugin.swapModal(true); 870 }); 871 $(window).on('ubermenu-resize-desktop', function () { 872 plugin.swapModal(false); 873 }); 874 } 875 }, 876 877 swapModal: function (moveToFooter) { 878 if (moveToFooter) { 879 // console.log( 'swapModal to Footer' ); 880 this.$placeholder = $('<span class="ubermenu-swap-placeholder">'); 881 this.$ubermenu.after(this.$placeholder); 882 this.$ubermenu.appendTo('body'); 883 } 884 else { 885 if (this.$placeholder) { 886 // console.log( 'swapModal to header', this.$placeholder ); 887 this.$placeholder.replaceWith(this.$ubermenu); 888 this.$placeholder = false; 889 } 890 } 891 }, 892 893 initializeTouchoffClose: function () { 894 895 if (!this.settings.touchOffClose) return; //Don't initialize if touch off close is disabled 896 897 var plugin = this; 898 $(document).on(this.touchStart + '.ubermenu_touchoff', function (e) { plugin.handleTouchoffCloseStart(e, this, plugin); }); 899 $(document).on(this.touchEnd + '.ubermenu_touchoff', function (e) { plugin.handleTouchoffClose(e, this, 'touch', plugin); }); 900 if (!this.suppress_clicks) $(document).on('mouseup.ubermenu_clickoff', function (e) { plugin.handleTouchoffClose(e, this, 'click', plugin); }); //use mouseup instead of click for firefox 901 902 }, 903 904 905 initializeTabs: function () { 906 907 var plugin = this; 908 var responsive = plugin.settings.responsive && (window.innerWidth <= plugin.settings.breakpoint) ? true : false; 909 910 plugin.$tab_blocks = plugin.$ubermenu.find('.ubermenu-tabs'); 911 912 //Reverse order so we do the innermost panels first 913 plugin.$tab_blocks = $(plugin.$tab_blocks.get().reverse()); 914 915 //When all the images load, check the tabs 916 $(window).on('load', function () { 917 plugin.sizeTabs(); 918 }); 919 920 //When the window is resized, check the tabs 921 plugin.windowwidth = window.innerWidth; 922 $(window).ubersmartresize(function () { 923 plugin.oldwindowwidth = plugin.windowwidth; 924 plugin.windowwidth = window.innerWidth; 925 //only run if width has changed 926 if (plugin.windowwidth != plugin.oldwindowwidth) { 927 plugin.clearTabSizes(plugin); 928 plugin.sizeTabs(); 929 plugin.checkActiveTabs(plugin); 930 } 931 }); 932 933 //When the submenu is opened (first time only), check the tabs 934 plugin.$ubermenu.find('.ubermenu-item-level-0.ubermenu-has-submenu-drop').on('ubermenuopen.sizetabs', function () { 935 $(this).off('ubermenuopen.sizetabs'); 936 plugin.sizeTabs(); 937 }); 938 //plugin.sizeTabs(); 939 940 //Look for dynamically sized tabs, as we need to resize each time we switch tabs 941 plugin.$ubermenu.find('.ubermenu-tabs.ubermenu-tabs-dynamic-sizing').on('ubermenuopen', '> .ubermenu-tabs-group > .ubermenu-tab', function () { 942 plugin.sizeTabsDynamic($(this).closest('.ubermenu-tabs')); 943 }); 944 945 if (!responsive) { 946 plugin.initializeActiveTab(plugin); 947 } 948 949 // Close tabs if hovering empty toggle if not in Click mode 950 plugin.$ubermenu.find('.ubermenu-tabs-group:not(.ubermenu-tabs-group--trigger-click) > .ubermenu-tab:not(.ubermenu-has-submenu-drop):not(.ubermenu-ignore-empty-tab)').on('mouseenter', function () { 951 plugin.closeSubmenuInstantly($(this).siblings('.ubermenu-active')); 952 }); 953 }, 954 955 checkActiveTabs: function (plugin) { 956 if (window.innerWidth <= plugin.settings.breakpoint) { 957 //close all open tabs 958 plugin.$tab_blocks.find('.ubermenu-tab.ubermenu-active').removeClass('ubermenu-active'); 959 } 960 else { 961 plugin.initializeActiveTab(plugin); 962 } 963 }, 964 965 initializeActiveTab: function (plugin) { 966 967 plugin.$tab_blocks.each(function () { 968 var show_default = $(this).hasClass('ubermenu-tabs-show-default'); 969 var show_current = $(this).hasClass('ubermenu-tabs-show-current'); 970 var $tabs_group = $(this).find('> .ubermenu-tabs-group'); 971 972 //If there's already an active tab, skip - nothing to do 973 if ($tabs_group.find('> .ubermenu-tab.ubermenu-active').length) return; 974 975 var tab_shown = false; 976 977 //Current 978 if (show_current) { 979 //Fill in any ancestor tab gaps 980 $tabs_group.find('.ubermenu-current-menu-item').parentsUntil($tabs_group, '.ubermenu-tab:not( .ubermenu-nocurrent )').addClass('ubermenu-current-menu-ancestor'); 981 982 //Find any current tabs 983 var $current_tab = $tabs_group.find('> .ubermenu-tab.ubermenu-current-menu-ancestor, > .ubermenu-tab.ubermenu-current-menu-item'); 984 if ($current_tab.length) { 985 plugin.openSubmenu($current_tab.first(), 'tab current', plugin); 986 tab_shown = true; 987 } 988 } 989 990 //Default 991 if (show_default && !tab_shown) { 992 //If there are no active tabs, activate the first one 993 if ($tabs_group.find('> .ubermenu-tab.ubermenu-active').length === 0) { 994 plugin.openSubmenu($tabs_group.find('> .ubermenu-tab').first(), 'tab default', plugin); 995 } 996 } 997 998 //Close all others? .ubermenu( 'closeSubmenu' , $current_tabs.siblings() ); 999 1000 }); 1001 }, 1002 1003 clearTabSizes: function (plugin) { 1004 plugin = plugin || this; 1005 plugin.$ubermenu.find('.ubermenu-submenu , .ubermenu-tabs , .ubermenu-tab-content-panel , .ubermenu-tabs-group').css('min-height', ''); 1006 }, 1007 1008 1009 sizeTabs: function () { 1010 1011 var plugin = this; 1012 var responsive = plugin.settings.responsive && (window.innerWidth <= plugin.settings.breakpoint) ? true : false;
1013 1014 //If this is responsive and we're below the breakpoint, don't size the tabs 1015 if (responsive) return; 1016 1017 plugin.initializeActiveTab(plugin); 1018 1019 //Do each tabs block 1020 plugin.$tab_blocks.each(function () { 1021 1022 //Stacked is if tabs are (a) Layout: Top, (b) Layout: Bottom, (c) Responsive Mode 1023 var stacked = false; 1024 1025 if (($(this).hasClass('ubermenu-tab-layout-top') || 1026 $(this).hasClass('ubermenu-tab-layout-bottom')) && 1027 !responsive) { 1028 1029 stacked = true; 1030 } 1031 1032 $(this).data('um-stacked', stacked); 1033 1034 var maxh = 0; 1035 1036 1037 var $tree; 1038 if (responsive) { 1039 $tree = $(this).parentsUntil('.ubermenu').add($(this).parents('.ubermenu')); 1040 } 1041 else { 1042 $tree = $(this).parentsUntil('.ubermenu-item-level-0'); 1043 } 1044 $tree.addClass('ubermenu-test-dimensions'); 1045 1046 //Find the maximum panel height for this group - only immediate tab panels, not nested 1047 var $panels = $(this).find(' > .ubermenu-tabs-group > .ubermenu-tab > .ubermenu-tab-content-panel'); 1048 var oh; 1049 $panels.each(function () { 1050 $(this).addClass('ubermenu-test-dimensions'); 1051 oh = $(this).outerHeight(); //outerheight 1052 if (oh > maxh) maxh = oh; 1053 $(this).data('um-oh', oh); 1054 $(this).removeClass('ubermenu-test-dimensions'); 1055 }); 1056 1057 $(this).data('um-max-panel-height', maxh); 1058 1059 //if( $(this).is( '#menu-item-185' ) ){ 1060 if ($(this).hasClass('ubermenu-tabs-dynamic-sizing')) { 1061 plugin.sizeTabsDynamic($(this), false); 1062 } 1063 else { 1064 plugin.sizeTabsMax($(this)); 1065 } 1066 1067 $tree.removeClass('ubermenu-test-dimensions'); 1068 1069 }); 1070 1071 }, 1072 1073 sizeTabsMax: function ($tab_block) { 1074 1075 var maxh = $tab_block.data('um-max-panel-height'); 1076 var stacked = $tab_block.data('um-stacked'); 1077 var $tabsgroup = $tab_block.find('> .ubermenu-tabs-group'); 1078 1079 //If left or right layout, set min-height on tabs group as well 1080 if (!stacked) { 1081 if ($tabsgroup.outerHeight() > maxh) { 1082 maxh = $tab_block.outerHeight(); 1083 } 1084 $tabsgroup.css('min-height', maxh); 1085 } 1086 //If top or bottom layout, set height for entire block 1087 else { 1088 //console.log( maxh , $tabsgroup.outerHeight() ); 1089 $tab_block.css('min-height', maxh + $tabsgroup.outerHeight()); 1090 } 1091 1092 //var $sub = $tab_block.closest( '.ubermenu-submenu-drop' ); 1093 //$sub.css( 'min-height' , false ); //this line does nothing, false is not valid - use '' to unset 1094 1095 //Set panel heights 1096 $tabsgroup.find('> .ubermenu-tab > .ubermenu-tab-content-panel').css('min-height', maxh); 1097 1098 }, 1099 1100 sizeTabsDynamic: function ($tab_block, animate) { 1101 1102 if (animate === undefined) animate = true; 1103 if (animate) { 1104 animate = $tab_block.hasClass('ubermenu-tabs-dynamic-sizing-animate'); 1105 } 1106 1107 var plugin = this; 1108 var responsive = plugin.settings.responsive && (window.innerWidth <= plugin.settings.breakpoint) ? true : false; 1109 if (responsive) return; //don't size when mobile 1110 1111 var stacked = $tab_block.data('um-stacked'); 1112 1113 //Look at the tabs group for the active item 1114 var $tabsgroup = $tab_block.find('> .ubermenu-tabs-group'); 1115 var prev_height = $tabsgroup.outerHeight(); 1116 $tabsgroup.css('min-height', '0'); //reset height 1117 var $active_panel = $tabsgroup.find('> .ubermenu-active > .ubermenu-tab-content-panel'); 1118 var active_oh = $active_panel.data('um-oh'); 1119 var tg_oh = $tabsgroup.outerHeight(); 1120 var maxh = tg_oh > active_oh ? $tab_block.outerHeight() : active_oh; //get the max height of the tabs group vs active tab 1121 1122 if (!stacked) { 1123 if (animate) { 1124 $tabsgroup.css('min-height', prev_height); 1125 $tabsgroup.stop().animate({ 'min-height': maxh }, 300, 'swing', function () { 1126 $active_panel.css('overflow', 'auto'); //force repaint for background images 1127 }); //set the tabs group height 1128 } 1129 else { 1130 $tabsgroup.css('min-height', maxh); //set the tabs group height 1131 } 1132 1133 } 1134 //If top or bottom layout, set height for entire block 1135 else { 1136 if (animate) { 1137 $tab_block.stop().animate({ 'min-height': maxh + $tabsgroup.outerHeight() }, 300, 'swing', function () { 1138 $active_panel.css('overflow', 'auto'); 1139 }); 1140 } 1141 else { 1142 $tab_block.css('min-height', maxh + $tabsgroup.outerHeight()); 1143 } 1144 } 1145 }, 1146 1147 initializeSegmentCurrentStates: function () { 1148 this.$ubermenu.find('.ubermenu-current-menu-item').first().parents('.ubermenu-item:not( .ubermenu-nocurrent )').addClass('ubermenu-current-menu-ancestor'); 1149 }, 1150 1151 disableTransitions: function () { 1152 this.transitions = false;
1153 this.$ubermenu 1154 .removeClass('ubermenu-transition-slide') 1155 .removeClass('ubermenu-transition-fade') 1156 .removeClass('ubermenu-transition-shift') 1157 .addClass('ubermenu-no-transitions') 1158 .addClass('ubermenu-transition-none'); 1159 }, 1160 1161 toggleMobileClass: function (on) { 1162 1163 if (on === undefined) { 1164 on = this.isMobile(); 1165 } 1166 1167 if (on) { 1168 this.$ubermenu.removeClass('ubermenu-desktop-view').addClass('ubermenu-mobile-view'); 1169 } 1170 else { 1171 this.$ubermenu.removeClass('ubermenu-mobile-view').addClass('ubermenu-desktop-view'); 1172 } 1173 }, 1174 1175 /* Handlers */ 1176 1177 handleClicks: function (e, a, plugin) { 1178 1179 var $a = $(a); 1180 1181 //Kill any clicks flagged by touchend 1182 if ($a.data('ubermenu-killClick')) { 1183 e.preventDefault(); 1184 //e.stopPropagation(); 1185 plugin.log('killed click after touchend ', e); 1186 } 1187 1188 //Reset flag in any event 1189 //plugin.log( 'reset kill click' ); 1190 //$a.data( 'ubermenu-killClick' , false ); 1191 }, 1192 1193 handleTouchInteraction: function (e, target, plugin) { 1194 1195 // e.preventDefault(); //this would stop clicks, but also scroll, which is a problem 1196 e.stopPropagation(); 1197 1198 //If we're touching Windows, disable transitions 1199 if (e.type.indexOf('pointer') >= 0) plugin.disableTransitions(); 1200 1201 var $target = $(target); 1202 //Move to HandleTap 1203 // $target.data( 'ubermenu-killClick' , true ); //Prevent Clicks from being handled normally 1204 // $target.data( 'ubermenu-killHover' , true ); //Prevent hover from being handled normally 1205 // setTimeout( function(){ $target.data( 'ubermenu-killClick' , false ).data( 'ubermenu-killHover' , false ); } , 1000 ); 1206 $target.parent().off('mouseleave.mouse_intent_none'); //don't allow hover intent out if we're touching 1207 1208 plugin.log('touchstart ' + e.type + ' ' + $target.text(), e); 1209 1210 //Setup touch events 1211 //plugin.log( 'setup touch events' ); 1212 $target.on(plugin.touchEnd, function (e) { plugin.handleTap(e, this, plugin); }); 1213 $target.on(plugin.touchMove, function (e) { plugin.preventInteractionOnScroll(e, this, plugin); }); 1214 1215 1216 //Note original event points for comparison 1217 //Standard 1218 if (e.originalEvent.touches) { 1219 $target.data('ubermenu-startX', e.originalEvent.touches[0].clientX); 1220 $target.data('ubermenu-startY', e.originalEvent.touches[0].clientY); 1221 } 1222 //Microsoft 1223 else if (e.originalEvent.clientY) { 1224 var pos = $target.offset(); 1225 //$target.data( 'ubermenu-pos' , pos ); 1226 $target.data('ubermenu-startX', e.originalEvent.clientX); 1227 $target.data('ubermenu-startY', e.originalEvent.clientY); 1228 } 1229 // else if( e.changedTouches ){ 1230 // $target.data( 'ubermenu-startX' , e.changedTouches[0].pageX ); 1231 // $target.data( 'ubermenu-startY' , e.changedTouches[0].pageY ); 1232 // } 1233 }, 1234 1235 preventInteractionOnScroll: function (e, target, plugin) { 1236 1237 plugin.log('touchmove interaction ' + e.type, e); 1238 1239 var $target = $(target); 1240 1241 //Check to see if the touch points were close enough together to be considered a tap 1242 //If not, they were a move/scroll, so unbind the touch event handlers and don't trigger menu 1243 if (e.originalEvent.touches) { 1244 if (Math.abs(e.originalEvent.touches[0].clientX - $target.data('ubermenu-startX')) > plugin.settings.moveThreshold || 1245 Math.abs(e.originalEvent.touches[0].clientY - $target.data('ubermenu-startY')) > plugin.settings.moveThreshold) { 1246 plugin.log('Preventing interaction on scroll, reset handlers (standard)'); 1247 plugin.resetHandlers($target, 'preventScroll touches', plugin); 1248 } 1249 else { 1250 plugin.log('diff = ' + Math.abs(e.originalEvent.touches[0].clientY - $target.data('ubermenu-startY'))); 1251 } 1252 } 1253 else if (e.originalEvent.clientY) { 1254 var pos = $target.data(pos); 1255 if (Math.abs(e.originalEvent.clientX - $target.data('ubermenu-startX')) > plugin.settings.moveThreshold || 1256 Math.abs(e.originalEvent.clientY - $target.data('ubermenu-startY')) > plugin.settings.moveThreshold) { 1257 plugin.log('Preventing interaction on scroll, reset handlers (standard)'); 1258 plugin.resetHandlers($target, 'preventScroll client', plugin); 1259 } 1260 else { 1261 plugin.log('diff = ' + e.originalEvent.clientY + ' - ' + $target.data('ubermenu-startY') + ' = ' + Math.abs(e.originalEvent.clientY - $target.data('ubermenu-startY'))); 1262 } 1263 } 1264 /* 1265 else if( e.changedTouches ){ 1266 if( Math.abs( e.changedTouches[0].pageX - $target.data( 'ubermenu-startX' ) ) > plugin.settings.moveThreshold || 1267 Math.abs( e.changedTouches[0].pageY - $target.data( 'ubermenu-startY' ) ) > plugin.settings.moveThreshold ){ 1268 plugin.log( 'Preventing interaction on scroll, reset handlers (Windows)' ); 1269 plugin.resetHandlers( $target ); 1270 } 1271 }*/ 1272 else plugin.log('no touch points found!'); 1273 1274 }, 1275 1276 1277 /* Handle tap - a touch interaction that completed */ 1278 handleTap: function (e, target, plugin) { 1279 1280 e.preventDefault(); 1281 e.stopPropagation(); 1282 1283 var $target = $(target); //.parent(); 1284 1285 plugin.setInteractionMode('press'); 1286 1287 //Quit if this was triggered after regular hover (which happens on IE) 1288 //Kill any touches flagged by mouseenter 1289 if ($target.data('ubermenu-killTouch')) { 1290 plugin.log('kill tap'); 1291 e.preventDefault(); 1292 e.stopPropagation(); 1293 } 1294 else { 1295 1296 var $li = $target.parent(); 1297 1298 plugin.log('handleTap [' + $target.text() + ']', e.type); 1299 1300 //$target.data( 'ubermenu-killHover' , true ); 1301 1302 $target.data('ubermenu-killClick', true); //Prevent Clicks from being handled normally 1303 $target.data('ubermenu-killHover', true); //Prevent hover from being handled normally 1304 setTimeout(function () { $target.data('ubermenu-killClick', false).data('ubermenu-killHover', false); }, 1000); 1305 1306 // 1307 //Close up other submenus before proceeding 1308 // - except if we're below the mobile breakpoint in accordion format 1309 // 1310 if (!plugin.isAccordion() || plugin.isAccordionFoldingSingle()) { 1311 plugin.closeSubmenuInstantly($li.siblings('.ubermenu-active')); 1312 } 1313 1314 1315 // 1316 //Toggle Submenus 1317 // 1318 1319 if ($li.hasClass('ubermenu-has-submenu-drop')) { 1320 1321 //if submenu is already open, close it and allow link to be followed 1322 if ($li.hasClass('ubermenu-active')) { 1323 1324 //Don't close tabs, unless we're on mobile 1325 if (!$li.hasClass('ubermenu-tab') || plugin.isMobile()) { 1326 plugin.closeSubmenu($li, 'toggleUberMenuActive', plugin); 1327 } 1328 //plugin.followLink( e , $li , plugin ); 1329 plugin.handleLink(e, target, plugin, true); 1330 } 1331 //if submenu is closed, open the submenu, prevent link from being followed 1332 else { 1333 plugin.openSubmenu($li, 'toggle', plugin); 1334 } 1335 } 1336 1337 // 1338 //Follow links that don't have submenus 1339 // 1340 else { 1341 //plugin.followLink( e , $li , plugin ); 1342 plugin.handleLink(e, target, plugin, true); 1343 } 1344 } 1345 1346 //Reset flag in any event 1347 $target.data('ubermenu-killTouch', false); 1348 1349 //plugin.resetHandlers( $li ); 1350 //plugin.log( 'reset handlers' ); 1351 plugin.resetHandlers($target, 'handleTap', plugin); 1352 1353 }, 1354 1355 1356 handleLink: function (e, link, plugin, follow) { 1357 1358 follow = follow || false; 1359 1360 plugin.log('handleLink'); 1361 1362 //e.preventDefault(); 1363 1364 var $link = $(link); 1365 1366 if (!$link.is('a')) return; 1367 1368 var href = $link.attr('href'); 1369 1370 var scrolltarget = $link.data('ubermenu-scrolltarget'); 1371 1372 if (scrolltarget) { 1373 1374 var $target_el = $(scrolltarget).first(); 1375 if ($target_el.length > 0) { 1376 e.preventDefault(); 1377 $link.trigger('ubermenuscrollto'); 1378 var $li = $link.parent('.ubermenu-item'); 1379 $li.addClass('ubermenu-current-menu-item'); 1380 $li.siblings().removeClass('ubermenu-current-menu-item').removeClass('ubermenu-current-menu-parent').removeClass('uberemnu-current-menu-ancestor'); 1381 1382 var anim_done = false;
1383 $('html,body').animate({ 1384 scrollTop: $target_el.offset().top - plugin.settings.scrollto_offset 1385 }, plugin.settings.scrollto_duration, 'swing', 1386 function () { 1387 //after scroll 1388 if (!anim_done) { 1389 plugin.closeSubmenu($link.closest('.ubermenu-item-level-0'), 'handeLink', plugin); 1390 if (plugin.settings.collapse_after_scroll && !plugin.$ubermenu.hasClass('ubermenu-responsive-nocollapse')) plugin.toggleMenuCollapse('toggle', false, plugin); 1391 $link.trigger('ubermenuscrollto_complete'); 1392 anim_done = true; 1393 } 1394 }); 1395 return false; //don't follow any links if this scroll target is present 1396 } 1397 //if target isn't present here, redirect with hash 1398 else { 1399 if (href && href.indexOf('#') == -1) { //check that hash does not already exist 1400 if (scrolltarget.indexOf('#') == -1) { //if this is a class, add a hash tag 1401 scrolltarget = '#' + scrolltarget; 1402 } 1403 window.location = href + scrolltarget; //append hash/scroll target to URL and redirect 1404 e.preventDefault(); 1405 return false; // So that the click event doesn't fall through to the next if/else and redirect to the href without hash 1406 } 1407 //No href, no worries 1408 } 1409 } 1410 1411 if (!href) { 1412 e.preventDefault(); 1413 } 1414 1415 //Handle clicks (where default was already prevented) 1416 else { 1417 if (follow && e.isDefaultPrevented()) { 1418 plugin.log('default prevented, follow link'); 1419 if ($link.attr('target') == '_blank') { 1420 window.open(href, '_blank'); 1421 } 1422 else { 1423 window.location = href; 1424 } 1425 } 1426 } 1427 1428 }, 1429 1430 1431 handleMouseClick: function (e, target, plugin) { 1432 plugin.log('handleMouseClick', e); 1433 1434 //Stop link follow 1435 //e.preventDefault(); //defer to 'else' so that the second click can be handled naturally 1436 1437 var $target = $(target); 1438 1439 if ($target.data('ubermenu-killClick')) { 1440 plugin.log('handleMouseClick: killClick'); 1441 return; //3.0.4.1 1442 } 1443 1444 var $item = $target.parent('.ubermenu-item'); //$( li ); 1445 1446 if ($item.length) { 1447 //Check if this is already open 1448 if ($item.hasClass('ubermenu-active')) { 1449 1450 //If it's a link, follow 1451 if ($target.is('a')) { 1452 plugin.handleLink(e, target, plugin); 1453 } 1454 1455 //If it's not a link (and not a tab), retract 1456 if (!$item.hasClass('ubermenu-tab')) { 1457 plugin.closeSubmenu($item, 'retract'); 1458 } 1459 } 1460 1461 //Close other menus, open this one 1462 else { 1463 1464 plugin.setInteractionMode('press'); 1465 1466 //Submenu to open 1467 if ($item.hasClass('ubermenu-has-submenu-drop')) { 1468 e.preventDefault(); //Not already active, don't allow click 1469 // Close other submenus, if not on mobile in accordion mode 1470 if (!plugin.isAccordion() || plugin.isAccordionFoldingSingle()) { 1471 plugin.closeSubmenuInstantly($item.siblings('.ubermenu-active')); 1472 } 1473 plugin.openSubmenu($item, 'click', plugin); 1474 } 1475 //Allow link to be followed 1476 else { 1477 //Just let it go 1478 //plugin.handleLink( e , target , plugin ); 1479 } 1480 } 1481 } 1482 1483 //Only attach mouseout after mouseover, this way menus opened by touch won't be closed by mouseout 1484 //$( li ).on( 'mouseout.ubermenu-submenu-toggle' , function( e ){ plugin.handleMouseout( e , this , plugin ); } ); 1485 }, 1486 1487 1488 1489 1490 1491 1492 handleMouseIntent: function (e, item, plugin) { ///*target*/ 1493 1494 plugin.log('handleMouseIntent'); 1495 plugin.setInteractionMode('hover'); 1496 1497 //var $target = $( target ); 1498 //var $item = $target.parent( '.ubermenu-item' ); 1499 var $item = $(item); 1500 1501 //console.log( '[' + $item.find('> .ubermenu-target').text() + '] handle mouse_intent' ); 1502 1503 //Cancel Timer 1504 if ($item.data('mouse_intent_timer')) { 1505 $item.data('mouse_intent_timer', clearTimeout($item.data('mouse_intent_timer'))); 1506 //console.log( 'clear timeout' ); 1507 } 1508 1509 //Prevent Touch events, which will occur on IE 1510 // var $target = $item.find( '> .ubermenu-target' ); 1511 // $target.data( 'ubermenu-killTouch' , true ); 1512 // setTimeout( function(){ $target.data( 'ubermenu-killTouch' , false ); } , 1000 ); 1513 1514 //Quit if this was triggered by touch 1515 var $a = $item.find('.ubermenu-target'); 1516 //Kill any hovers flagged by touchstart 1517 if ($a.data('ubermenu-killHover')) { 1518 plugin.log('killHover MouseIntent'); 1519 e.preventDefault(); 1520 e.stopPropagation(); 1521 return; 1522 } 1523 1524 pX = e.pageX; pY = e.pageY; 1525 1526 1527 $item.on('mousemove.mouse_intent', plugin.trackMouse); 1528 1529 $item.data('mouse_intent_timer', setTimeout( 1530 function () { 1531 plugin.compare(e, $item, plugin.handleMouseIntentSuccess, plugin); 1532 }, 1533 plugin.settings.intent_interval 1534 )); 1535 1536 //Cancel if out - since we only hook up the close submenu event after a successfull intent 1537 $item.on('mouseleave.mouse_intent_none', function () { 1538 //plugin.log( 'mouseleave' ); 1539 $(this).data('mouse_intent_timer', clearTimeout($(this).data('mouse_intent_timer'))); 1540 $item.data('mouse_intent_state', 0); 1541 $item.off('mouseleave.mouse_intent_none'); 1542 1543 if ($a.data('ubermenu-killHover')) { 1544 plugin.log('killHover MouseIntent_Cancel'); 1545 e.preventDefault(); 1546 e.stopPropagation(); 1547 return; 1548 } 1549 1550 plugin.closeSubmenu($item, 'mouse_intent_cancel', plugin); 1551 }); 1552 //} 1553 //else console.log( 'STATE 1' ); 1554 1555 1556 }, 1557 1558 handleMouseIntentSuccess: function (e, $item, plugin) { 1559 1560 plugin.log('handleMouseIntentSuccess'); 1561 1562 //Cancel the quickleave event 1563 $item.off('mouseleave.mouse_intent_none'); 1564 1565 //Quit if this was triggered by touch - on Android Stock, this is triggered after touch, but mouseenter is triggered before 1566 var $a = $item.find('.ubermenu-target'); 1567 //Kill any hovers flagged by touchstart 1568 if ($a.data('ubermenu-killHover')) { 1569 plugin.log('Kill hover on IntentSuccess'); 1570 e.preventDefault(); 1571 e.stopPropagation(); 1572 return; 1573 } 1574 //Reset flag in any event 1575 $a.data('ubermenu-killHover', false); 1576 1577 1578 plugin.triggerSubmenu(e, $item, plugin); 1579 1580 //Setup mouseleave, but not for tabs, except below breakpoint 1581 if (!$item.hasClass('ubermenu-tab') || window.innerWidth <= plugin.settings.breakpoint) { 1582 $item.on('mouseleave.mouse_intent', function (e) { plugin.handleMouseIntentLeave(e, this, plugin); }); 1583 } 1584 }, 1585 1586 handleMouseIntentLeave: function (e, item, plugin) { 1587 1588 //var $target = $( target ); 1589 //var $item = $target.parent( '.ubermenu-item' ); 1590 var $item = $(item); 1591 1592 //console.log( '[' + $item.find('> .ubermenu-target').text() + '] handle mouse_intent LEAVE' ); 1593 1594 //Cancel timer 1595 if ($item.data('mouse_intent_timer')) { 1596 $item.data('mouse_intent_timer', clearTimeout($item.data('mouse_intent_timer'))); 1597 //console.log( 'clear timeout' ); 1598 } 1599 1600 $item.off('mousemove.mouse_intent', plugin.trackMouse); 1601 //$item.off( 'mouseleave.mouse_intent' ); //special 1602 1603 if ($item.data('mouse_intent_state') == 1) { 1604 //$item.data( 'mouse_intent_state' , 0 ); 1605 $item.data('mouse_intent_timer', setTimeout( 1606 function () { 1607 plugin.delayMouseLeave(e, $item, plugin.handleMouseIntentLeaveSuccess, plugin); 1608 }, 1609 plugin.settings.intent_delay 1610 )); 1611 } 1612 1613 }, 1614 1615 handleMouseIntentLeaveSuccess: function (e, $el, plugin) { 1616 $el.off('mouseleave.mouse_intent'); //special 1617 if ($el.find('> .ubermenu-target').data('ubermenu-killHover')) return; 1618 plugin.closeSubmenu($el, 'mouse_intent_leave', plugin); 1619 1620 }, 1621 1622 delayMouseLeave: function (e, $el, func, plugin) { 1623 //console.log( 'delay mouse leave ' ); 1624 $el.data('mouse_intent_timer', clearTimeout($el.data('mouse_intent_timer'))); 1625 $el.data('mouse_intent_state', 0); 1626 return func.apply($el, [e, $el, plugin]); 1627 }, 1628 1629 trackMouse: function (e) { 1630 cX = e.pageX; 1631 cY = e.pageY; 1632 }, 1633 1634 compare: function (e, $el, func, plugin) { 1635 1636 //console.log( 'compare' ); 1637 1638 $el.data('mouse_intent_timer', clearTimeout($el.data('mouse_intent_timer'))); 1639 1640 // compare mouse positions to see if they've crossed the threshold 1641 if ((Math.abs(pX - cX) + Math.abs(pY - cY)) < plugin.settings.intent_threshold) { 1642 //console.log( 'threshold met' ); 1643 $el.off('mousemove.mouse_intent', plugin.track); 1644 // set hoverIntent state to true (so mouseOut can be called) 1645 $el.data('mouse_intent_state', 1); 1646 return func.apply($el, [e, $el, plugin]); 1647 } else { 1648 //console.log( 'keep polling' );
1649 // set previous coordinates for next time 1650 pX = cX; pY = cY; 1651 // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) 1652 $el.data('mouse_intent_timer', setTimeout(function () { plugin.compare(e, $el, func, plugin); }, plugin.settings.intent_interval)); 1653 } 1654 }, 1655 1656 1657 1658 1659 1660 triggerSubmenu: function (e, $item, plugin) { 1661 plugin.closeSubmenuInstantly($item.siblings('.ubermenu-active, .ubermenu-in-transition')); 1662 plugin.openSubmenu($item, 'mouseenter', plugin); 1663 }, 1664 1665 1666 1667 1668 handleMouseover: function (e, target, plugin) { 1669 1670 if (plugin.events_disabled) return; 1671 plugin.setInteractionMode('hover'); 1672 1673 var $target = $(target); 1674 1675 $target.data('ubermenu-killTouch', true); 1676 setTimeout(function () { $target.data('ubermenu-killTouch', false); }, 1000); 1677 1678 plugin.log('handleMouseenter, add mouseleave', e); 1679 1680 var $item = $target.parent('.ubermenu-item'); //$( li ); 1681 1682 if ($item.length) { 1683 if (!$item.hasClass('ubermenu-active')) { 1684 1685 plugin.triggerSubmenu(e, $item, plugin); 1686 1687 //Set up mouseleave event, but no hover out for Tabs 1688 if (!$item.hasClass('ubermenu-tab') || window.innerWidth <= plugin.settings.breakpoint) { 1689 $item.on('mouseleave.ubermenu-submenu-toggle', function (e) { plugin.handleMouseleave(e, this, plugin); }); 1690 } 1691 } 1692 } 1693 }, 1694 1695 //Mouseleave should be for LI so that we can use subs 1696 handleMouseleave: function (e, li, plugin) { 1697 1698 plugin.log('handleMouseleave, remove mouseleave', e); 1699 1700 $(li).off('mouseleave.ubermenu-submenu-toggle'); //Unbind mouseout event, it'll be rebind next mouseover 1701 1702 plugin.closeSubmenu($(li), 'mouseout'); 1703 }, 1704 1705 1706 1707 handleSubmenuRetractorStart: function (e, li, plugin) { 1708 e.preventDefault(); 1709 e.stopPropagation(); 1710 1711 $(li).on(plugin.touchEnd, function (e) { plugin.handleSubmenuRetractorEnd(e, this, plugin); }); 1712 1713 plugin.log('handleSubmenuRetractorStart ' + $(li).text()); 1714 1715 }, 1716 1717 handleSubmenuRetractorEnd: function (e, li, plugin) { 1718 e.preventDefault(); 1719 e.stopPropagation(); 1720 1721 var $parent_item = $(li).closest('.ubermenu-item'); 1722 plugin.closeSubmenu($parent_item, 'handleSubmenuRetractor'); 1723 1724 $(li).off(plugin.touchEnd); 1725 1726 plugin.log('handleSubmenuRetractorEnd ' + $parent_item.find('> .ubermenu-target').text()); 1727 return false; 1728 1729 }, 1730 1731 1732 handleResponsiveToggle: function (e, toggle, plugin) { 1733 1734 plugin.log('handleResponsiveToggle ' + e.type, e); 1735 1736 e.preventDefault(); 1737 e.stopPropagation(); 1738 1739 //Prevent Double-fire 1740 //some browsers fire click and touch, so prevent the click manually 1741 if (e.type == 'touchend') { 1742 plugin.$ubermenu.data('ubermenu-prevent-click', true); 1743 setTimeout(function () { 1744 plugin.$ubermenu.data('ubermenu-prevent-click', false); 1745 }, 500); 1746 } 1747 else if (e.type == 'click' && plugin.$ubermenu.data('ubermenu-prevent-click')) { 1748 plugin.$ubermenu.data('ubermenu-prevent-click', false); 1749 return; 1750 } 1751 1752 //TODO just call off/on click.xyz? or binding already fired? 1753 //Or should we just have a state set that returns, independent of event type 1754 1755 plugin.toggleMenuCollapse('toggle', toggle, plugin); 1756 1757 }, 1758 1759 handleTouchoffCloseStart: function (e, _this, plugin) { 1760 plugin.touchoffclosestart = $(window).scrollTop(); 1761 }, 1762 1763 handleTouchoffClose: function (e, _this, eventtype, plugin) { 1764 //Only fire if the touch event occurred outside the menu AND we've clicked OR we've touched but haven't scrolled 1765 if (!$(e.target).closest('.ubermenu').length && (eventtype == 'click' || plugin.touchoffclosestart == $(window).scrollTop())) { 1766 1767 plugin.log('touchoff close ', e); 1768 1769 //If there are any submenus to close, close them and temporarily disable hover events 1770 if (plugin.closeAllSubmenus()) { 1771 //console.log( plugin.settings.submenuAnimationDuration ); 1772 //temporarily disable hovering so that the mouseover event doesn't fire and reopen the menu 1773 plugin.disableSubmenuToggleMouseEvents(); 1774 window.setTimeout(function () { 1775 //plugin.initializeSubmenuToggleMouseEvents( plugin ); 1776 plugin.reenableSubmenuToggleMouseEvents(plugin); 1777 }, 1778 plugin.settings.submenuAnimationDuration); 1779 1780 //can't call /e.preventDefault(); because it will stop mouseover events from firing 1781 } 1782 } 1783 }, 1784 1785 1786 1787 1788 /* Controllers */ 1789 1790 toggleMenuCollapse: function (action, toggle, plugin) { 1791 1792 plugin = plugin || this; 1793 toggle = toggle || '.ubermenu-resposive-toggle'; 1794 //console.log( toggle + '[data-ubermenu-target="'+ plugin.$ubermenu.attr( 'id' ) + '"]' ); 1795 var $toggle; 1796 //Passed regular Object 1797 if (typeof toggle == 'object') { 1798 $toggle = $(toggle); 1799 } 1800 //Passed selector string 1801 else { 1802 $toggle = $(toggle + '[data-ubermenu-target="' + plugin.$ubermenu.attr('id') + '"]'); 1803 } 1804 1805 action = action || 'toggle'; 1806
1807 if (action == 'toggle') { 1808 if (plugin.$ubermenu.hasClass('ubermenu-responsive-collapse')) { 1809 action = 'open'; 1810 } 1811 else { 1812 action = 'close'; 1813 } 1814 } 1815 1816 //plugin.$ubermenu.slideToggle(); 1817 1818 var $allToggles = $('.ubermenu-responsive-toggle[data-ubermenu-target=' + plugin.$ubermenu.attr('id') + ']'); 1819 1820 if (action == 'open') { 1821 plugin.$ubermenu.removeClass('ubermenu-responsive-collapse').trigger('ubermenutoggledopen'); 1822 $allToggles.trigger('ubermenutoggledopen'); 1823 $allToggles.toggleClass('ubermenu-responsive-toggle-open'); 1824 1825 if (plugin.modalMobile) plugin.trapModalFocus($toggle); 1826 1827 if (plugin.settings.aria_responsive_toggle) { 1828 $allToggles.attr('aria-expanded', true); 1829 } 1830 } 1831 else { 1832 plugin.$ubermenu.addClass('ubermenu-responsive-collapse').trigger('ubermenutoggledclose'); 1833 $allToggles.trigger('ubermenutoggledclose'); 1834 $allToggles.toggleClass('ubermenu-responsive-toggle-open'); 1835 1836 if (plugin.settings.aria_responsive_toggle) { 1837 $allToggles.attr('aria-expanded', false); 1838 } 1839 } 1840 //plugin.$ubermenu.toggleClass( 'ubermenu-responsive-collapse' ); 1841 //$toggle.trigger( 'ubermenutoggled' ); 1842 1843 if (plugin.transitions && !plugin.$ubermenu.hasClass('ubermenu-responsive-nocollapse')) { 1844 plugin.$ubermenu.addClass('ubermenu-in-transition'); 1845 plugin.$ubermenu.on(plugin.transitionend + '_toggleubermenu', function () { 1846 plugin.$ubermenu.removeClass('ubermenu-in-transition'); 1847 plugin.$ubermenu.off(plugin.transitionend + '_toggleubermenu'); 1848 }); 1849 } 1850 }, 1851 1852 trapModalFocus: function ($toggle) { 1853 var plugin = this; 1854 var focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; 1855 var all = this.$ubermenu.find(focusableElements); 1856 this.firstFocus = all.first()[0]; 1857 this.lastFocus = all.last()[0]; 1858 1859 var checkFocus = this.checkModalFocusTab.bind(this); 1860 document.addEventListener('keydown', checkFocus); 1861 setTimeout(function () { 1862 plugin.firstFocus.focus(); 1863 }, 100); 1864 plugin.$ubermenu.on('ubermenutoggledclose', function () { 1865 document.removeEventListener('keydown', checkFocus); 1866 this.firstFocus = false; 1867 this.lastFocus = false; 1868 // Return focus to toggle 1869 $toggle[0].focus(); 1870 }); 1871 }, 1872 checkModalFocusTab: function (e) { 1873 if (e.key === 'Tab' || e.keyCode === 9) { 1874 if (e.shiftKey) { 1875 if (document.activeElement === this.firstFocus) { 1876 this.lastFocus.focus(); 1877 console.log('focus force to last'); 1878 e.preventDefault(); 1879 } 1880 } 1881 else if (document.activeElement === this.lastFocus) { 1882 this.firstFocus.focus(); 1883 console.log('focus force to first'); 1884 e.preventDefault(); 1885 } 1886 } 1887 }, 1888 1889 1890 positionSubmenus: function () { 1891 1892 var plugin = this; 1893 if (plugin.orientation == 'h') { 1894 1895 // Position vertically when in flex mode 1896 if (plugin.vstretch) { 1897 // Set submenu vertical position if submenu is not bounded by menu bar or inner menu bar 1898 if ( 1899 plugin.$ubermenu.css('position') === 'static' && 1900 plugin.$ubermenu.find('> .ubermenu-nav').css('position') === 'static' && 1901 !plugin.isMobile() 1902 ) { 1903 1904 // Does the main bar have a border we need to account for? 1905 var borderProp = plugin.inverted ? 'border-top-width' : 'border-bottom-width'; 1906 var addBorder = plugin.$ubermenu.css(borderProp) !== '0px'; 1907 1908 plugin.$ubermenu.find('.ubermenu-item-level-0 > .ubermenu-submenu-drop').each(function () { 1909 if (plugin.inverted) { 1910 // bottom:auto is overridden by top:auto, so we have to translate the submenu instead 1911 $(this).css({ 'transform': 'translateY(-100%)', 'bottom': 'auto', 'marginTop': addBorder ? '-1px' : 0 }); 1912 } 1913 else { 1914 var itemHeight = $(this).parent().outerHeight(); 1915 if (addBorder) itemHeight++; // Shift an extra pixel 1916 $(this).css({ 'top': 'auto', 'marginTop': itemHeight }); 1917 } 1918 }); 1919 } 1920 // Remove top positioning otherwise 1921 else { 1922 if (plugin.inverted) { 1923 plugin.$ubermenu.find('.ubermenu-item-level-0 > .ubermenu-submenu-drop').css({ 'transform': '', 'marginTop': '', 'bottom': '' }); 1924 } 1925 else { 1926 plugin.$ubermenu.find('.ubermenu-item-level-0 > .ubermenu-submenu-drop').css({ 'marginTop': '', 'top': '' }); 1927 } 1928 } 1929 } 1930 1931 // Center submenus 1932 plugin.$ubermenu.find('.ubermenu-submenu-drop.ubermenu-submenu-align-center').each(function () { 1933 1934 var $parent = $(this).parent('.ubermenu-item'); 1935 var $sub = $(this); 1936 var $container; 1937 if (plugin.$ubermenu.hasClass('ubermenu-bound')) { 1938 $container = $parent.closest('.ubermenu , .ubermenu-submenu'); // main menu bar 1939 } 1940 else if (plugin.$ubermenu.hasClass('ubermenu-bound-inner')) { 1941 $container = $parent.closest('.ubermenu-nav , .ubermenu-submenu'); // inner menu bar 1942 } 1943 else { 1944 var $parent_sub = $parent.closest('.ubermenu-submenu'); 1945 1946 //Find the closest relatively positioned element 1947 if ($parent_sub.length === 0) { 1948 $container = plugin.$ubermenu.offsetParent(); 1949 if (!$container) $container = $('body'); //If no offset parent, assume bounds of body tag 1950 } 1951 else { 1952 $container = $parent_sub; 1953 } 1954 } 1955 1956 var sub_width = $sub.outerWidth(); 1957 1958 var parent_width = $parent.outerWidth(); 1959 var parent_left_edge = $parent.offset().left; 1960 1961 var container_width = $container.width(); 1962 var container_left_edge = $container.offset().left; 1963 1964 // console.log( $container ); 1965 // console.log( 'parent_width: ' + parent_width ); 1966 // console.log( 'parent_left: ' + parent_left_edge ); 1967 // console.log( 'container_width: ' + container_width ); 1968 // console.log( 'container_left: ' + container_left_edge ); 1969 1970 1971 var center_left = (parent_left_edge + (parent_width / 2)) - (container_left_edge + (sub_width / 2)); 1972 1973 //If submenu is left of container edge, align left 1974 var left = center_left > 0 ? center_left : 0; 1975 1976 //If submenu width is larger than container width, center to container (menu bar) 1977 if (sub_width > container_width) { 1978 left = (sub_width - container_width) / -2; 1979 } 1980 //If submenu is right of container edge, align right 1981 else if (left + sub_width > container_width) { 1982 //left = container_width - sub_width; 1983 $sub.css({ 'right': 0, 'left': 'auto' }); 1984 left = false;
1985 } 1986 1987 if (left !== false) { 1988 $sub.css('left', left); 1989 } 1990 }); 1991 } 1992 }, 1993 1994 1995 openSubmenu: function ($li, tag, plugin) { 1996 1997 plugin = plugin || this; 1998 1999 plugin.log('Open Submenu ' + tag); 2000 2001 if (!$li.hasClass('ubermenu-active')) { 2002 $li.addClass('ubermenu-active'); 2003 2004 //ARIA 2005 if (plugin.settings.aria_expanded) { 2006 $li.find('>.ubermenu-target').attr('aria-expanded', 'true'); 2007 } 2008 if (plugin.settings.aria_hidden) { 2009 $li.find('>.ubermenu-submenu').attr('aria-hidden', 'false'); 2010 } 2011 2012 if (plugin.transitions) { 2013 $li.addClass('ubermenu-in-transition'); 2014 2015 $li.find('> .ubermenu-submenu').on(plugin.transitionend + '_opensubmenu', function () { 2016 plugin.log('finished submenu open transition'); 2017 $li.removeClass('ubermenu-in-transition'); 2018 $(this).off(plugin.transitionend + '_opensubmenu'); 2019 }); 2020 } 2021 $li.trigger('ubermenuopen'); 2022 } 2023 }, 2024 2025 closeSubmenu: function ($li, tag, plugin) { 2026 2027 plugin = plugin || this; 2028 2029 plugin.log('closeSubmenu ' + $li.find('>a').text() + ' [' + tag + ']'); 2030 2031 //If this menu is currently active and has a submenu, close it 2032 if ($li.hasClass('ubermenu-item-has-children') && $li.hasClass('ubermenu-active')) { 2033 2034 if (plugin.transitions) { 2035 $li.addClass('ubermenu-in-transition'); //transition class keeps visual flag until transition completes 2036 } 2037 2038 $li.each(function () { 2039 var _$li = $(this); 2040 var _$ul = _$li.find('> .ubermenu-submenu'); 2041 2042 //Remove the transition flag once the transition is completed 2043 if (plugin.transitions) { 2044 _$ul.on(plugin.transitionend + '_closesubmenu', function () { 2045 plugin.log('finished submenu close transition'); 2046 _$li.removeClass('ubermenu-in-transition'); 2047 _$ul.off(plugin.transitionend + '_closesubmenu'); 2048 }); 2049 } 2050 }); 2051 } 2052 2053 //Actually remove the active class, which causes the submenu to close 2054 $li.removeClass('ubermenu-active'); 2055 $li.trigger('ubermenuclose'); 2056 2057 //ARIA 2058 if (plugin.settings.aria_expanded) { 2059 $li.find('>.ubermenu-target').attr('aria-expanded', 'false'); 2060 } 2061 if (plugin.settings.aria_hidden) { 2062 $li.find('>.ubermenu-submenu').attr('aria-hidden', 'true'); 2063 } 2064 2065 }, 2066 2067 //Close subs without transition 2068 closeSubmenuInstantly: function ($li) { 2069 if ($li.length === 0) return; 2070 var plugin = this; 2071 $li.addClass('ubermenu-notransition'); 2072 $li.removeClass('ubermenu-active').removeClass('ubermenu-in-transition'); 2073 $li[0].offsetHeight; //triggers reflow 2074 $li.removeClass('ubermenu-notransition'); 2075 $li.trigger('ubermenuclose'); 2076 2077 //ARIA 2078 if (plugin.settings.aria_expanded) { 2079 $li.find('>.ubermenu-target,>.ubermenu-submenu').attr('aria-expanded', 'false'); 2080 } 2081 if (plugin.settings.aria_hidden) { 2082 $li.find('>.ubermenu-submenu').attr('aria-hidden', 'true'); 2083 } 2084 }, 2085 2086 //Top level subs only 2087 closeAllSubmenus: function () { 2088 //$( this.element ).find( 'li.menu-item-has-children' ).removeClass( 'ubermenu-active' ); 2089 var $actives = this.$ubermenu.find('.ubermenu-item-level-0.ubermenu-active'); 2090 if ($actives.length) this.closeSubmenuInstantly($actives); 2091 return $actives.length; 2092 }, 2093 2094 resetHandlers: function ($target, tag, plugin) { 2095 plugin.log('ResetHandlers: ' + tag); 2096 $target.off(this.touchEnd); 2097 $target.off(this.touchMove); 2098 2099 var $item = $target.parent(); 2100 $item.off('mousemove.mouse_intent'); 2101 $item.off('mouseleave.mouse_intent_none'); 2102 $item.data('mouse_intent_timer', clearTimeout($item.data('mouse_intent_timer'))); 2103 $item.data('mouse_intent_state', 0); 2104 }, 2105 2106 isMobile: function () { 2107 return window.innerWidth <= this.settings.breakpoint; 2108 }, 2109 isTablet: function () { 2110 return window.innerWidth <= this.settings.breakpoint && window.innerWidth > 480; 2111 }, 2112 //Currently in accordion mode? 2113 isAccordion: function () { 2114 var plugin = this; 2115 return plugin.mobileAccordion && 2116 ( 2117 window.innerWidth <= 480 // mobile single column 2118 || 2119 plugin.isTablet() && plugin.$ubermenu.hasClass('ubermenu-responsive-single-column') // tablet single column 2120 ) 2121 2122 }, 2123 isAccordionFoldingSingle: function () { 2124 return this.mobileAccordionFolding === 'single'; 2125 }, 2126 2127 //hover or press 2128 setInteractionMode: function (mode) { 2129 // If it changed 2130 if (this.interactionMode !== mode) { 2131 this.$ubermenu 2132 .removeClass('ubermenu-interaction-' + this.interactionMode) 2133 .addClass('ubermenu-interaction-' + mode); 2134 this.interactionMode = mode; 2135 } 2136 }, 2137 2138 2139 log: function (content, o, plugin) { 2140 plugin = plugin || this; 2141 2142 if (plugin.settings.debug) { 2143 if (plugin.settings.debug_onscreen) { 2144 this.debug_target.prepend('<div class="um-debug-content">' + content + '</div>'); 2145 } 2146 else console.log(content, o); 2147 } 2148 } 2149 }; 2150 2151 /* 2152 $.fn[ pluginName ] = function ( options ) { 2153 return this.each(function() { 2154 if ( !$.data( this, "plugin_" + pluginName ) ) { 2155 $.data( this, "plugin_" + pluginName, new Plugin( this, options ) ); 2156 } 2157 }); 2158 }; 2159 */ 2160 2161 $.fn[pluginName] = function (options) { 2162 2163 var args = arguments; 2164 2165 if (options === undefined || typeof options === 'object') { 2166 return this.each(function () { 2167 if (!$.data(this, "plugin_" + pluginName)) { 2168 $.data(this, "plugin_" + pluginName, new Plugin(this, options)); 2169 } 2170 }); 2171 } 2172 else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 2173 // Cache the method call to make it possible to return a value 2174 var returns; 2175 2176 this.each(function () { 2177 var instance = $.data(this, 'plugin_' + pluginName); 2178 2179 // Tests that there's already a plugin-instance and checks that the requested public method exists 2180 if (instance instanceof Plugin && typeof instance[options] === 'function') { 2181 2182 // Call the method of our plugin instance, and pass it the supplied arguments. 2183 returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1)); 2184 } 2185 2186 // Allow instances to be destroyed via the 'destroy' method 2187 if (options === 'destroy') { 2188 $.data(this, 'plugin_' + pluginName, null); 2189 } 2190 }); 2191 2192 // If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability. 2193 return returns !== undefined ? returns : this; 2194 } 2195 }; 2196 2197})(jQuery, window, document); 2198 2199 2200 2201(function ($) { 2202 2203 var ubermenu_is_initialized = false;
2204 2205 //jQuery( document ).ready( function($){ 2206 jQuery(function ($) { 2207 initialize_ubermenu('document.ready'); 2208 }); 2209 2210 //Backup 2211 $(window).on('load', function () { 2212 initialize_ubermenu('window.load'); 2213 }); 2214 2215 function initialize_ubermenu(init_point) { 2216 2217 if (ubermenu_is_initialized) return; 2218 2219 ubermenu_is_initialized = true; 2220 2221 // if( ( typeof console != "undefined" ) && init_point == 'window.load' ) console.log( 'Notice: UberMenu initialized via ' + init_point + '. This indicates that an unrelated error on the site prevented it from loading via the normal document ready event.' ); 2222 2223 //Scroll to non-ID "hashes" 2224 if (window.location.hash.substring(1, 2) == '.') { 2225 var $scrollTarget = $('body').find(window.location.hash.substring(1)); 2226 if ($scrollTarget.length) { 2227 window.scrollTo(0, $scrollTarget.offset().top - ubermenu_data.scrollto_offset); 2228 $('.ubermenu').find('.ubermenu-target[data-ubermenu-scrolltarget="' + window.location.hash.substring(1) + '"]').parent().addClass('ubermenu-current-menu-item'); 2229 } 2230 } 2231 //Make sure offset is applied to normal hashes 2232 else if (window.location.hash.length) { 2233 setTimeout(function () { 2234 try { 2235 var $scrollTarget = $('body').find(window.location.hash); 2236 if ($scrollTarget.length) { 2237 window.scrollTo(0, $scrollTarget.offset().top - ubermenu_data.scrollto_offset); 2238 $('.ubermenu').find('.ubermenu-target[data-ubermenu-scrolltarget="' + window.location.hash + '"]').parent().addClass('ubermenu-current-menu-item'); 2239 } 2240 } 2241 catch (error) { 2242 //not an element identifier, don't scroll 2243 } 2244 }, 100); 2245 } 2246 2247 2248 //Clean out empty items left by unbalanced dynamic items 2249 $('.ubermenu-item:empty').each(function () { //find all empty items 2250 var $p = $(this).parent(); //get parent UL 2251 $(this).remove(); //remove the empty item 2252 if ($p.find('.ubermenu-item').length == 0) { //if this submenu is now empty (no items) 2253 //remove submenu classes, remove submenu 2254 $p.parent().removeClass('ubermenu-has-submenu-drop').removeClass('ubermenu-has-submenu-flyout').off().find('.ubermenu-target > .ubermenu-sub-indicator').remove(); 2255 $p.remove(); 2256 } 2257 }); 2258 $('.ubermenu-submenu:empty').each(function () { 2259 var $p = $(this).parent('li'); 2260 $p.removeClass('ubermenu-has-submenu-drop'); 2261 $p.find('.ubermenu-sub-indicator').remove(); 2262 }); 2263 2264 2265 $('#wp-admin-bar-ubermenu_loading').remove(); 2266 2267 $('.ubermenu').ubermenu({ 2268 //touchOffClose: false 2269 //debug: true, 2270 //debug_onscreen: true, 2271 //mouseEvents: false 2272 //clicktest: true 2273 }).trigger('ubermenuinit'); 2274 2275 2276 //Search Autofocus 2277 $('.ubermenu-submenu .ubermenu-search-input-autofocus').closest('.ubermenu-has-submenu-drop').on('ubermenuopen', function () { 2278 var $input = $(this).find('.ubermenu-submenu .ubermenu-search-input'); 2279 if ($input.length) { 2280 setTimeout(function () { 2281 $input[0].focus(); 2282 }, 250); 2283 } 2284 }); 2285 2286 //Resizing detection - add class to body while resizing, 2287 // and trigger events when we cross the breakpoint 2288 var resizeTimer; 2289 var lastWidth = window.innerWidth; 2290 var curWidth = window.innerWidth; 2291 var breakpoint = ubermenu_data.hasOwnProperty('responsive_breakpoint') ? parseInt(ubermenu_data.responsive_breakpoint) : 959; 2292 2293 window.addEventListener('resize', function () { 2294 2295 // Add class while resizing 2296 document.body.classList.add('um-window-resizing'); 2297 clearTimeout(resizeTimer); 2298 resizeTimer = setTimeout(function () { 2299 document.body.classList.remove('um-window-resizing'); 2300 }, 400); 2301 2302 // Trigger custom resize even when crossing breakpoint 2303 curWidth = window.innerWidth; 2304 if (lastWidth <= breakpoint && curWidth > breakpoint) { 2305 // console.log( 'resized to desktop' ); 2306 $(window).trigger('ubermenu-resize-desktop'); 2307 } 2308 else if (lastWidth > breakpoint && curWidth <= breakpoint) { 2309 // console.log( 'resized to mobile' ); 2310 $(window).trigger('ubermenu-resize-mobile'); 2311 } 2312 lastWidth = curWidth; 2313 }); 2314 2315 //Google Maps 2316 if ( 2317 typeof google !== 'undefined' && 2318 typeof google.maps !== 'undefined' && 2319 typeof google.maps.LatLng !== 'undefined') { 2320 $('.ubermenu-map-canvas').each(function () { 2321 2322 var $canvas = $(this); 2323 var dataZoom = $canvas.attr('data-zoom') ? parseInt($canvas.attr('data-zoom')) : 8; 2324 2325 var latlng = $canvas.attr('data-lat') ? 2326 new google.maps.LatLng($canvas.attr('data-lat'), $canvas.attr('data-lng')) : 2327 new google.maps.LatLng(40.7143528, -74.0059731); 2328 2329 var myOptions = { 2330 zoom: dataZoom, 2331 mapTypeId: google.maps.MapTypeId.ROADMAP, 2332 center: latlng 2333 }; 2334 2335 var map = new google.maps.Map(this, myOptions); 2336 2337 if ($canvas.attr('data-address')) { 2338 var geocoder = new google.maps.Geocoder(); 2339 geocoder.geocode({ 2340 'address': $canvas.attr('data-address') 2341 }, 2342 function (results, status) { 2343 if (status == google.maps.GeocoderStatus.OK) { 2344 map.setCenter(results[0].geometry.location); 2345 latlng = results[0].geometry.location; 2346 var marker = new google.maps.Marker({ 2347 map: map, 2348 position: results[0].geometry.location, 2349 title: $canvas.attr('data-mapTitle') 2350 }); 2351 } 2352 }); 2353 } 2354 else { 2355 //place marker for regular lat/long 2356 var marker = new google.maps.Marker({ 2357 map: map, 2358 position: latlng, 2359 title: $canvas.attr('data-mapTitle') 2360 }); 2361 } 2362 2363 var $li = $(this).closest('.ubermenu-has-submenu-drop'); 2364 var mapHandler = function () { 2365 google.maps.event.trigger(map, "resize"); 2366 map.setCenter(latlng); 2367 map.setZoom(dataZoom); 2368 //Only resize the first time we open 2369 $li.off('ubermenuopen', mapHandler); 2370 }; 2371 $li.on('ubermenuopen', mapHandler); 2372 }); 2373 } 2374 } 2375 2376})(jQuery); 2377 2378 2379/* 2380 * Deprecated API Functions 2381 */ 2382//jQuery( '.ubermenu' ).ubermenu( 'openSubmenu' , jQuery( '#menu-item-401' ) ); 2383function uberMenu_openMega(id) { 2384 jQuery('.ubermenu').ubermenu('openSubmenu', jQuery(id)); 2385}
2386function uberMenu_openFlyout(id) { 2387 jQuery('.ubermenu').ubermenu('openSubmenu', jQuery(id)); 2388} 2389function uberMenu_close(id) { 2390 jQuery('.ubermenu').ubermenu('closeSubmenu', jQuery(id)); 2391} 2392function uberMenu_redrawSubmenus() { 2393 jQuery('.ubermenu').ubermenu('positionSubmenus'); 2394}
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.