PageSourceSearch

https://www.sariyermarket.com/Themes/Sariyer/Content/scripts/smartmenus.js?vObs=4.2.4.8

js sariyermarket.com collected 2026-10-01 23:49:24 UTC 52,959 bytes, 1,388 lines download raw bytes

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

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.