PageSourceSearch

https://www.osteriailgranaio.it/wp-content/plugins/powerpack-eleme…/smartmenu/jquery-smartmenu.js?ver=1.0.1

js osteriailgranaio.it collected 2026-09-26 18:40:48 UTC 45,303 bytes, 1,223 lines download raw bytes

1/*!
2 * SmartMenus jQuery Plugin - v1.0.1 - November 1, 2016
3 * http://www.smartmenus.org/
4 *
5 * Copyright Vasil Dinkov, Vadikom Web Ltd.
6 * http://vadikom.com
7 *
8 * Licensed MIT
9 */
10
11(function (factory) {
12	if (typeof define === 'function' && define.amd) {
13		// AMD
14		define(['jquery'], factory);
15	} else if (typeof module === 'object' && typeof module.exports === 'object') {
16		// CommonJS
17		module.exports = factory(require('jquery'));
18	} else {
19		// Global jQuery
20		factory(jQuery);
21	}
22}(function ($) {
23
24	var menuTrees = [],
25		IE = !!window.createPopup, // detect it for the iframe shim
26		mouse = false, // optimize for touch by default - we will detect for mouse input
27		touchEvents = 'ontouchstart' in window, // we use this just to choose between toucn and pointer events, not for touch screen detection
28		mouseDetectionEnabled = false,
29		requestAnimationFrame = window.requestAnimationFrame || function (callback) { return setTimeout(callback, 1000 / 60); },
30		cancelAnimationFrame = window.cancelAnimationFrame || function (id) { clearTimeout(id); };
31
32	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
33	function initMouseDetection(disable) {
34		var eNS = '.smartmenus_mouse';
35		if (!mouseDetectionEnabled && !disable) {
36			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
37			// in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
38			var firstTime = true,
39				lastMove = null;
40			$(document).bind(getEventsNS([
41				['mousemove', function (e) {
42					var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
43					if (lastMove) {
44						var deltaX = Math.abs(lastMove.x - thisMove.x),
45							deltaY = Math.abs(lastMove.y - thisMove.y);
46						if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
47							mouse = true;
48							// if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
49							if (firstTime) {
50								var $a = $(e.target).closest('a');
51								if ($a.is('a')) {
52									$.each(menuTrees, function () {
53										if ($.contains(this.$root[0], $a[0])) {
54											this.itemEnter({ currentTarget: $a[0] });
55											return false;
56										}
57									});
58								}
59								firstTime = false;
60							}
61						}
62					}
63					lastMove = thisMove;
64				}],
65				[touchEvents ? 'touchstart' : 'pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut', function (e) {
66					if (isTouchEvent(e.originalEvent)) {
67						mouse = false;
68					}
69				}]
70			], eNS));
71			mouseDetectionEnabled = true;
72		} else if (mouseDetectionEnabled && disable) {
73			$(document).unbind(eNS);
74			mouseDetectionEnabled = false;
75		}
76	}
77
78	function isTouchEvent(e) {
79		return !/^(4|mouse)$/.test(e.pointerType);
80	}
81
82	// returns a jQuery bind() ready object
83	function getEventsNS(defArr, eNS) {
84		if (!eNS) {
85			eNS = '';
86		}
87		var obj = {};
88		$.each(defArr, function (index, value) {
89			obj[value[0].split(' ').join(eNS + ' ') + eNS] = value[1];
90		});
91		return obj;
92	}
93
94	$.SmartMenus = function (elm, options) {
95		this.$root = $(elm);
96		this.opts = options;
97		this.rootId = ''; // internal
98		this.accessIdPrefix = '';
99		this.$subArrow = null;
100		this.activatedItems = []; // stores last activated A's for each level
101		this.visibleSubMenus = []; // stores visible sub menus UL's (might be in no particular order)
102		this.showTimeout = 0;
103		this.hideTimeout = 0;
104		this.scrollTimeout = 0;
105		this.clickActivated = false;
106		this.focusActivated = false;
107		this.zIndexInc = 0;
108		this.idInc = 0;
109		this.$firstLink = null; // we'll use these for some tests
110		this.$firstSub = null; // at runtime so we'll cache them
111		this.disabled = false;
112		this.$disableOverlay = null;
113		this.$touchScrollingSub = null;
114		this.cssTransforms3d = 'perspective' in elm.style || 'webkitPerspective' in elm.style;
115		this.wasCollapsible = false;
116		this.init();
117	};
118
119	$.extend($.SmartMenus, {
120		hideAll: function () {
121			$.each(menuTrees, function () {
122				this.menuHideAll();
123			});
124		},
125		destroy: function () {
126			while (menuTrees.length) {
127				menuTrees[0].destroy();
128			}
129			initMouseDetection(true);
130		},
131		prototype: {
132			init: function (refresh) {
133				var self = this;
134
135				if (!refresh) {
136					menuTrees.push(this);
137
138					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
139					this.accessIdPrefix = 'sm-' + this.rootId + '-';
140
141					if (this.$root.hasClass('sm-rtl')) {
142						this.opts.rightToLeftSubMenus = true;
143					}
144
145					// init root (main menu)
146					var eNS = '.smartmenus';
147					this.$root
148						.data('smartmenus', this)
149						.attr('data-smartmenus-id', this.rootId)
150						.dataSM('level', 1)
151						.bind(getEventsNS([
152							['mouseover focusin', $.proxy(this.rootOver, this)],
153							['mouseout focusout', $.proxy(this.rootOut, this)],
154							['keydown', $.proxy(this.rootKeyDown, this)]
155						], eNS))
156						.delegate('a', getEventsNS([
157							['mouseenter', $.proxy(this.itemEnter, this)],
158							['mouseleave', $.proxy(this.itemLeave, this)],
159							['mousedown', $.proxy(this.itemDown, this)],
160							['focus', $.proxy(this.itemFocus, this)],
161							['blur', $.proxy(this.itemBlur, this)],
162							['click', $.proxy(this.itemClick, this)]
163						], eNS));
164
165					// hide menus on tap or click outside the root UL
166					eNS += this.rootId;
167					if (this.opts.hideOnClick) {
168						$(document).bind(getEventsNS([
169							['touchstart', $.proxy(this.docTouchStart, this)],
170							['touchmove', $.proxy(this.docTouchMove, this)],
171							['touchend', $.proxy(this.docTouchEnd, this)],
172							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
173							['click', $.proxy(this.docClick, this)]
174						], eNS));
175					}
176					// hide sub menus on resize
177					$(window).bind(getEventsNS([['resize orientationchange', $.proxy(this.winResize, this)]], eNS));
178
179					if (this.opts.subIndicators) {
180						this.$subArrow = $('<span/>').addClass('sub-arrow');
181						if (this.opts.subIndicatorsText) {
182							this.$subArrow.html(this.opts.subIndicatorsText);
183						}
184					}
185
186					// make sure mouse detection is enabled
187					initMouseDetection();
188				}
189
190				// init sub menus
191				this.$firstSub = this.$root.find('ul').each(function () { self.menuInit($(this)); }).eq(0);
192
193				this.$firstLink = this.$root.find('a').eq(0);
194
195				// find current item
196				if (this.opts.markCurrentItem) {
197					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
198						reHash = /#.*/,
199						locHref = window.location.href.replace(reDefaultDoc, ''),
200						locHrefNoHash = locHref.replace(reHash, '');
201					this.$root.find('a').each(function () {
202						var href = this.href.replace(reDefaultDoc, ''),
203							$this = $(this);
204						if (href == locHref || href == locHrefNoHash) {
205							$this.addClass('current');
206							if (self.opts.markCurrentTree) {
207								$this.parentsUntil('[data-smartmenus-id]', 'ul').each(function () {
208									$(this).dataSM('parent-a').addClass('current');
209								});
210							}
211						}
212					});
213				}
214
215				// save initial state
216				this.wasCollapsible = this.isCollapsible();
217			},
218			destroy: function (refresh) {
219				if (!refresh) {
220					var eNS = '.smartmenus';
221					this.$root
222						.removeData('smartmenus')
223						.removeAttr('data-smartmenus-id')
224						.removeDataSM('level')
225						.unbind(eNS)
226						.undelegate(eNS);
227					eNS += this.rootId;
228					$(document).unbind(eNS);
229					$(window).unbind(eNS);
230					if (this.opts.subIndicators) {
231						this.$subArrow = null;
232					}
233				}
234				this.menuHideAll();
235				var self = this;
236				this.$root.find('ul').each(function () {
237					var $this = $(this);
238					if ($this.dataSM('scroll-arrows')) {
239						$this.dataSM('scroll-arrows').remove();
240					}
241					if ($this.dataSM('shown-before')) {
242						if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
243							$this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
244						}
245						if ($this.dataSM('scroll-arrows')) {
246							$this.dataSM('scroll-arrows').remove();
247						}
248						$this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
249					}
250					if (($this.attr('id') || '').indexOf(self.accessIdPrefix) == 0) {
251						$this.removeAttr('id');
252					}
253				})
254					.removeDataSM('in-mega')
255					.removeDataSM('shown-before')
256					.removeDataSM('ie-shim')
257					.removeDataSM('scroll-arrows')
258					.removeDataSM('parent-a')
259					.removeDataSM('level')
260					.removeDataSM('beforefirstshowfired')
261					.removeAttr('role')
262					.removeAttr('aria-hidden')
263					.removeAttr('aria-labelledby')
264					.removeAttr('aria-expanded');
265				this.$root.find('a.has-submenu').each(function () {
266					var $this = $(this);
267					if ($this.attr('id').indexOf(self.accessIdPrefix) == 0) {
268						$this.removeAttr('id');
269					}
270				})
271					.removeClass('has-submenu')
272					.removeDataSM('sub')
273					.removeAttr('aria-haspopup')
274					.removeAttr('aria-controls')
275					.removeAttr('aria-expanded')
276					.closest('li').removeDataSM('sub');
277				if (this.opts.subIndicators) {
278					this.$root.find('span.sub-arrow').remove();
279				}
280				if (this.opts.markCurrentItem) {
281					this.$root.find('a.current').removeClass('current');
282				}
283				if (!refresh) {
284					this.$root = null;
285					this.$firstLink = null;
286					this.$firstSub = null;
287					if (this.$disableOverlay) {
288						this.$disableOverlay.remove();
289						this.$disableOverlay = null;
290					}
291					menuTrees.splice($.inArray(this, menuTrees), 1);
292				}
293			},
294			disable: function (noOverlay) {
295				if (!this.disabled) {
296					this.menuHideAll();
297					// display overlay over the menu to prevent interaction
298					if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
299						var pos = this.$root.offset();
300						this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
301							position: 'absolute',
302							top: pos.top,
303							left: pos.left,
304							width: this.$root.outerWidth(),
305							height: this.$root.outerHeight(),
306							zIndex: this.getStartZIndex(true),
307							opacity: 0
308						}).appendTo(document.body);
309					}
310					this.disabled = true;
311				}
312			},
313			docClick: function (e) {
314				if (this.$touchScrollingSub) {
315					this.$touchScrollingSub = null;
316					return;
317				}
318				// hide on any click outside the menu or on a menu link
319				if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
320					this.menuHideAll();
321				}
322			},
323			docTouchEnd: function (e) {
324				if (!this.lastTouch) {
325					return;
326				}
327				if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
328					if (this.hideTimeout) {
329						clearTimeout(this.hideTimeout);
330						this.hideTimeout = 0;
331					}
332					// hide with a delay to prevent triggering accidental unwanted click on some page element
333					var self = this;
334					this.hideTimeout = setTimeout(function () { self.menuHideAll(); }, 350);
335				}
336				this.lastTouch = null;
337			},
338			docTouchMove: function (e) {
339				if (!this.lastTouch) {
340					return;
341				}
342				var touchPoint = e.originalEvent.touches[0];
343				this.lastTouch.x2 = touchPoint.pageX;
344				this.lastTouch.y2 = touchPoint.pageY;
345			},
346			docTouchStart: function (e) {
347				var touchPoint = e.originalEvent.touches[0];
348				this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
349			},
350			enable: function () {
351				if (this.disabled) {
352					if (this.$disableOverlay) {
353						this.$disableOverlay.remove();
354						this.$disableOverlay = null;
355					}
356					this.disabled = false;
357				}
358			},
359			getClosestMenu: function (elm) {
360				var $closestMenu = $(elm).closest('ul');
361				while ($closestMenu.dataSM('in-mega')) {
362					$closestMenu = $closestMenu.parent().closest('ul');
363				}
364				return $closestMenu[0] || null;
365			},
366			getHeight: function ($elm) {
367				return this.getOffset($elm, true);
368			},
369			// returns precise width/height float values
370			getOffset: function ($elm, height) {
371				var old;
372				if ($elm.css('display') == 'none') {
373					old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
374					$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
375				}
376				var box = $elm[0].getBoundingClientRect && $elm[0].getBoundingClientRect(),
377					val = box && (height ? box.height || box.bottom - box.top : box.width || box.right - box.left);
378				if (!val && val !== 0) {
379					val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
380				}
381				if (old) {
382					$elm.hide().css(old);
383				}
384				return val;
385			},
386			getStartZIndex: function (root) {
387				var zIndex = parseInt(this[root ? '$root' : '$firstSub'].css('z-index'));
388				if (!root && isNaN(zIndex)) {
389					zIndex = parseInt(this.$root.css('z-index'));
390				}
391				return !isNaN(zIndex) ? zIndex : 1;
392			},
393			getTouchPoint: function (e) {
394				return e.touches && e.touches[0] || e.changedTouches && e.changedTouches[0] || e;
395			},
396			getViewport: function (height) {
397				var name = height ? 'Height' : 'Width',
398					val = document.documentElement['client' + name],
399					val2 = window['inner' + name];
400				if (val2) {
401					val = Math.min(val, val2);
402				}
403				return val;
404			},
405			getViewportHeight: function () {
406				return this.getViewport(true);
407			},
408			getViewportWidth: function () {
409				return this.getViewport();
410			},
411			getWidth: function ($elm) {
412				return this.getOffset($elm);
413			},
414			handleEvents: function () {
415				return !this.disabled && this.isCSSOn();
416			},
417			handleItemEvents: function ($a) {
418				return this.handleEvents() && !this.isLinkInMegaMenu($a);
419			},
420			isCollapsible: function () {
421				return this.$firstSub.css('position') == 'static';
422			},
423			isCSSOn: function () {
424				return this.$firstLink.css('display') == 'block';
425			},
426			isFixed: function () {
427				var isFixed = this.$root.css('position') == 'fixed';
428				if (!isFixed) {
429					this.$root.parentsUntil('body').each(function () {
430						if ($(this).css('position') == 'fixed') {
431							isFixed = true;
432							return false;
433						}
434					});
435				}
436				return isFixed;
437			},
438			isLinkInMegaMenu: function ($a) {
439				return $(this.getClosestMenu($a[0])).hasClass('mega-menu');
440			},
441			isTouchMode: function () {
442				return !mouse || this.opts.noMouseOver || this.isCollapsible();
443			},
444			itemActivate: function ($a, focus) {
445				var $ul = $a.closest('ul'),
446					level = $ul.dataSM('level');
447				// if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
448				if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
449					var self = this;
450					$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function () {
451						self.itemActivate($(this).dataSM('parent-a'));
452					});
453				}
454				// hide any visible deeper level sub menus
455				if (!this.isCollapsible() || focus) {
456					this.menuHideSubMenus(!this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level);
457				}
458				// save new active item for this level
459				this.activatedItems[level - 1] = $a;
460				if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
461					return;
462				}
463				// show the sub menu if this item has one
464				var $sub = $a.dataSM('sub');
465				if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
466					this.menuShow($sub);
467				}
468			},
469			itemBlur: function (e) {
470				var $a = $(e.currentTarget);
471				if (!this.handleItemEvents($a)) {
472					return;
473				}
474				this.$root.triggerHandler('blur.smapi', $a[0]);
475			},
476			itemClick: function (e) {
477				var $a = $(e.currentTarget);
478				if (!this.handleItemEvents($a)) {
479					return;
480				}
481				if (this.$touchScrollingSub && this.$touchScrollingSub[0] == $a.closest('ul')[0]) {
482					this.$touchScrollingSub = null;
483					e.stopPropagation();
484					return false;
485				}
486				if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
487					return false;
488				}
489				var subArrowClicked = $(e.target).is('span.sub-arrow'),
490					$sub = $a.dataSM('sub'),
491					firstLevelSub = $sub ? $sub.dataSM('level') == 2 : false;
492				// if the sub is not visible
493				if ($sub && !$sub.is(':visible')) {
494					if (this.opts.showOnClick && firstLevelSub) {
495						this.clickActivated = true;
496					}
497					// try to activate the item and show the sub
498					this.itemActivate($a);
499					// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
500					// if it couldn't show it, then the sub menus are disabled with an !important declaration (e.g. via mobile styles) so let the link get loaded
501					if ($sub.is(':visible')) {
502						this.focusActivated = true;
503						return false;
504					}
505				} else if (this.isCollapsible() && subArrowClicked) {
506					this.itemActivate($a);
507					this.menuHide($sub);
508					return false;
509				}
510				if (this.opts.showOnClick && firstLevelSub || $a.hasClass('disabled') || this.$root.triggerHandler('select.smapi', $a[0]) === false) {
511					return false;
512				}
513			},
514			itemDown: function (e) {
515				var $a = $(e.currentTarget);
516				if (!this.handleItemEvents($a)) {
517					return;
518				}
519				$a.dataSM('mousedown', true);
520			},
521			itemEnter: function (e) {
522				var $a = $(e.currentTarget);
523				if (!this.handleItemEvents($a)) {
524					return;
525				}
526				if (!this.isTouchMode()) {
527					if (this.showTimeout) {
528						clearTimeout(this.showTimeout);
529						this.showTimeout = 0;
530					}
531					var self = this;
532					this.showTimeout = setTimeout(function () { self.itemActivate($a); }, this.opts.showOnClick && $a.closest('ul').dataSM('level') == 1 ? 1 : this.opts.showTimeout);
533				}
534				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
535			},
536			itemFocus: function (e) {
537				var $a = $(e.currentTarget);
538				if (!this.handleItemEvents($a)) {
539					return;
540				}
541				// fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
542				if (this.focusActivated && (!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
543					this.itemActivate($a, true);
544				}
545				this.$root.triggerHandler('focus.smapi', $a[0]);
546			},
547			itemLeave: function (e) {
548				var $a = $(e.currentTarget);
549				if (!this.handleItemEvents($a)) {
550					return;
551				}
552				if (!this.isTouchMode()) {
553					$a[0].blur();
554					if (this.showTimeout) {
555						clearTimeout(this.showTimeout);
556						this.showTimeout = 0;
557					}
558				}
559				$a.removeDataSM('mousedown');
560				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
561			},
562			menuHide: function ($sub) {
563				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
564					return;
565				}
566				$sub.stop(true, true);
567				if ($sub.css('display') != 'none') {
568					var complete = function () {
569						// unset z-index
570						$sub.css('z-index', '');
571					};
572					// if sub is collapsible (mobile view)
573					if (this.isCollapsible()) {
574						if (this.opts.collapsibleHideFunction) {
575							this.opts.collapsibleHideFunction.call(this, $sub, complete);
576						} else {
577							$sub.hide(this.opts.collapsibleHideDuration, complete);
578						}
579					} else {
580						if (this.opts.hideFunction) {
581							this.opts.hideFunction.call(this, $sub, complete);
582						} else {
583							$sub.hide(this.opts.hideDuration, complete);
584						}
585					}
586					// remove IE iframe shim
587					if ($sub.dataSM('ie-shim')) {
588						$sub.dataSM('ie-shim').remove().css({ '-webkit-transform': '', transform: '' });
589					}
590					// deactivate scrolling if it is activated for this sub
591					if ($sub.dataSM('scroll')) {
592						this.menuScrollStop($sub);
593						$sub.css({ 'touch-action': '', '-ms-touch-action': '', '-webkit-transform': '', transform: '' })
594							.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
595					}
596					// unhighlight parent item + accessibility
597					$sub.dataSM('parent-a').removeClass('highlighted').attr('aria-expanded', 'false');
598					$sub.attr({
599						'aria-expanded': 'false',
600						'aria-hidden': 'true'
601					});
602					var level = $sub.dataSM('level');
603					this.activatedItems.splice(level - 1, 1);
604					this.visibleSubMenus.splice($.inArray($sub, this.visibleSubMenus), 1);
605					this.$root.triggerHandler('hide.smapi', $sub[0]);
606				}
607			},
608			menuHideAll: function () {
609				if (this.showTimeout) {
610					clearTimeout(this.showTimeout);
611					this.showTimeout = 0;
612				}
613				// hide all subs
614				// if it's a popup, this.visibleSubMenus[0] is the root UL
615				var level = this.opts.isPopup ? 1 : 0;
616				for (var i = this.visibleSubMenus.length - 1; i >= level; i--) {
617					this.menuHide(this.visibleSubMenus[i]);
618				}
619				// hide root if it's popup
620				if (this.opts.isPopup) {
621					this.$root.stop(true, true);
622					if (this.$root.is(':visible')) {
623						if (this.opts.hideFunction) {
624							this.opts.hideFunction.call(this, this.$root);
625						} else {
626							this.$root.hide(this.opts.hideDuration);
627						}
628						// remove IE iframe shim
629						if (this.$root.dataSM('ie-shim')) {
630							this.$root.dataSM('ie-shim').remove();
631						}
632					}
633				}
634				this.activatedItems = [];
635				this.visibleSubMenus = [];
636				this.clickActivated = false;
637				this.focusActivated = false;
638				// reset z-index increment
639				this.zIndexInc = 0;
640				this.$root.triggerHandler('hideAll.smapi');
641			},
642			menuHideSubMenus: function (level) {
643				for (var i = this.activatedItems.length - 1; i >= level; i--) {
644					var $sub = this.activatedItems[i].dataSM('sub');
645					if ($sub) {
646						this.menuHide($sub);
647					}
648				}
649			},
650			menuIframeShim: function ($ul) {
651				// create iframe shim for the menu
652				if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
653					$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
654						.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
655					);
656				}
657			},
658			menuInit: function ($ul) {
659				if (!$ul.dataSM('in-mega')) {
660					// mark UL's in mega drop downs (if any) so we can neglect them
661					if ($ul.hasClass('mega-menu')) {
662						$ul.find('ul').dataSM('in-mega', true);
663					}
664					// get level (much faster than, for example, using parentsUntil)
665					var level = 2,
666						par = $ul[0];
667					while ((par = par.parentNode.parentNode) != this.$root[0]) {
668						level++;
669					}
670					// cache stuff for quick access
671					var $a = $ul.prevAll('a').eq(-1);
672					// if the link is nested (e.g. in a heading)
673					if (!$a.length) {
674						$a = $ul.prevAll().find('a').eq(-1);
675					}
676					$a.addClass('has-submenu').dataSM('sub', $ul);
677					$ul.dataSM('parent-a', $a)
678						.dataSM('level', level)
679						.parent().dataSM('sub', $ul);
680					// accessibility
681					var aId = $a.attr('id') || this.accessIdPrefix + (++this.idInc),
682						ulId = $ul.attr('id') || this.accessIdPrefix + (++this.idInc);
683					$a.attr({
684						id: aId,
685						'aria-haspopup': 'true',
686						'aria-controls': ulId,
687						'aria-expanded': 'false'
688					});
689					$ul.attr({
690						id: ulId,
691						'role': 'group',
692						'aria-hidden': 'true',
693						'aria-labelledby': aId,
694						'aria-expanded': 'false'
695					});
696					// add sub indicator to parent item
697					if (this.opts.subIndicators) {
698						$a[this.opts.subIndicatorsPos](this.$subArrow.clone());
699					}
700				}
701			},
702			menuPosition: function ($sub) {
703				var $a = $sub.dataSM('parent-a'),
704					$li = $a.closest('li'),
705					$ul = $li.parent(),
706					level = $sub.dataSM('level'),
707					subW = this.getWidth($sub),
708					subH = this.getHeight($sub),
709					itemOffset = $a.offset(),
710					itemX = itemOffset.left,
711					itemY = itemOffset.top,
712					itemW = this.getWidth($a),
713					itemH = this.getHeight($a),
714					$win = $(window),
715					winX = $win.scrollLeft(),
716					winY = $win.scrollTop(),
717					winW = this.getViewportWidth(),
718					winH = this.getViewportHeight(),
719					horizontalParent = $ul.parent().is('[data-sm-horizontal-sub]') || level == 2 && !$ul.hasClass('sm-vertical'),
720					rightToLeft = this.opts.rightToLeftSubMenus && !$li.is('[data-sm-reverse]') || !this.opts.rightToLeftSubMenus && $li.is('[data-sm-reverse]'),
721					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
722					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
723					x, y;
724				if (horizontalParent) {
725					x = rightToLeft ? itemW - subW - subOffsetX : subOffsetX;
726					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
727				} else {
728					x = rightToLeft ? subOffsetX - subW : itemW - subOffsetX;
729					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
730				}
731				if (this.opts.keepInViewport) {
732					var absX = itemX + x,
733						absY = itemY + y;
734					if (rightToLeft && absX < winX) {
735						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
736					} else if (!rightToLeft && absX + subW > winX + winW) {
737						x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
738					}
739					if (!horizontalParent) {
740						if (subH < winH && absY + subH > winY + winH) {
741							y += winY + winH - subH - absY;
742						} else if (subH >= winH || absY < winY) {
743							y += winY - absY;
744						}
745					}
746					// do we need scrolling?
747					// 0.49 used for better precision when dealing with float values
748					if (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49) {
749						var self = this;
750						if (!$sub.dataSM('scroll-arrows')) {
751							$sub.dataSM('scroll-arrows', $([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0], $('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]])
752								.bind({
753									mouseenter: function () {
754										$sub.dataSM('scroll').up = $(this).hasClass('scroll-up');
755										self.menuScroll($sub);
756									},
757									mouseleave: function (e) {
758										self.menuScrollStop($sub);
759										self.menuScrollOut($sub, e);
760									},
761									'mousewheel DOMMouseScroll': function (e) { e.preventDefault(); }
762								})
763								.insertAfter($sub)
764							);
765						}
766						// bind scroll events and save scroll data for this sub
767						var eNS = '.smartmenus_scroll';
768						$sub.dataSM('scroll', {
769							y: this.cssTransforms3d ? 0 : y - itemH,
770							step: 1,
771							// cache stuff for faster recalcs later
772							itemH: itemH,
773							subH: subH,
774							arrowDownH: this.getHeight($sub.dataSM('scroll-arrows').eq(1))
775						})
776							.bind(getEventsNS([
777								['mouseover', function (e) { self.menuScrollOver($sub, e); }],
778								['mouseout', function (e) { self.menuScrollOut($sub, e); }],
779								['mousewheel DOMMouseScroll', function (e) { self.menuScrollMousewheel($sub, e); }]
780							], eNS))
781							.dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: subW - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: $sub.css('z-index') })
782							.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
783						// when a menu tree is fixed positioned we allow scrolling via touch too
784						// since there is no other way to access such long sub menus if no mouse is present
785						if (this.isFixed()) {
786							$sub.css({ 'touch-action': 'none', '-ms-touch-action': 'none' })
787								.bind(getEventsNS([
788									[touchEvents ? 'touchstart touchmove touchend' : 'pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp', function (e) {
789										self.menuScrollTouch($sub, e);
790									}]
791								], eNS));
792						}
793					}
794				}
795				$sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
796				// IE iframe shim
797				this.menuIframeShim($sub);
798				if ($sub.dataSM('ie-shim')) {
799					$sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH });
800				}
801			},
802			menuScroll: function ($sub, once, step) {
803				var data = $sub.dataSM('scroll'),
804					$arrows = $sub.dataSM('scroll-arrows'),
805					end = data.up ? data.upEnd : data.downEnd,
806					diff;
807				if (!once && data.momentum) {
808					data.momentum *= 0.92;
809					diff = data.momentum;
810					if (diff < 0.5) {
811						this.menuScrollStop($sub);
812						return;
813					}
814				} else {
815					diff = step || (once || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor(data.step));
816				}
817				// hide any visible deeper level sub menus
818				var level = $sub.dataSM('level');
819				if (this.activatedItems[level - 1] && this.activatedItems[level - 1].dataSM('sub') && this.activatedItems[level - 1].dataSM('sub').is(':visible')) {
820					this.menuHideSubMenus(level - 1);
821				}
822				data.y = data.up && end <= data.y || !data.up && end >= data.y ? data.y : (Math.abs(end - data.y) > diff ? data.y + (data.up ? diff : -diff) : end);
823				$sub.add($sub.dataSM('ie-shim')).css(this.cssTransforms3d ? { '-webkit-transform': 'translate3d(0, ' + data.y + 'px, 0)', transform: 'translate3d(0, ' + data.y + 'px, 0)' } : { marginTop: data.y });
824				// show opposite arrow if appropriate
825				if (mouse && (data.up && data.y > data.downEnd || !data.up && data.y < data.upEnd)) {
826					$arrows.eq(data.up ? 1 : 0).show();
827				}
828				// if we've reached the end
829				if (data.y == end) {
830					if (mouse) {
831						$arrows.eq(data.up ? 0 : 1).hide();
832					}
833					this.menuScrollStop($sub);
834				} else if (!once) {
835					if (this.opts.scrollAccelerate && data.step < this.opts.scrollStep) {
836						data.step += 0.2;
837					}
838					var self = this;
839					this.scrollTimeout = requestAnimationFrame(function () { self.menuScroll($sub); });
840				}
841			},
842			menuScrollMousewheel: function ($sub, e) {
843				if (this.getClosestMenu(e.target) == $sub[0]) {
844					e = e.originalEvent;
845					var up = (e.wheelDelta || -e.detail) > 0;
846					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
847						$sub.dataSM('scroll').up = up;
848						this.menuScroll($sub, true);
849					}
850				}
851				e.preventDefault();
852			},
853			menuScrollOut: function ($sub, e) {
854				if (mouse) {
855					if (!/^scroll-(up|down)/.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || this.getClosestMenu(e.relatedTarget) != $sub[0])) {
856						$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
857					}
858				}
859			},
860			menuScrollOver: function ($sub, e) {
861				if (mouse) {
862					if (!/^scroll-(up|down)/.test(e.target.className) && this.getClosestMenu(e.target) == $sub[0]) {
863						this.menuScrollRefreshData($sub);
864						var data = $sub.dataSM('scroll'),
865							upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
866						$sub.dataSM('scroll-arrows').eq(0).css('margin-top', upEnd).end()
867							.eq(1).css('margin-top', upEnd + this.getViewportHeight() - data.arrowDownH).end()
868							.css('visibility', 'visible');
869					}
870				}
871			},
872			menuScrollRefreshData: function ($sub) {
873				var data = $sub.dataSM('scroll'),
874					upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
875				if (this.cssTransforms3d) {
876					upEnd = -(parseFloat($sub.css('margin-top')) - upEnd);
877				}
878				$.extend(data, {
879					upEnd: upEnd,
880					downEnd: upEnd + this.getViewportHeight() - data.subH
881				});
882			},
883			menuScrollStop: function ($sub) {
884				if (this.scrollTimeout) {
885					cancelAnimationFrame(this.scrollTimeout);
886					this.scrollTimeout = 0;
887					$sub.dataSM('scroll').step = 1;
888					return true;
889				}
890			},
891			menuScrollTouch: function ($sub, e) {
892				e = e.originalEvent;
893				if (isTouchEvent(e)) {
894					var touchPoint = this.getTouchPoint(e);
895					// neglect event if we touched a visible deeper level sub menu
896					if (this.getClosestMenu(touchPoint.target) == $sub[0]) {
897						var data = $sub.dataSM('scroll');
898						if (/(start|down)$/i.test(e.type)) {
899							if (this.menuScrollStop($sub)) {
900								// if we were scrolling, just stop and don't activate any link on the first touch
901								e.preventDefault();
902								this.$touchScrollingSub = $sub;
903							} else {
904								this.$touchScrollingSub = null;
905							}
906							// update scroll data since the user might have zoomed, etc.
907							this.menuScrollRefreshData($sub);
908							// extend it with the touch properties
909							$.extend(data, {
910								touchStartY: touchPoint.pageY,
911								touchStartTime: e.timeStamp
912							});
913						} else if (/move$/i.test(e.type)) {
914							var prevY = data.touchY !== undefined ? data.touchY : data.touchStartY;
915							if (prevY !== undefined && prevY != touchPoint.pageY) {
916								this.$touchScrollingSub = $sub;
917								var up = prevY < touchPoint.pageY;
918								// changed direction? reset...
919								if (data.up !== undefined && data.up != up) {
920									$.extend(data, {
921										touchStartY: touchPoint.pageY,
922										touchStartTime: e.timeStamp
923									});
924								}
925								$.extend(data, {
926									up: up,
927									touchY: touchPoint.pageY
928								});
929								this.menuScroll($sub, true, Math.abs(touchPoint.pageY - prevY));
930							}
931							e.preventDefault();
932						} else { // touchend/pointerup
933							if (data.touchY !== undefined) {
934								if (data.momentum = Math.pow(Math.abs(touchPoint.pageY - data.touchStartY) / (e.timeStamp - data.touchStartTime), 2) * 15) {
935									this.menuScrollStop($sub);
936									this.menuScroll($sub);
937									e.preventDefault();
938								}
939								delete data.touchY;
940							}
941						}
942					}
943				}
944			},
945			menuShow: function ($sub) {
946				if (!$sub.dataSM('beforefirstshowfired')) {
947					$sub.dataSM('beforefirstshowfired', true);
948					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
949						return;
950					}
951				}
952				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
953					return;
954				}
955				$sub.dataSM('shown-before', true)
956					.stop(true, true);
957				if (!$sub.is(':visible')) {
958					// highlight parent item
959					var $a = $sub.dataSM('parent-a');
960					if (this.opts.keepHighlighted || this.isCollapsible()) {
961						$a.addClass('highlighted');
962					}
963					if (this.isCollapsible()) {
964						$sub.removeClass('sm-nowrap').css({ zIndex: '', width: 'auto', minWidth: '', maxWidth: '', top: '', left: '', marginLeft: '', marginTop: '' });
965					} else {
966						// set z-index
967						$sub.css('z-index', this.zIndexInc = (this.zIndexInc || this.get
967StartZIndex()) + 1);
968						// min/max-width fix - no way to rely purely on CSS as all UL's are nested
969						if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
970							$sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
971							if (this.opts.subMenusMinWidth) {
972								$sub.css('min-width', this.opts.subMenusMinWidth);
973							}
974							if (this.opts.subMenusMaxWidth) {
975								var noMaxWidth = this.getWidth($sub);
976								$sub.css('max-width', this.opts.subMenusMaxWidth);
977								if (noMaxWidth > this.getWidth($sub)) {
978									$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
979								}
980							}
981						}
982						this.menuPosition($sub);
983						// insert IE iframe shim
984						if ($sub.dataSM('ie-shim')) {
985							$sub.dataSM('ie-shim').insertBefore($sub);
986						}
987					}
988					var complete = function () {
989						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
990						$sub.css('overflow', '');
991					};
992					// if sub is collapsible (mobile view)
993					if (this.isCollapsible()) {
994						if (this.opts.collapsibleShowFunction) {
995							this.opts.collapsibleShowFunction.call(this, $sub, complete);
996						} else {
997							$sub.show(this.opts.collapsibleShowDuration, complete);
998						}
999					} else {
1000						if (this.opts.showFunction) {
1001							this.opts.showFunction.call(this, $sub, complete);
1002						} else {
1003							$sub.show(this.opts.showDuration, complete);
1004						}
1005					}
1006					// accessibility
1007					$a.attr('aria-expanded', 'true');
1008					$sub.attr({
1009						'aria-expanded': 'true',
1010						'aria-hidden': 'false'
1011					});
1012					// store sub menu in visible array
1013					this.visibleSubMenus.push($sub);
1014					this.$root.triggerHandler('show.smapi', $sub[0]);
1015				}
1016			},
1017			popupHide: function (noHideTimeout) {
1018				if (this.hideTimeout) {
1019					clearTimeout(this.hideTimeout);
1020					this.hideTimeout = 0;
1021				}
1022				var self = this;
1023				this.hideTimeout = setTimeout(function () {
1024					self.menuHideAll();
1025				}, noHideTimeout ? 1 : this.opts.hideTimeout);
1026			},
1027			popupShow: function (left, top) {
1028				if (!this.opts.isPopup) {
1029					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
1030					return;
1031				}
1032				if (this.hideTimeout) {
1033					clearTimeout(this.hideTimeout);
1034					this.hideTimeout = 0;
1035				}
1036				this.$root.dataSM('shown-before', true)
1037					.stop(true, true);
1038				if (!this.$root.is(':visible')) {
1039					this.$root.css({ left: left, top: top });
1040					// IE iframe shim
1041					this.menuIframeShim(this.$root);
1042					if (this.$root.dataSM('ie-shim')) {
1043						this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left: left, top: top }).insertBefore(this.$root);
1044					}
1045					// show menu
1046					var self = this,
1047						complete = function () {
1048							self.$root.css('overflow', '');
1049						};
1050					if (this.opts.showFunction) {
1051						this.opts.showFunction.call(this, this.$root, complete);
1052					} else {
1053						this.$root.show(this.opts.showDuration, complete);
1054					}
1055					this.visibleSubMenus[0] = this.$root;
1056				}
1057			},
1058			refresh: function () {
1059				this.destroy(true);
1060				this.init(true);
1061			},
1062			rootKeyDown: function (e) {
1063				if (!this.handleEvents()) {
1064					return;
1065				}
1066				switch (e.keyCode) {
1067					case 27: // reset on Esc
1068						var $activeTopItem = this.activatedItems[0];
1069						if ($activeTopItem) {
1070							this.menuHideAll();
1071							$activeTopItem[0].focus();
1072							var $sub = $activeTopItem.dataSM('sub');
1073							if ($sub) {
1074								this.menuHide($sub);
1075							}
1076						}
1077						break;
1078					case 32: // activate item's sub on Space
1079						var $target = $(e.target);
1080						if ($target.is('a') && this.handleItemEvents($target)) {
1081							var $sub = $target.dataSM('sub');
1082							if ($sub && !$sub.is(':visible')) {
1083								this.itemClick({ currentTarget: e.target });
1084								e.preventDefault();
1085							}
1086						}
1087						break;
1088				}
1089			},
1090			rootOut: function (e) {
1091				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1092					return;
1093				}
1094				if (this.hideTimeout) {
1095					clearTimeout(this.hideTimeout);
1096					this.hideTimeout = 0;
1097				}
1098				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
1099					var self = this;
1100					this.hideTimeout = setTimeout(function () { self.menuHideAll(); }, this.opts.hideTimeout);
1101				}
1102			},
1103			rootOver: function (e) {
1104				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1105					return;
1106				}
1107				if (this.hideTimeout) {
1108					clearTimeout(this.hideTimeout);
1109					this.hideTimeout = 0;
1110				}
1111			},
1112			winResize: function (e) {
1113				if (!this.handleEvents()) {
1114					// we still need to resize the disable overlay if it's visible
1115					if (this.$disableOverlay) {
1116						var pos = this.$root.offset();
1117						this.$disableOverlay.css({
1118							top: pos.top,
1119							left: pos.left,
1120							width: this.$root.outerWidth(),
1121							height: this.$root.outerHeight()
1122						});
1123					}
1124					return;
1125				}
1126				// hide sub menus on resize - on mobile do it only on orientation change
1127				if (!('onorientationchange' in window) || e.type == 'orientationchange') {
1128					var isCollapsible = this.isCollapsible();
1129					// if it was collapsible before resize and still is, don't do it
1130					if (!(this.wasCollapsible && isCollapsible)) {
1131						if (this.activatedItems.length) {
1132							this.activatedItems[this.activatedItems.length - 1][0].blur();
1133						}
1134						this.menuHideAll();
1135					}
1136					this.wasCollapsible = isCollapsible;
1137				}
1138			}
1139		}
1140	});
1141
1142	$.fn.dataSM = function (key, val) {
1143		if (val) {
1144			return this.data(key + '_smartmenus', val);
1145		}
1146		return this.data(key + '_smartmenus');
1147	};
1148
1149	$.fn.removeDataSM = function (key) {
1150		return this.removeData(key + '_smartmenus');
1151	};
1152
1153	$.fn.smartmenus = function (options) {
1154		if (typeof options == 'string') {
1155			var args = arguments,
1156				method = options;
1157			Array.prototype.shift.call(args);
1158			return this.each(function () {
1159				var smartmenus = $(this).data('smartmenus');
1160				if (smartmenus && smartmenus[method]) {
1161					smartmenus[method].apply(smartmenus, args);
1162				}
1163			});
1164		}
1165		// [data-sm-options] attribute on the root UL
1166		var dataOpts = this.data('sm-options') || null;
1167		if (dataOpts) {
1168			try {
1169				dataOpts = eval('(' + dataOpts + ')');
1170			} catch (e) {
1171				dataOpts = null;
1172				alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.');
1173			};
1174		}
1175		return this.each(function () {
1176			new $.SmartMenus(this, $.extend({}, $.fn.smartmenus.defaults, options, dataOpts));
1177		});
1178	};
1179
1180	// default settings
1181	$.fn.smartmenus.defaults = {
1182		isPopup: false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
1183		mainMenuSubOffsetX: 0,		// pixels offset from default position
1184		mainMenuSubOffsetY: 0,		// pixels offset from default position
1185		subMenusSubOffsetX: 0,		// pixels offset from default position
1186		subMenusSubOffsetY: 0,		// pixels offset from default position
1187		subMenusMinWidth: '10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1188		subMenusMaxWidth: '20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1189		subIndicators: true,		// create sub menu indicators - creates a SPAN and inserts it in the A
1190		subIndicatorsPos: 'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
1191		subIndicatorsText: '+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
1192		scrollStep: 30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
1193		scrollAccelerate: true,		// accelerate scrolling or use a fixed step
1194		showTimeout: 250,		// timeout before showing the sub menus
1195		hideTimeout: 500,		// timeout before hiding the sub menus
1196		showDuration: 0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
1197		showFunction: null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
1198		// don't forget to call complete() at the end of whatever you do
1199		// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
1200		hideDuration: 0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
1201		hideFunction: function ($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
1202		// don't forget to call complete() at the end of whatever you do
1203		// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
1204		collapsibleShowDuration: 0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
1205		collapsibleShowFunction: function ($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
1206		// (i.e. when mobile styles are used to make the sub menus collapsible)
1207		collapsibleHideDuration: 0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
1208		collapsibleHideFunction: function ($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
1209		// (i.e. when mobile styles are used to make the sub menus collapsible)
1210		showOnClick: false,		// show the first-level sub menus onclick instead of onmouseover (i.e. mimic desktop app menus) (matters only for mouse input)
1211		hideOnClick: true,		// hide the sub menus on click/tap anywhere on the page
1212		noMouseOver: false,		// disable sub menus activation onmouseover (i.e. behave like in touch mode - use just mouse clicks) (matters only for mouse input)
1213		keepInViewport: true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
1214		keepHighlighted: true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
1215		markCurrentItem: false,		// automatically add the 'current' class to the A element of the item linking to the current URL
1216		markCurrentTree: true,		// add the 'current' class also to the A elements of all ancestor items of the current item
1217		rightToLeftSubMenus: false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
1218		bottomToTopSubMenus: false,		// bottom to top display of the sub menus
1219		overlapControlsInIE: true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
1220	};
1221
1222	return $;
1223}));

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.