PageSourceSearch

https://www.holtags.com/wp-content/plugins/ubermenu/assets/js/ubermenu.js?ver=3.8.5

js holtags.com collected 2026-10-08 10:10:43 UTC 80,857 bytes, 2,431 lines download raw bytes

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

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.