PageSourceSearch

https://bentarrow.ca/wp-content/plugins/ubermenu/assets/js/ubermenu.js?ver=3.8.1

js bentarrow.ca collected 2026-09-28 08:08:50 UTC 79,759 bytes, 2,394 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_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.