PageSourceSearch

https://www.lsrhs.net/js/jquery.smartmenus.js

js lsrhs.net collected 2026-09-24 10:12:25 UTC 45,012 bytes, 1,215 lines download raw bytes

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

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.