PageSourceSearch

https://mairie-lesbordes.fr/wp-content/plugins/unlimited-elementor…/assets/js/jquery.smartmenus.js?ver=1.3.4

js mairie-lesbordes.fr collected 2026-10-02 22:58:58 UTC 45,999 bytes, 1,226 lines download raw bytes

1/*
2 * SmartMenus jQuery v1.2.0
3 * http://www.smartmenus.org/
4 *
5 * Copyright Vasil Dinkov, Vadikom Web Ltd.
6 * http://vadikom.com/
7 *
8 * Released under the MIT license:
9 * http://www.opensource.org/licenses/MIT
10 */
11
12(function(factory) {
13	if (typeof define === 'function' && define.amd) {
14		// AMD
15		define(['jquery'], factory);
16	} else if (typeof module === 'object' && typeof module.exports === 'object') {
17		// CommonJS
18		module.exports = factory(require('jquery'));
19	} else {
20		// Global jQuery
21		factory(jQuery);
22	}
23} (function($) {
24
25	var menuTrees = [],
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		canAnimate = !!$.fn.animate;
32
33	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
34	function initMouseDetection(disable) {
35		var eNS = '.smartmenus_mouse';
36		if (!mouseDetectionEnabled && !disable) {
37			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
38			// 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)
39			var firstTime = true,
40				lastMove = null,
41				events = {
42					'mousemove': function(e) {
43						var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
44						if (lastMove) {
45							var deltaX = Math.abs(lastMove.x - thisMove.x),
46								deltaY = Math.abs(lastMove.y - thisMove.y);
47		 					if ((deltaX > 0 || deltaY > 0) && deltaX <= 4 && deltaY <= 4 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
48								mouse = true;
49								// 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
50								if (firstTime) {
51									var $a = $(e.target).closest('a');
52									if ($a.is('a')) {
53										$.each(menuTrees, function() {
54											if ($.contains(this.$root[0], $a[0])) {
55												this.itemEnter({ currentTarget: $a[0] });
56												return false;
57											}
58										});
59									}
60									firstTime = false;
61								}
62							}
63						}
64						lastMove = thisMove;
65					}
66				};
67			events[touchEvents ? 'touchstart' : 'pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut'] = function(e) {
68				if (isTouchEvent(e.originalEvent)) {
69					mouse = false;
70				}
71			};
72			$(document).on(getEventsNS(events, eNS));
73			mouseDetectionEnabled = true;
74		} else if (mouseDetectionEnabled && disable) {
75			$(document).off(eNS);
76			mouseDetectionEnabled = false;
77		}
78	}
79
80	function isTouchEvent(e) {
81		return !/^(4|mouse)$/.test(e.pointerType);
82	}
83
84	// returns a jQuery on() ready object
85	function getEventsNS(events, eNS) {
86		if (!eNS) {
87			eNS = '';
88		}
89		var eventsNS = {};
90		for (var i in events) {
91			eventsNS[i.split(' ').join(eNS + ' ') + eNS] = events[i];
92		}
93		return eventsNS;
94	}
95
96	$.SmartMenus = function(elm, options) {
97		this.$root = $(elm);
98		this.opts = options;
99		this.rootId = ''; // internal
100		this.accessIdPrefix = '';
101		this.$subArrow = null;
102		this.activatedItems = []; // stores last activated A's for each level
103		this.visibleSubMenus = []; // stores visible sub menus UL's (might be in no particular order)
104		this.showTimeout = 0;
105		this.hideTimeout = 0;
106		this.scrollTimeout = 0;
107		this.clickActivated = false;
108		this.focusActivated = false;
109		this.zIndexInc = 0;
110		this.idInc = 0;
111		this.$firstLink = null; // we'll use these for some tests
112		this.$firstSub = null; // at runtime so we'll cache them
113		this.disabled = false;
114		this.$disableOverlay = null;
115		this.$touchScrollingSub = null;
116		this.cssTransforms3d = 'perspective' in elm.style || 'webkitPerspective' in elm.style;
117		this.wasCollapsible = false;
118		this.init();
119	};
120
121	$.extend($.SmartMenus, {
122		hideAll: function() {
123			$.each(menuTrees, function() {
124				this.menuHideAll();
125			});
126		},
127		destroy: function() {
128			while (menuTrees.length) {
129				menuTrees[0].destroy();
130			}
131			initMouseDetection(true);
132		},
133		prototype: {
134			init: function(refresh) {
135				var self = this;
136
137				if (!refresh) {
138					menuTrees.push(this);
139
140					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
141					this.accessIdPrefix = 'sm-' + this.rootId + '-';
142
143					if (this.$root.hasClass('sm-rtl')) {
144						this.opts.rightToLeftSubMenus = true;
145					}
146
147					// init root (main menu)
148					var eNS = '.smartmenus';
149					this.$root
150						.data('smartmenus', this)
151						.attr('data-smartmenus-id', this.rootId)
152						.dataSM('level', 1)
153						.on(getEventsNS({
154							'mouseover focusin': $.proxy(this.rootOver, this),
155							'mouseout focusout': $.proxy(this.rootOut, this),
156							'keydown': $.proxy(this.rootKeyDown, this)
157						}, eNS))
158						.on(getEventsNS({
159							'mouseenter': $.proxy(this.itemEnter, this),
160							'mouseleave': $.proxy(this.itemLeave, this),
161							'mousedown': $.proxy(this.itemDown, this),
162							'focus': $.proxy(this.itemFocus, this),
163							'blur': $.proxy(this.itemBlur, this),
164							'click': $.proxy(this.itemClick, this)
165						}, eNS), 'a');
166
167					// hide menus on tap or click outside the root UL
168					eNS += this.rootId;
169					if (this.opts.hideOnClick) {
170						$(document).on(getEventsNS({
171							'touchstart': $.proxy(this.docTouchStart, this),
172							'touchmove': $.proxy(this.docTouchMove, this),
173							'touchend': $.proxy(this.docTouchEnd, this),
174							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
175							'click': $.proxy(this.docClick, this)
176						}, eNS));
177					}
178					// hide sub menus on resize
179					$(window).on(getEventsNS({ 'resize orientationchange': $.proxy(this.winResize, this) }, eNS));
180
181					if (this.opts.subIndicators) {
182						this.$subArrow = $('<span/>').addClass('pea-sub-arrow-icon fa fa-caret-down');
183						if (this.opts.subIndicatorsText) {
184							this.$subArrow.html(this.opts.subIndicatorsText);
185						}
186					}
187
188					// make sure mouse detection is enabled
189					initMouseDetection();
190				}
191
192				// init sub menus
193				this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
194
195				this.$firstLink = this.$root.find('a').eq(0);
196
197				// find current item
198				if (this.opts.markCurrentItem) {
199					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
200						reHash = /#.*/,
201						locHref = window.location.href.replace(reDefaultDoc, ''),
202						locHrefNoHash = locHref.replace(reHash, '');
203					this.$root.find('a:not(.mega-menu a)').each(function() {
204						var href = this.href.replace(reDefaultDoc, ''),
205							$this = $(this);
206						if (href == locHref || href == locHrefNoHash) {
207							$this.addClass('current');
208							if (self.opts.markCurrentTree) {
209								$this.parentsUntil('[data-smartmenus-id]', 'ul').each(function() {
210									$(this).dataSM('parent-a').addClass('current');
211								});
212							}
213						}
214					});
215				}
216
217				// save initial state
218				this.wasCollapsible = this.isCollapsible();
219			},
220			destroy: function(refresh) {
221				if (!refresh) {
222					var eNS = '.smartmenus';
223					this.$root
224						.removeData('smartmenus')
225						.removeAttr('data-smartmenus-id')
226						.removeDataSM('level')
227						.off(eNS);
228					eNS += this.rootId;
229					$(document).off(eNS);
230					$(window).off(eNS);
231					if (this.opts.subIndicators) {
232						this.$subArrow = null;
233					}
234				}
235				this.menuHideAll();
236				var self = this;
237				this.$root.find('ul').each(function() {
238						var $this = $(this);
239						if ($this.dataSM('scroll-arrows')) {
240							$this.dataSM('scroll-arrows').remove();
241						}
242						if ($this.dataSM('shown-before')) {
243							if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
244								$this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
245							}
246							if ($this.dataSM('scroll-arrows')) {
247								$this.dataSM('scroll-arrows').remove();
248							}
249							$this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
250						}
251						if (($this.attr('id') || '').indexOf(self.accessIdPrefix) == 0) {
252							$this.removeAttr('id');
253						}
254					})
255					.removeDataSM('in-mega')
256					.removeDataSM('shown-before')
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.pea-sub-arrow-icon').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).closest('a').length) {
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') != 'inline';
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, hideDeeperSubs) {
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() || hideDeeperSubs) {
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 $sub = $a.dataSM('sub'),
490					firstLevelSub = $sub ? $sub.dataSM('level') == 2 : false;
491				if ($sub) {
492					var subArrowClicked = $(e.target).is('.span.pea-sub-arrow-icon'),
493						collapsible = this.isCollapsible(),
494						behaviorToggle = /toggle$/.test(this.opts.collapsibleBehavior),
495						behaviorLink = /link$/.test(this.opts.collapsibleBehavior),
496						behaviorAccordion = /^accordion/.test(this.opts.collapsibleBehavior);
497					// if the sub is hidden, try to show it
498					if (!$sub.is(':visible')) {
499						if (!behaviorLink || !collapsible || subArrowClicked) {
500							if (!collapsible && this.opts.showOnClick && firstLevelSub) {
501								this.clickActivated = true;
502							}
503							// try to activate the item and show the sub
504							this.itemActivate($a, behaviorAccordion);
505							// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
506							// 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
507							if ($sub.is(':visible')) {
508								this.focusActivated = true;
509								return false;
510							}
511						}
512					// if the sub is visible and showOnClick: true, hide the sub
513					} else if (!collapsible && this.opts.showOnClick && firstLevelSub) {
514						this.menuHide($sub);
515						this.clickActivated = false;
516						this.focusActivated = false;
517						return false;
518					// if the sub is visible and we are in collapsible mode
519					} else if (collapsible && (behaviorToggle || subArrowClicked)) {
520						this.itemActivate($a, behaviorAccordion);
521						this.menuHide($sub);
522						return false;
523					}
524				}
525				if (!collapsible && this.opts.showOnClick && firstLevelSub || $a.hasClass('disabled') || this.$root.triggerHandler('select.smapi', $a[0]) === false) {
526					return false;
527				}
528			},
529			itemDown: function(e) {
530				var $a = $(e.currentTarget);
531				if (!this.handleItemEvents($a)) {
532					return;
533				}
534				$a.dataSM('mousedown', true);
535			},
536			itemEnter: function(e) {
537				var $a = $(e.currentTarget);
538				if (!this.handleItemEvents($a)) {
539					return;
540				}
541				if (!this.isTouchMode()) {
542					if (this.showTimeout) {
543						clearTimeout(this.showTimeout);
544						this.showTimeout = 0;
545					}
546					var self = this;
547					this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.closest('ul').dataSM('level') == 1 ? 1 : this.opts.showTimeout);
548				}
549				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
550			},
551			itemFocus: function(e) {
552				var $a = $(e.currentTarget);
553				if (!this.handleItemEvents($a)) {
554					return;
555				}
556				// 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
557				if (this.focusActivated && (!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
558					this.itemActivate($a, true);
559				}
560				this.$root.triggerHandler('focus.smapi', $a[0]);
561			},
562			itemLeave: function(e) {
563				var $a = $(e.currentTarget);
564				if (!this.handleItemEvents($a)) {
565					return;
566				}
567				if (!this.isTouchMode()) {
568					$a[0].blur();
569					if (this.showTimeout) {
570						clearTimeout(this.showTimeout);
571						this.showTimeout = 0;
572					}
573				}
574				$a.removeDataSM('mousedown');
575				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
576			},
577			menuHide: function($sub) {
578				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
579					return;
580				}
581				if (canAnimate) {
582					$sub.stop(true, true);
583				}
584				if ($sub.css('display') != 'none') {
585					var complete = function() {
586						// unset z-index
587						$sub.css('z-index', '');
588					};
589					// if sub is collapsible (mobile view)
590					if (this.isCollapsible()) {
591						if (canAnimate && this.opts.collapsibleHideFunction) {
592							this.opts.collapsibleHideFunction.call(this, $sub, complete);
593						} else {
594							$sub.hide(this.opts.collapsibleHideDuration, complete);
595						}
596					} else {
597						if (canAnimate && this.opts.hideFunction) {
598							this.opts.hideFunction.call(this, $sub, complete);
599						} else {
600							$sub.hide(this.opts.hideDuration, complete);
601						}
602					}
603					// deactivate scrolling if it is activated for this sub
604					if ($sub.dataSM('scroll')) {
605						this.menuScrollStop($sub);
606						$sub.css({ 'touch-action': '', '-ms-touch-action': '', '-webkit-transform': '', transform: '' })
607							.off('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
608					}
609					// unhighlight parent item + accessibility
610					$sub.dataSM('parent-a').removeClass('highlighted').attr('aria-expanded', 'false');
611					$sub.attr({
612						'aria-expanded': 'false',
613						'aria-hidden': 'true'
614					});
615					var level = $sub.dataSM('level');
616					this.activatedItems.splice(level - 1, 1);
617					this.visibleSubMenus.splice($.inArray($sub, this.visibleSubMenus), 1);
618					this.$root.triggerHandler('hide.smapi', $sub[0]);
619				}
620			},
621			menuHideAll: function() {
622				if (this.showTimeout) {
623					clearTimeout(this.showTimeout);
624					this.showTimeout = 0;
625				}
626				// hide all subs
627				// if it's a popup, this.visibleSubMenus[0] is the root UL
628				var level = this.opts.isPopup ? 1 : 0;
629				for (var i = this.visibleSubMenus.length - 1; i >= level; i--) {
630					this.menuHide(this.visibleSubMenus[i]);
631				}
632				// hide root if it's popup
633				if (this.opts.isPopup) {
634					if (canAnimate) {
635						this.$root.stop(true, true);
636					}
637					if (this.$root.is(':visible')) {
638						if (canAnimate && this.opts.hideFunction) {
639							this.opts.hideFunction.call(this, this.$root);
640						} else {
641							this.$root.hide(this.opts.hideDuration);
642						}
643					}
644				}
645				this.activatedItems = [];
646				this.visibleSubMenus = [];
647				this.clickActivated = false;
648				this.focusActivated = false;
649				// reset z-index increment
650				this.zIndexInc = 0;
651				this.$root.triggerHandler('hideAll.smapi');
652			},
653			menuHideSubMenus: function(level) {
654				for (var i = this.activatedItems.length - 1; i >= level; i--) {
655					var $sub = this.activatedItems[i].dataSM('sub');
656					if ($sub) {
657						this.menuHide($sub);
658					}
659				}
660			},
661			menuInit: function($ul) {
662				if (!$ul.dataSM('in-mega')) {
663					// mark UL's in mega drop downs (if any) so we can neglect them
664					if ($ul.hasClass('mega-menu')) {
665						$ul.find('ul').dataSM('in-mega', true);
666					}
667					// get level (much faster than, for example, using parentsUntil)
668					var level = 2,
669						par = $ul[0];
670					while ((par = par.parentNode.parentNode) != this.$root[0]) {
671						level++;
672					}
673					// cache stuff for quick access
674					var $a = $ul.prevAll('a').eq(-1);
675					// if the link is nested (e.g. in a heading)
676					if (!$a.length) {
677						$a = $ul.prevAll().find('a').eq(-1);
678					}
679					$a.addClass('has-submenu').dataSM('sub', $ul);
680					$ul.dataSM('parent-a', $a)
681						.dataSM('level', level)
682						.parent().dataSM('sub', $ul);
683					// accessibility
684					var aId = $a.attr('id') || this.accessIdPrefix + (++this.idInc),
685						ulId = $ul.attr('id') || this.accessIdPrefix + (++this.idInc);
686					$a.attr({
687						id: aId,
688						'aria-haspopup': 'true',
689						'aria-controls': ulId,
690						'aria-expanded': 'false'
691					});
692					$ul.attr({
693						id: ulId,
694						'role': 'group',
695						'aria-hidden': 'true',
696						'aria-labelledby': aId,
697						'aria-expanded': 'false'
698					});
699					// add sub indicator to parent item
700					if (this.opts.subIndicators) {
701						$a[this.opts.subIndicatorsPos](this.$subArrow.clone());
702					}
703				}
704			},
705			menuPosition: function($sub) {
706				var $a = $sub.dataSM('parent-a'),
707					$li = $a.closest('li'),
708					$ul = $li.parent(),
709					level = $sub.dataSM('level'),
710					subW = this.getWidth($sub),
711					subH = this.getHeight($sub),
712					itemOffset = $a.offset(),
713					itemX = itemOffset.left,
714					itemY = itemOffset.top,
715					itemW = this.getWidth($a),
716					itemH = this.getHeight($a),
717					$win = $(window),
718					winX = $win.scrollLeft(),
719					winY = $win.scrollTop(),
720					winW = this.getViewportWidth(),
721					winH = this.getViewportHeight(),
722					horizontalParent = $ul.parent().is('[data-sm-horizontal-sub]') || level == 2 && !$ul.hasClass('sm-vertical'),
723					rightToLeft = this.opts.rightToLeftSubMenus && !$li.is('[data-sm-reverse]') || !this.opts.rightToLeftSubMenus && $li.is('[data-sm-reverse]'),
724					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
725					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
726					x, y;
727				if (horizontalParent) {
728					x = rightToLeft ? itemW - subW - subOffsetX : subOffsetX;
729					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
730				} else {
731					x = rightToLeft ? subOffsetX - subW : itemW - subOffsetX;
732					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
733				}
734				if (this.opts.keepInViewport) {
735					var absX = itemX + x,
736						absY = itemY + y;
737					if (rightToLeft && absX < winX) {
738						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
739					} else if (!rightToLeft && absX + subW > winX + winW) {
740						x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
741					}
742					if (!horizontalParent) {
743						if (subH < winH && absY + subH > winY + winH) {
744							y += winY + winH - subH - absY;
745						} else if (subH >= winH || absY < winY) {
746							y += winY - absY;
747						}
748					}
749					// do we need scrolling?
750					// 0.49 used for better precision when dealing with float values
751					if (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49) {
752						var self = this;
753						if (!$sub.dataSM('scroll-arrows')) {
754							$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]])
755								.on({
756									mouseenter: function() {
757										$sub.dataSM('scroll').up = $(this).hasClass('scroll-up');
758										self.menuScroll($sub);
759									},
760									mouseleave: function(e) {
761										self.menuScrollStop($sub);
762										self.menuScrollOut($sub, e);
763									},
764									'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
765								})
766								.insertAfter($sub)
767							);
768						}
769						// bind scroll events and save scroll data for this sub
770						var eNS = '.smartmenus_scroll';
771						$sub.dataSM('scroll', {
772								y: this.cssTransforms3d ? 0 : y - itemH,
773								step: 1,
774								// cache stuff for faster recalcs later
775								itemH: itemH,
776								subH: subH,
777								arrowDownH: this.getHeight($sub.dataSM('scroll-arrows').eq(1))
778							})
779							.on(getEventsNS({
780								'mouseover': function(e) { self.menuScrollOver($sub, e); },
781								'mouseout': function(e) { self.menuScrollOut($sub, e); },
782								'mousewheel DOMMouseScroll': function(e) { self.menuScrollMousewheel($sub, e); }
783							}, eNS))
784							.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') })
785								.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
786						// when a menu tree is fixed positioned we allow scrolling via touch too
787						// since there is no other way to access such long sub menus if no mouse is present
788						if (this.isFixed()) {
789							var events = {};
790							events[touchEvents ? 'touchstart touchmove touchend' : 'pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp'] = function(e) {
791								self.menuScrollTouch($sub, e);
792							};
793							$sub.css({ 'touch-action': 'none', '-ms-touch-action': 'none' }).on(getEventsNS(events, eNS));
794						}
795					}
796				}
797				$sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
798			},
799			menuScroll: function($sub, once, step) {
800				var data = $sub.dataSM('scroll'),
801					$arrows = $sub.dataSM('scroll-arrows'),
802					end = data.up ? data.upEnd : data.downEnd,
803					diff;
804				if (!once && data.momentum) {
805					data.momentum *= 0.92;
806					diff = data.momentum;
807					if (diff < 0.5) {
808						this.menuScrollStop($sub);
809						return;
810					}
811				} else {
812					diff = step || (once || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor(data.step));
813				}
814				// hide any visible deeper level sub menus
815				var level = $sub.dataSM('level');
816				if (this.activatedItems[level - 1] && this.activatedItems[level - 1].dataSM('sub') && this.activatedItems[level - 1].dataSM('sub').is(':visible')) {
817					this.menuHideSubMenus(level - 1);
818				}
819				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);
820				$sub.css(this.cssTransforms3d ? { '-webkit-transform': 'translate3d(0, ' + data.y + 'px, 0)', transform: 'translate3d(0, ' + data.y + 'px, 0)' } : { marginTop: data.y });
821				// show opposite arrow if appropriate
822				if (mouse && (data.up && data.y > data.downEnd || !data.up && data.y < data.upEnd)) {
823					$arrows.eq(data.up ? 1 : 0).show();
824				}
825				// if we've reached the end
826				if (data.y == end) {
827					if (mouse) {
828						$arrows.eq(data.up ? 0 : 1).hide();
829					}
830					this.menuScrollStop($sub);
831				} else if (!once) {
832					if (this.opts.scrollAccelerate && data.step < this.opts.scrollStep) {
833						data.step += 0.2;
834					}
835					var self = this;
836					this.scrollTimeout = requestAnimationFrame(function() { self.menuScroll($sub); });
837				}
838			},
839			menuScrollMousewheel: function($sub, e) {
840				if (this.getClosestMenu(e.target) == $sub[0]) {
841					e = e.originalEvent;
842					var up = (e.wheelDelta || -e.detail) > 0;
843					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
844						$sub.dataSM('scroll').up = up;
845						this.menuScroll($sub, true);
846					}
847				}
848				e.preventDefault();
849			},
850			menuScrollOut: function($sub, e) {
851				if (mouse) {
852					if (!/^scroll-(up|down)/.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || this.getClosestMenu(e.relatedTarget) != $sub[0])) {
853						$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
854					}
855				}
856			},
857			menuScrollOver: function($sub, e) {
858				if (mouse) {
859					if (!/^scroll-(up|down)/.test(e.target.className) && this.getClosestMenu(e.target) == $sub[0]) {
860						this.menuScrollRefreshData($sub);
861						var data = $sub.dataSM('scroll'),
862							upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
863						$sub.dataSM('scroll-arrows').eq(0).css('margin-top', upEnd).end()
864							.eq(1).css('margin-top', upEnd + this.getViewportHeight() - data.arrowDownH).end()
865							.css('visibility', 'visible');
866					}
867				}
868			},
869			menuScrollRefreshData: function($sub) {
870				var data = $sub.dataSM('scroll'),
871					upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
872				if (this.cssTransforms3d) {
873					upEnd = -(parseFloat($sub.css('margin-top')) - upEnd);
874				}
875				$.extend(data, {
876					upEnd: upEnd,
877					downEnd: upEnd + this.getViewportHeight() - data.subH
878				});
879			},
880			menuScrollStop: function($sub) {
881				if (this.scrollTimeout) {
882					cancelAnimationFrame(this.scrollTimeout);
883					this.scrollTimeout = 0;
884					$sub.dataSM('scroll').step = 1;
885					return true;
886				}
887			},
888			menuScrollTouch: function($sub, e) {
889				e = e.originalEvent;
890				if (isTouchEvent(e)) {
891					var touchPoint = this.getTouchPoint(e);
892					// neglect event if we touched a visible deeper level sub menu
893					if (this.getClosestMenu(touchPoint.target) == $sub[0]) {
894						var data = $sub.dataSM('scroll');
895						if (/(start|down)$/i.test(e.type)) {
896							if (this.menuScrollStop($sub)) {
897								// if we were scrolling, just stop and don't activate any link on the first touch
898								e.preventDefault();
899								this.$touchScrollingSub = $sub;
900							} else {
901								this.$touchScrollingSub = null;
902							}
903							// update scroll data since the user might have zoomed, etc.
904							this.menuScrollRefreshData($sub);
905							// extend it with the touch properties
906							$.extend(data, {
907								touchStartY: touchPoint.pageY,
908								touchStartTime: e.timeStamp
909							});
910						} else if (/move$/i.test(e.type)) {
911							var prevY = data.touchY !== undefined ? data.touchY : data.touchStartY;
912							if (prevY !== undefined && prevY != touchPoint.pageY) {
913								this.$touchScrollingSub = $sub;
914								var up = prevY < touchPoint.pageY;
915								// changed direction? reset...
916								if (data.up !== undefined && data.up != up) {
917									$.extend(data, {
918										touchStartY: touchPoint.pageY,
919										touchStartTime: e.timeStamp
920									});
921								}
922								$.extend(data, {
923									up: up,
924									touchY: touchPoint.pageY
925								});
926								this.menuScroll($sub, true, Math.abs(touchPoint.pageY - prevY));
927							}
928							e.preventDefault();
929						} else { // touchend/pointerup
930							if (data.touchY !== undefined) {
931								if (data.momentum = Math.pow(Math.abs(touchPoint.pageY - data.touchStartY) / (e.timeStamp - data.touchStartTime), 2) * 15) {
932									this.menuScrollStop($sub);
933									this.menuScroll($sub);
934									e.preventDefault();
935								}
936								delete data.touchY;
937							}
938						}
939					}
940				}
941			},
942			menuShow: function($sub) {
943				if (!$sub.dataSM('beforefirstshowfired')) {
944					$sub.dataSM('beforefirstshowfired', true);
945					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
946						return;
947					}
948				}
949				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
950					return;
951				}
952				$sub.dataSM('shown-before', true);
953				if (canAnimate) {
954					$sub.stop(true, true);
955				}
956				if (!$sub.is(':visible')) {
957					// highlight parent item
958					var $a = $sub.dataSM('parent-a'),
959						collapsible = this.isCollapsible();
960					if (this.opts.keepHighlighted || collapsible) {
961						$a.addClass('highlighted');
962					}
963					if (collapsible) {
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					}
984					var complete = function() {
985						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
986						$sub.css('overflow', '');
987					};
988					// if sub is collapsible (mobile view)
989					if (collapsible) {
990						if (canAnimate && this.opts.collapsibleShowFunction) {
991							this.opts.collapsibleShowFunction.call(this, $sub, complete);
992						} else {
993							$sub.show(this.opts.collapsibleShowDuration, complete);
994						}
995					} else {
996						if (canAnimate && this.opts.showFunction) {
997							this.opts.showFunction.call(this, $sub, complete);
998						} else {
999							$sub.show(this.opts.showDuration, complete);
1000						}
1001					}
1002					// accessibility
1003					$a.attr('aria-expanded', 'true');
1004					$sub.attr({
1005						'aria-expanded': 'true',
1006						'aria-hidden': 'false'
1007					});
1008					// store sub menu in visible array
1009					this.visibleSubMenus.push($sub);
1010					this.$root.triggerHandler('show.smapi', $sub[0]);
1011				}
1012			},
1013			popupHide: function(noHideTimeout) {
1014				if (this.hideTimeout) {
1015					clearTimeout(this.hideTimeout);
1016					this.hideTimeout = 0;
1017				}
1018				var self = this;
1019				this.hideTimeout = setTimeout(function() {
1020					self.menuHideAll();
1021				}, noHideTimeout ? 1 : this.opts.hideTimeout);
1022			},
1023			popupShow: function(left, top) {
1024				if (!this.opts.isPopup) {
1025					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
1026					return;
1027				}
1028				if (this.hideTimeout) {
1029					clearTimeout(this.hideTimeout);
1030					this.hideTimeout = 0;
1031				}
1032				this.$root.dataSM('shown-before', true);
1033				if (canAnimate) {
1034					this.$root.stop(true, true);
1035				}
1036				if (!this.$root.is(':visible')) {
1037					this.$root.css({ left: left, top: top });
1038					// show menu
1039					var self = this,
1040						complete = function() {
1041							self.$root.css('overflow', '');
1042						};
1043					if (canAnimate && this.opts.showFunction) {
1044						this.opts.showFunction.call(this, this.$root, complete);
1045					} else {
1046						this.$root.show(this.opts.showDuration, complete);
1047					}
1048					this.visibleSubMenus[0] = this.$root;
1049				}
1050			},
1051			refresh: function() {
1052				this.destroy(true);
1053				this.init(true);
1054			},
1055			rootKeyDown: function(e) {
1056				if (!this.handleEvents()) {
1057					return;
1058				}
1059				switch (e.keyCode) {
1060					case 27: // reset on Esc
1061						var $activeTopItem = this.activatedItems[0];
1062						if ($activeTopItem) {
1063							this.menuHideAll();
1064							$activeTopItem[0].focus();
1065							var $sub = $activeTopItem.dataSM('sub');
1066							if ($sub) {
1067								this.menuHide($sub);
1068							}
1069						}
1070						break;
1071					case 32: // activate item's sub on Space
1072						var $target = $(e.target);
1073						if ($target.is('a') && this.handleItemEvents($target)) {
1074							var $sub = $target.dataSM('sub');
1075							if ($sub && !$sub.is(':visible')) {
1076								this.itemClick({ currentTarget: e.target });
1077								e.preventDefault();
1078							}
1079						}
1080						break;
1081				}
1082			},
1083			rootOut: function(e) {
1084				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1085					return;
1086				}
1087				if (this.hideTimeout) {
1088					clearTimeout(this.hideTimeout);
1089					this.hideTimeout = 0;
1090				}
1091				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
1092					var self = this;
1093					this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
1094				}
1095			},
1096			rootOver: function(e) {
1097				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1098					return;
1099				}
1100				if (this.hideTimeout) {
1101					clearTimeout(this.hideTimeout);
1102					this.hideTimeout = 0;
1103				}
1104			},
1105			winResize: function(e) {
1106				if (!this.handleEvents()) {
1107					// we still need to resize the disable overlay if it's visible
1108					if (this.$disableOverlay) {
1109						var pos = this.$root.offset();
1110	 					this.$disableOverlay.css({
1111							top: pos.top,
1112							left: pos.left,
1113							width: this.$root.outerWidth(),
1114							height: this.$root.outerHeight()
1115						});
1116					}
1117					return;
1118				}
1119				// hide sub menus on resize - on mobile do it only on orientation change
1120				if (!('onorientationchange' in window) || e.type == 'orientationchange') {
1121					var collapsible = this.isCollapsible();
1122					// if it was collapsible before resize and still is, don't do it
1123					if (!(this.wasCollapsible && collapsible)) { 
1124						if (this.activatedItems.length) {
1125							this.activatedItems[this.activatedItems.length - 1][0].blur();
1126						}
1127						this.menuHideAll();
1128					}
1129					this.wasCollapsible = collapsible;
1130				}
1131			}
1132		}
1133	});
1134
1135	$.fn.dataSM = function(key, val) {
1136		if (val) {
1137			return this.data(key + '_smartmenus', val);
1138		}
1139		return this.data(key + '_smartmenus');
1140	};
1141
1142	$.fn.removeDataSM = function(key) {
1143		return this.removeData(key + '_smartmenus');
1144	};
1145
1146	$.fn.smartmenus = function(options) {
1147		if (typeof options == 'string') {
1148			var args = arguments,
1149				method = options;
1150			Array.prototype.shift.call(args);
1151			return this.each(function() {
1152				var smartmenus = $(this).data('smartmenus');
1153				if (smartmenus && smartmenus[method]) {
1154					smartmenus[method].apply(smartmenus, args);
1155				}
1156			});
1157		}
1158		return this.each(function() {
1159			// [data-sm-options] attribute on the root UL
1160			var dataOpts = $(this).data('sm-options') || null;
1161			if (dataOpts && typeof dataOpts != 'object') {
1162				dataOpts = null;
1163				alert('ERROR\n\nSmartMenus jQuery init:\nThe value of the "data-sm-options" attribute must be valid JSON.');
1164			}
1165			// do not support function options
1166			if (dataOpts) {
1167				$.each(['showFunction', 'hideFunction', 'collapsibleShowFunction', 'collapsibleHideFunction'], function() {
1168					if (this in dataOpts) {
1169						delete dataOpts[this];
1170					}
1171				});
1172			}
1173			new $.SmartMenus(this, $.extend({}, $.fn.smartmenus.defaults, options, dataOpts));
1174		});
1175	};
1176
1177	// default settings
1178	$.fn.smartmenus.defaults = {
1179		isPopup:		false,		// is this a popup menu (can be shown via the popupS
1179how/popupHide methods) or a permanent menu bar
1180		mainMenuSubOffsetX:	0,		// pixels offset from default position
1181		mainMenuSubOffsetY:	0,		// pixels offset from default position
1182		subMenusSubOffsetX:	0,		// pixels offset from default position
1183		subMenusSubOffsetY:	0,		// pixels offset from default position
1184		subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1185		subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1186		subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
1187		subIndicatorsPos: 	'append',	// position of the SPAN relative to the menu item content ('append', 'prepend')
1188		subIndicatorsText:	'',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
1189		scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
1190		scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
1191		showTimeout:		250,		// timeout before showing the sub menus
1192		hideTimeout:		500,		// timeout before hiding the sub menus
1193		showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
1194		showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
1195							// don't forget to call complete() at the end of whatever you do
1196							// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
1197		hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
1198		hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
1199							// don't forget to call complete() at the end of whatever you do
1200							// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
1201		collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
1202		collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
1203							// (i.e. when mobile styles are used to make the sub menus collapsible)
1204		collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
1205		collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
1206							// (i.e. when mobile styles are used to make the sub menus collapsible)
1207		showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (i.e. mimic desktop app menus) (matters only for mouse input)
1208		hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
1209		noMouseOver:		false,		// disable sub menus activation onmouseover (i.e. behave like in touch mode - use just mouse clicks) (matters only for mouse input)
1210		keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
1211		keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
1212		markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
1213		markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
1214		rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
1215		bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
1216		collapsibleBehavior:	'default'	// parent items behavior in collapsible (mobile) view ('default', 'toggle', 'link', 'accordion', 'accordion-toggle', 'accordion-link')
1217							// 'default' - first tap on parent item expands sub, second tap loads its link
1218							// 'toggle' - the whole parent item acts just as a toggle button for its sub menu (expands/collapses on each tap)
1219							// 'link' - the parent item acts as a regular item (first tap loads its link), the sub menu can be expanded only via the +/- button
1220							// 'accordion' - like 'default' but on expand also resets any visible sub menus from deeper levels or other branches
1221							// 'accordion-toggle' - like 'toggle' but on expand also resets any visible sub menus from deeper levels or other branches
1222							// 'accordion-link' - like 'link' but on expand also resets any visible sub menus from deeper levels or other branches
1223	};
1224
1225	return $;
1226}));

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.