PageSourceSearch

https://pycon.ie/static/js/jquery.smartmenus.a4fdee57ca72.js

js pycon.ie collected 2026-09-28 10:02:54 UTC 44,189 bytes, 1,219 lines download raw bytes

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

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.