PageSourceSearch

https://www.ifocus.gr/modules/mod_vertical_menu/js/mod_vertical_menu.js?v=4.0.320

js ifocus.gr collected 2026-10-02 07:26:33 UTC 56,052 bytes, 1,452 lines download raw bytes

1/*!
2 * mod_vertical_menu - Vertical Menu
3 *
4 * @author    Balint Polgarfi
5 * @copyright 2014-2023 Offlajn.com All Rights Reserved
6 * @license   https://gnu.org/licenses/gpl-2.0.html
7 * @link      https://offlajn.com
8 */
9
10;(function($, undefined) {
11
12var Hlpr = {
13	width: $.fn.width,
14	hackWidth: function(reduce) {
15		return function(arg) {
16			var value = Hlpr.width.apply(this, arguments);
17			if (this[0] == window && !$.isNumeric(arg) && value > 767) value -= reduce;
18			return value;
19		}
20	},
21	preventDefault: function(e) {
22		e.preventDefault();
23	},
24	stopPropagation: function(e) {
25		e.stopPropagation();
26	},
27	cancelTransitionEnd: function(e) {
28		if(e.target.tagName != "NAV") e.stopPropagation();
29	},
30	loadImage: function(src) {
31		new Image().src = src;
32	},
33	calcOrigin: function(origin, w, dir) {
34		var o = origin.split(/\s+/);
35		if (o[2] && o[2].indexOf("%") > 0) // convert originZ to px
36			o[2] = parseFloat(o[2]) / 100 * w + "px";
37		if (dir < 0) { // invert
38			if (o[0].indexOf("%") < 0)
39				o[0] = (parseInt(o[0]) / w * 100).toPrecision(3);
40			o[0] = (parseInt(o[0]) - 50) * dir + 50 + "%";
41		}
42		return o.join(" ");
43	}
44};
45
46var Target = {
47	hide: function() {
48		this.target.style.display = "none";
49	},
50	remove: function() {
51		this.target.parentNode.removeChild(this.target);
52	},
53	scrollUp: function() {
54		$(this.target.children[0]).scrollTop(0);
55		this.target.style.display = "none";
56	},
57	autoMaxWidth: function() {
58		this.target.attributes["class"].value = "sm-title";
59		this.target.style.maxWidth = "";
60	},
61	autoHeight: function() {
62		this.target.style.height = "";
63	}
64};
65
66// calc scrollWidth
67$(function() {
68	var outer = document.createElement("div");
69	outer.style.visibility = "hidden";
70	outer.style.width = "100px";
71	document.body.appendChild(outer);
72	var widthNoScroll = outer.offsetWidth;
73	outer.style.overflow = "scroll";
74	var inner = document.createElement("div");
75	inner.style.width = "100%";
76	outer.appendChild(inner);
77	var widthWithScroll = inner.offsetWidth;
78	outer.parentNode.removeChild(outer);
79
80	Hlpr.scrollWidth = widthNoScroll - widthWithScroll;
81});
82
83smTransform = (function() {
84	var tr, trs = {transform: 0, WebkitTransform: 0, MozTransform: 0, msTransform: 0, OTransform: 0};
85	for (tr in trs) if (tr in document.documentElement.style) return tr;
86})();
87
88smTransition = (function() {
89	var tr, trs = {transition: 0, WebkitTransition: 0, MozTransition: 0, OTransition: 0};
90	for (tr in trs) if (tr in document.documentElement.style) return tr;
91})();
92
93smTransitionEnd = smTransition && smTransition + (smTransition[0] == 't' ? 'end' : 'End');
94
95var $body,
96		$win = $(window),
97		$html = $(document.documentElement),
98		oldIE = navigator.userAgent.match(/MSIE ([6-9])/),
99		backScale = 0.75;
100if (smMobile) $html.addClass('sm-mobile');
101
102$.fn._center = function(bi) {
103	var x, w, prev;
104	if (bi)
105		prev = this[0].parentNode.children[1] == this[0] ? 0 : $(this[0].parentNode.children[0]).outerWidth();
106	else
107		prev = this.prev().outerWidth() * backScale || 0;
108	w = this.css("maxWidth", "none").width() + 30;
109	this[0].style.maxWidth = "";
110	x = Math.floor((this.parent().outerWidth() - w) / 2);
111	return prev > x ? prev : x;
112};
113
114function deleteMediaRules(match) {
115	var css = '';
116	for (var j = 0; j < document.styleSheets.length; j++) {
117		var style = document.styleSheets[j];
118		if (!style.href || style.href.indexOf('mod_vertical_menu') < 0) try {
119			for (var i = 0; i < style.cssRules.length; i++) {
120				var rule = style.cssRules[i];
121				if (rule.media && rule.media.mediaText.match(match)) {
122					if (style.href)
123						css += rule.cssText.replace(/(url\(['"]?)([^)]+)/i, function(m, url, href) {
124							return href.match(/^\/|^https?:|^data:/i) ? m : url + style.href.replace(/[^/]+$/, '') + href;
125						}) + '\n';
126					else css += rule.cssText + '\n';
127					style.deleteRule(i--);
128				}
129			}
130		} catch(ex) {}
131	}
132	return css;
133}
134function hackMediaWidths(width) {
135	width = width || 0;
136	if (!hackMediaWidths.hasOwnProperty('css')) {
137		hackMediaWidths.css = [deleteMediaRules('width')];
138		hackMediaWidths.$css = $('<style id="hack-media">').appendTo(document.head);
139	}
140	if (!hackMediaWidths.css.hasOwnProperty(width)) {
141		hackMediaWidths.css[width] = hackMediaWidths.css[0].replace(/\((\w+-width:\s*)(\d+)px\)/g, function() {
142			return '('+ arguments[1] + (parseInt(arguments[2]) + width) +'px)';
143		});
144	}
145	hackMediaWidths.$css.html(hackMediaWidths.css[width]);
146};
147
148$.fn.VerticalMenu = function(args) { return args ? new VerticalSlideMenu(args) : this.eq(0).data("VerticalMenu") };
149
150( VerticalSlideMenu = function(o) {this.init(o)} ).prototype = {
151	$drop: $(),
152	$overlay: $(),
153	minSwipe: 2,
154	scrollMinDur: 0.4,
155	scrollDistDiv: 5000,
156	titleWidth: "66%",
157	fullClass: "sm-full-",
158	openClass: "sm-open-",
159	closeClass: "sm-close",
160	effect: 1, // [1 - 14]
161	effectEx: {'-1':1, '1':1, '6':1, '9':1, '11':1, '12':1},
162	popupDur: 0.6,
163	baseCSS: {
164		x: 0, y: 0,
165		opacity: 1,
166		rotationX: 0,
167		rotationY: 0,
168		rotationZ: 0,
169		skewX: 0, scaleX: 1,
170		skewY: 0, scaleY: 1
171	},
172
173	init: function(obj) {
174		$.extend(this, obj);
175		$body = $body || $(document.body);
176		this.$node = $('#off-menu_'+this.id).data("VerticalMenu", this);
177		this.$parent = this.$node.parent().addClass("sm-parent");
178		this.$head = this.$node.find(".sm-head");
179		this.$back = this.$head.find(".sm-back");
180		this.$title = this.$head.children(":last");
181		this.$lvls = this.$node.find(".sm-levels");
182		this.$filter = this.$node.find(".sm-filter").attr("value", "");
183		this.$dt = this.$lvls.find("dt.parent, dt.notparent");
184		this.fullClass += this.id;
185		this.openClass += this.id;
186		this.menuPadding = parseInt(this.$lvls.children(':first').css('paddingLeft'));
187		this.bindUpdateScrollbar = smMobile ? undefined : this.updateScrollbar.bind(this, 0);
188		this.$lvls.find('a').map(function(i, a) { // scrollspy
189			var hash = a.href.match(/#([\w-]+)$/);
190			return hash ? document.getElementById(hash[1]) : undefined;
191		}).menuspy().length && $body.on('scrollEnter.menuspy', function(e) {
192			this.$lvls.find('a[href$="#'+ e.target.id +'"]').closest('dt').addClass('active').siblings('.active').removeClass('active');
193		}.bind(this)).on('scrollLeave.menuspy', function(e) {
194			this.$lvls.find('a[href$="#'+ e.target.id +'"]').closest('dt').removeClass('active');
195		}.bind(this));
196
197		this.$node.find('.sm-x').on('click.sm touchend.sm', function() {
198			this.closeMenu();
199		}.bind(this));
200
201		this.dropmenu = this.navtype == 'drop';
202		this.accordion = this.navtype == 'accordion';
203		this.treemenu = this.navtype == 'tree' || this.accordion;
204		this.expand = this.navtype == 'expand' || this.treemenu;
205
206		if (this.overlay && !this.expand) { // override menu type
207			this.dropmenu = 0;
208			this.expand = 1;
209			this.treemenu = this.navtype != 'expand';
210		}
211		if ((this.treemenu || this.expand) && !this.$filter.length) this.backItem = '';
212		if (this.popup || this.overlay) this.sidebarUnder = 0;
213		if (this.treemenu)
214			this.$node.addClass("sm-tree").on("click.sm touchend.sm", "dt.parent", $.proxy(this, "onClickTree"));
215		this.build();
216		this.onResize();
217		if (!this.sidebar && this.winWidth >= this.sidebarUnder) this.$lvls.height(''); // height fix for webkit
218		if (this.filterMinChar < 1) this.filterMinChar = 1;
219
220		this.bg = this[this.overlay ? "$overlay" : "$node"].css("backgroundImage").match(/url\(['"]?(.+?)['"]?\)/i);
221
222		if (this.popup || this.sidebar || this.overlay || this.winWidth < this.sidebarUnder) {
223			// only make the autoopen work if the menu is not in module position
224			var w = $(window).width(), visible = true;
225			if (!+this.visibility[4]) { // basic
226				if (w < 768 && !+this.visibility[0]) visible = false;
227				if (w >= 768 && w < 992 && !+this.visibility[1]) visible = false;
228				if (w >= 992 && w < 1200 && !+this.visibility[2]) visible = false;
229				if (w >= 1200 && !+this.visibility[3]) visible = false;
230			} else { // advanced
231				if (this.visibility[5][0] > w || this.visibility[6][0] < w) visible = false;
232			}
233			if (visible) switch (this.autoOpen) {
234				case 1: // Only once
235					if (!window.sessionStorage || sessionStorage.smAutoOpen) break;
236					sessionStorage.smAutoOpen = 1;
237				case 2: // Always
238					if (w < 768 && this.autoOpen == 2) break; // disable always auto open on mobile
239					if (this.autoOpenAnim) {
240						// animate auto open
241						$win.one('load.sm', $.proxy(this, "openMenu"));
242					} else {
243						// don't animate auto open
244						if (this.popup) {
245							this.$button.eq(1).hide();
246							this.$noTrans = function() { this.$button.eq(1).show(), delete this.$noTrans }.bind(this);
247						} else {
248							this.$noTrans = this.$node
249								.add(this.$pusher)
250								.add(this.$content)
251								.add(this.$overlay.parent())
252								.add(this.$button.hide())
253								.css(smTransition+'Duration', '1ms')
254							;
255						}
256						$(this.openMenu.bind(this));
257					}
258			}
259		}
260
261		// add events
262		if (this.parentHref) // don't slide to submenu
263			this.$node.on("click.sm touchend.sm", "dt.parent a", $.proxy(this, "onRedirect"));
264		else // disable redirection when pointer-events:none isn't supported
265			this.$node.on("click.sm", "dl:not(.sm-result) dt.parent a", Hlpr.preventDefault);
266		this.$node
267			.on("click.sm touchend.sm", this.expand && !this.treemenu ? "dt" : ".notparent", $.proxy(this, "onRedirect"))
268			.on("click.sm touchend.sm", ".sm-back, .sm-back-item", $.proxy(this, "onBack"))
269			.on("click.sm touchend.sm", ".sm-reset", $.proxy(this, "onResetFilter"))
270			.on("click.sm", Hlpr.stopPropagation);
271		if (!this.expand) this.$node.on("click.sm touchend.sm", "dt.parent", $.proxy(this, "onOpen"));
272		this.$filter
273			.on("keydown.sm", function disableTab(e) {if (e.keyCode == 9) e.currentTarget.blur()})
274			.on("keyup.sm", $.proxy(this, "onFilter"));
275		this.$button.on("click.sm", $.proxy(this, "onClickButton")); // touch event causes problems on scroll
276		this.$lvls
277			.on("touchstart.sm", $.proxy(this, "onTouchStart"))
278			.on("touchmove.sm", $.proxy(this, "onTouchMove"))
279			.on("touchend.sm touchcancel.sm", $.proxy(this, "onTouchEnd"))
280			.on("mousedown.sm", $.proxy(this, "onMouseDown"));
281		$win
282			.on("resize.sm", $.proxy(this, "onResize"))
283			.on("keyup.sm", $.proxy(this, "onEsc"));
284		if (this.bg) $win.on("load.sm", Hlpr.loadImage.bind(0, this.bg[1]));
285		$body.one("touchend.sm", $.proxy(this, "touchOptimize"));
286	},
287
288	touchOptimize: function() {
289		this.$node
290			.off("click.sm", "dt.parent")
291			.off("click.sm", "dt")
292			.off("click.sm", ".notparent")
293			.off("click.sm", ".sm-back, .sm-back-item");
294		if (this.dropmenu)
295			this.$drop.off("click.sm", "dt");
296	},
297
298	build: function() {
299		// build menu icon
300		this.$button = $('<div class="menu-icon-cont sm-btn-'+this.id+'">')
301			.html('<div class="menu-icon3"><span></span><span></span><span></span></div>');
302
303		if (this.dropmenu) {
304			this.anim.inRotationY = this.anim.inCSS.rotationY;
305			this.anim.outRotationY = this.anim.outCSS.rotationY;
306			this.miRotationY = this.miCSS.rotationY;
307			this.$drop = $('<nav>').css({
308				position: 'absolute',
309				left: 0, right: 0,
310				top: 0, zIndex: 999
311			}).addClass("sm-drop off-menu_"+this.id)
312				.appendTo($body)
313				.on("click.sm touchend.sm", "dt", $.proxy(this, "onRedirect"))
314		}
315
316		// plus svg animation hack
317		$(this.$lvls[0].querySelectorAll('.sm-arrow use[*|href*=plus]')).each(function() {
318			$(this).clone().insertAfter(this);
319		});
320
321		//add logo link
322		var link = this.logoUrl;
323		if (link) $("nav.off-menu_" + this.id +" .sm-logo img").css("cursor", "pointer").click(function(e) {
324			document.location.href = link;
325		});
326
327		if (this.popup) { // POPUP
328			this.initPopup();
329		} else if (this.sidebar || this.sidebarUnder) { // SIDEBAR
330			this.initContainer();
331			this.initSidebar();
332		} else if (this.overlay) { // OVERLAY
333			this.initContainer();
334			this.initOverlay();
335		}
336
337		// build submenus
338		var $div = this.$lvls.find(".sm-level");
339		$div.push( // add level for search results
340			$('<div class="sm-level level2"><dl class="sm-result level2">')
341				.css('display', 'none').appendTo(this.$lvls)[0]);
342		this.opened = $div[0];
343		this.mainmenu = this.opened;
344		if (this.treemenu) this.$lvls.find('dd.opened').css('display', 'block'); // init tree/accordion animation
345		if (this.backItem) {
346			var $backItem = $("dt:first", $div[0]).clone().removeClass("parent notparent active").addClass("sm-back-item");
347			$backItem.find("a").removeAttr("href").html(this.backItem);
348			$backItem.find(".desc, .sm-icon, .productnum").remove();
349		}
350		if (!this.expand) {
351			var level, div, i;
352			for (i = 1; i < $div.length; i++) {
353				if (this.backItem && (!this.dropmenu || i == $div.length - 1)) // if drop, add only for search results
354					$backItem.clone().prependTo($div[i].children[0]); // add back menu-item to sublevels
355				div = $div[i];
356				div.style.display = "none";
357				div.parent = $(div.parentNode).closest(".sm-level")[0];
358				div.parentItem = $(div.parentNode).prev()[0];
359				if (div.parentNode.tagName == "DD")
360					$(div.parentNode).prev()[0].menu = div;
361			}
362		}
363		this.$result = this.$lvls.children(":last");
364		if (this.expand && this.backItem) $backItem.clone().prependTo(this.$result[0].children[0]); // add back to search result
365	},
366
367	initPopup: function() {
368		var $clone = this.$button.clone()
369			.css(smTransform, "translateX(-100%)")
370			.css("zIndex", -1).appendTo(this.$node);
371		this.$node.css({
372			display: "block", position: "absolute",
373			visibility: "hidden", opacity: 0,
374			left: 0, top: 0,
375			width: this.width,
376			margin: 0, zIndex: 99999,
377			WebkitTransition: "none", MozTransition: "none", msTransition: "none", transition: "none"
378		}).addClass("sm-popup");
379		if (this.menuIconCorner) { // button in corner
380			this.$node.appendTo(this.$button);
381			this.$button.appendTo($body);
382		} else { // button in module position
383			this.$node.appendTo($body);
384			this.$button.appendTo(this.$parent);
385		}
386		this.$button.addClass('sm-popup-burger').push($clone[0]);
387		this.$cont = $body;
388		this.effect = 0;
389	},
390
391	initContainer: function() {
392		this.htmlCSS = {
393			background: $html.css("backgroundColor")+" "+$html.css("backgroundImage")+" "+$html.css("backgroundRepeat")+" "+
394				$html.css("backgroundAttachment")+" "+$html.css("backgroundPosition")+" / "+$html.css("backgroundSize")
395		};
396		this.bodyCSS = {
397			overflowY : $body.css("overflowY"),
398			borderTop: $body.css("borderTopWidth")+" "+$body.css("borderTopStyle")+" "+$body.css("borderTopColor"),
399			borderBottom: $body.css("borderBottomWidth")+" "+$body.css("borderBottomStyle")+" "+$body.css("borderBottomColor"),
400			borderLeft: $body.css("borderLeftWidth")+" "+$body.css("borderLeftStyle")+" "+$body.css("borderLeftColor"),
401			borderRight: $body.css("borderRightWidth")+" "+$body.css("borderRightStyle")+" "+$body.css("borderRightColor"),
402			padding: $body.css("paddingTop")+" "+$body.css("paddingRight")+" "+$body.css("paddingBottom")+" "+$body.css("paddingLeft"),
403			background: $body.css("backgroundColor")+" "+$body.css("backgroundImage")+" "+$body.css("backgroundRepeat")+" "+
404				$body.css("backgroundAttachment")+" "+$body.css("backgroundPosition")+" / "+$body.css("backgroundSize")
405		};
406
407		this.$cont = $body.addClass("sm-container");
408		this.$pusher = $(".sm-pusher");
409		if (!this.$pusher.length) {
410			var $cnt = $body.children(':not(nav.sm-menu)');
411			this.$pusher = $('<div class="sm-pusher"><div class="sm-content"><div class="sm-content-inner">').appendTo($body);
412		}
413		this.$content = this.$pusher.children();
414		this.$inner = this.$content.children(".sm-content-inner").append($cnt);
415
416		$html.css('overflow-y', 'auto');
417	},
418
419	initSidebar: function() {
420		if (this.sidebar) {
421			this.$button.css("display", "");
422			this.$node.appendTo(this.$cont);
423		}
424		this.$cont.on("click.sm touchstart.sm", function(e) { this.effect < 0 || this.closeMenu(e) }.bind(this));
425		// disable page scrolling when sidebar is open
426		this.$lvls.on("touchstart.sm", function(e) { this.effect < 0 || this.scrollFixStart(e) }.bind(this));
427		this.$cont.on("touchmove.sm", function(e) { this.effect < 0 || this.scrollFixMove(e) }.bind(this));
428
429		// TransitionEnd fix
430		this.$node.on(smTransitionEnd, Hlpr.cancelTransitionEnd);
431		this.$inner.on(smTransitionEnd, Hlpr.cancelTransitionEnd);
432
433		if (this.menuIconCorner) {
434			this.$button.appendTo(this.$inner);
435		} else {
436			this.$button.appendTo(this.$parent);
437			this.$button.push(this.$button.clone().css("opacity", 0).appendTo(this.$node)[0]);
438		}
439		if (!this.sidebar) this.$button.css("display", "none");
440	},
441
442	initOverlay: function() {
443		var $overlay = $('<div>').addClass("sm-overlay-"+this.id).appendTo($body);
444		this.$overlay = $('<div>').addClass("sm-overlay-win").appendTo($overlay)
445			.on("click.sm "+smTransitionEnd, Hlpr.stopPropagation) // don't close overlay on click & TransitionEnd fix
446		this.$node.appendTo(this.$overlay);
447		this.$cont.on("click.sm touchstart.sm", $.proxy(this, "closeMenu"));
448		// disable touch-scrolling when sidebar is open
449		this.$lvls.on("touchstart.sm", $.proxy(this, "scrollFixStart"));
450		this.$cont.on("touchmove.sm", $.proxy(this, "scrollFixMove"));
451
452		if (this.menuIconCorner) {
453			this.$button.appendTo($body);
454		} else {
455			this.$button.appendTo(this.$parent);
456			this.$button.push(this.$button.clone().css("opacity", 0).insertBefore(this.$node)[0]);
457		}
458	},
459
460	scrollFixStart: function(e) {
461		this.touchstartY = e.originalEvent.touches[0].clientY;
462	},
463
464	scrollFixMove: function(e) {
465		if (!this.$cont.hasClass(this.openClass)) return;
466		else if (!$(e.target).closest('.sm-levels').length) return e.preventDefault();
467
468		var node = this.opened.children[0];
469		if (this.touchstartY < e.originalEvent.touches[0].clientY) {  // on scroll up
470			if (node.scrollTop == 0) e.preventDefault();
471		} else { // on scroll down
472			if (node.scrollTop == node.scrollHeight - node.offsetHeight) e.preventDefault();
473		}
474	},
475
476	onMouseDown: function(e) {
477		if (e.button == 0) {
478			if (e.target.tagName == "A") e.preventDefault();
479			this.onTouchStart(e);
480			$body
481				.on("mousemove.sm", $.proxy(this, "onTouchMove"))
482				.on("mouseup.sm", $.proxy(this, "onMouseUp"));
483		}
484	},
485
486	onMouseUp: function(e) {
487		if (e.button == 0) {
488			$body.off("mousemove.sm mouseup.sm");
489			this.onTouchEnd(e);
490		}
491	},
492
493	onTouchStart: function(e) {
494		var touch = e.originalEvent.touches && e.originalEvent.touches[0] || e;
495		this.touchStartX = this.touchPrevX = touch.pageX;
496		this.touchStartY = this.touchPrevY = touch.pageY;
497		this.touchTarget = e.target;
498		delete this.touchEvent;
499	},
500
501	onTouchMove: function(e) {
502		if (this.touchEvent == "scroll") return;
503		if (smMobile && smMobile[0] == "Windows Phone") e.preventDefault(); // disable scrolling or swipe back
504		var touch = e.originalEvent.touches && e.originalEvent.touches[0] || e,
505				x = touch.pageX,
506				y = touch.pageY;
507				diffX = x - this.touchStartX,
508				diffY = y - this.touchStartY;
509		if (this.touchEvent === undefined) {
510			var dx = Math.abs(diffX),
511					dy = Math.abs(diffY);
512			if (dy > this.minSwipe && dy >= dx) { // vertical swipe
513				this.touchEvent = "scroll";
514				$body.addClass("sm-scroll");
515			} else if (!this.opened.parent) {
516				// don't check back swipe on main level
517			} else if (diffX > this.minSwipe && dx > dy) { // back swipe
518				if (this.openTL && this.openTL.isActive()) {
519					if (this.openTL.time() < this.dur) return;
520					else this.openTL.pause();
521				}
522				this.touchEvent = "back";
523				if (!smMobile) $(this.opened.children).perfectScrollbar("destroy");
524				this.opened.parent.style.display = "block";
525				this.initBackTimeline();
526				TweenLite.set(this.$lvls[0], {perspective: this.perspective}); // blur fix for retina
527				this.nodeW = this.$lvls.outerWidth();
528			}
529		}
530		if (this.touchEvent == "back") {
531			e.preventDefault(); // disable scrolling
532			diffX -= this.minSwipe;
533
534			if (diffX <= 1) diffX = 0.1;
535			if (diffX >= this.nodeW) diffX = this.nodeW - 0.1;
536			this.backTL.seek(this.dur * diffX / this.nodeW);
537
538			this.touchDirLeft = this.touchPrevX < x;
539			this.touchDirTop = this.touchPrevY < y;
540			if (this.touchPrevX != x) this.touchPrevX = x;
541			if (this.touchPrevY != y) this.touchPrevY = y;
542		}
543	},
544
545	onTouchEnd: function(e) {
546		var touch = e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] || e,
547				dx = Math.abs(this.touchStartX - touch.pageX),
548				dy = Math.abs(this.touchStartY - touch.pageY);
549
550		if (!this.touchEvent && dx <= this.minSwipe && dy <= this.minSwipe) return;
551		e.preventDefault();
552		e.stopPropagation();
553
554		if (this.touchEvent == "scroll") {
555			$body.removeClass("sm-scroll");
556		} else if (this.touchEvent == "back") {
557			// disable click when swipe finished on the same menu-item where it started
558			if (this.touchTarget == e.target) this.disableClick = true;
559			if (this.touchDirLeft) {
560				this.back();
561			} else {
562				if (!smMobile) $(this.opened.children).perfectScrollbar();
563				this.backTL.reverse();
564			}
565		}
566	},
567
568	onEsc: function(e) {
569		if (e.keyCode == 27 && !this.$button.eq(1).hasClass("sm-hide") && this.$button.css('display') != 'none') {
570			e.stopPropagation();
571			this.closeMenu();
572		}
573	},
574
575	onClickButton: function(e) {
576		e.stopPropagation();
577		e.preventDefault();
578		if (this.$button.hasClass("sm-hide")) {
579			this.$button[0]._gsTransform.x = this.menuIconX;
580			this.$button.css(smTransform, '').removeClass("sm-hide");
581			return;
582		}
583		if (this.popup) { // POPUP
584			if (this.$node.css("visibility") == "hidden") {
585				this.openMenu();
586			} else {
587				if (e.currentTarget == this.$button[0]) return; // don't close menu for any click
588				this.closeMenu();
589			}
590		} else { // SIDEBAR || OVERLAY
591			$(e.currentTarget).hasClass(this.closeClass) ? this.closeMenu() : this.openMenu();
592		}
593	},
594
595	onOpenPopup: function() {
596		var bw = this.$button.width(),
597				bh = this.$button.height(),
598				w = this.$node.width(),
599				h = this.$node.height();
600		this.$node.css("visibility", "visible");
601		if (this.menuIconCorner) {  // fixed position
602			TweenLite.set(this.$node[0], {transformOrigin: "0% 0% 0"});
603		} else {  // module position
604			this.onResize();
605		}
606		var tl = new TimelineLite({ paused: true, autoRemoveChildren: true, onComplete: this.$noTrans });
607		tl.add(TweenLite.fromTo(this.$node[0], 1, {scaleX: bw/w, scaleY: bh/h}, {css: {scaleX: 1, scaleY: 1}, ease: Back.easeOut}), 0);
608		tl.add(TweenLite.to(this.$node[0], 1, {css: {opacity: 1}, onComplete: $.proxy(this.$button.eq(1), "addClass", this.closeClass)}), 0);
609		tl.add(TweenLite.fromTo(this.$button[1], 0.75, {x: -bw, opacity: 1}, {css: {x: 0}}), -0.5);
610		tl.duration(this.$noTrans ? 0.001 : this.popupDur);
611		tl.play();
612		this.$cont.addClass(this.openClass);
613		this.$node.trigger("openMenu", this);
614	},
615
616	onClosePopup: function() {
617		var bw = this.$button.width(),
618				bh = this.$button.height(),
619				w = this.$node.width(),
620				h = this.$node.height();
621		this.$button.removeClass(this.closeClass);
622		var tl = new TimelineLite({paused: true, autoRemoveChildren: true});
623		tl.add(TweenLite.to(this.$button[1], 0.3, {css: {x: -bw}}), -0.15);
624		tl.add(TweenLite.to(this.$node[0], 0.3, {css: {opacity: 0}, ease: Quad.easeIn}), 0.3);
625		tl.add(TweenLite.to(this.$node[0], 0.3, {css: {scaleX: bw/w, scaleY: bh/h}, onComplete: $.proxy(this.$node, "css", "visibility", "hidden")}), 0.3);
626		tl.play();
627		this.$cont.removeClass(this.openClass);
628		this.$node.trigger("closeAllDrops",this);
629		this.$node.trigger("closeMenu", this);
630		this.$node.trigger("closeMenu", this);
631	},
632
633	openMenu: function(e, effect) {
634		if (this.$cont.hasClass(this.openClass)) return;
635		var otherOpen = this.$cont[0].className.match(/\bsm-open-(\d+)\b/);
636		if (otherOpen) { // if another menu is already open
637			window['sm'+otherOpen[1]].closeMenu();
638			return setTimeout($.proxy(this, 'openMenu'), 550);
639		}
640		if (e && e.stopPropagation) e.stopPropagation();
641		if (this.popup) return this.onOpenPopup();
642
643		$body.off(smTransitionEnd+".tr").one(smTransitionEnd+".tr", ".sm-pusher", $.proxy(this, "onEndOpenMenu"));
644
645		this.effect = effect || this.effect;
646		if (this.effect < 0) {
647			$html.addClass('sm-reduce-width');
648			hackMediaWidths(this.width);
649			$.fn.width = Hlpr.hackWidth(this.width);
650		}
651		this.$cont.addClass("sm-effect-" + Math.abs(this.effect));
652
653		var btn = this.$button.length && (!this.menuIconCorner || this.overlay || this.effectEx[this.effect]);
654		if (btn && this.menuIconCorner) this.$button.appendTo(this.overlay ? this.$overlay : this.$node);
655
656		this.scroll = $win.scrollTop();
657		if (smMobile) {
658			this.effect < 0 ? $win.off("resize.sm") : $win.off("resize.sm").scrollTop(0); // don't resize during animation on mobile
659		}
660
661		this.$cont.css({ position: 'fixed', top: 0, bottom: 0 });
662		$html.addClass(this.fullClass);
663		this.$inner.css(this.bodyCSS);
664		this.$content.css(this.htmlCSS);
665
666		if (!smMobile) {
667			if (btn && !this.menuIconCorner) {
668				if (!this.overlay) this.$button.eq(1).appendTo(this.effectEx[this.effect] ? this.$node : this.$inner);
669				this.$button[1].style.display = "inline-block";
670			}
671		} else { // mobile
672			if (this.$button[1]) this.$button[1].style.display = "block";
673		}
674
675		if (this.overlay) {
676			this.$content.css('maxWidth', '100vw'); // Firefox bugfix (#18)
677			this.$lvls.css('maxHeight', '');
678		} else if (this.sidebar || this.winWidth < this.sidebarUnder) {
679			if (this.effect > 0) this.$content.css('maxWidth', '100vw'); // Firefox bugfix (#18)
680			this.$lvls.css('maxHeight', this.effect == 8 ? window.innerHeight : window.innerHeight - this.$lvls.position().top);
680 // fix for sidebar effect 8
681		}
682
683		this.$content.scrollTop(this.scroll);
684		this.$cont.css("background", this.siteBg).addClass(this.openClass);
685		this.$node.trigger("openMenu", this);
686		if (oldIE) this.onEndOpenMenu();
687	},
688
689	onEndOpenMenu: function(e) {
690		if (smMobile) {
691			$win.on("resize.sm", $.proxy(this, "onResize")); // allow resize after animation on mobile
692			if (this.sidebar || this.winWidth < this.sidebarUnder) // fix for sidebar bottom can't be see
693				this.$lvls.css('maxHeight', window.innerHeight - this.$lvls.position().top);
694		}
695		if (this.effect < 0 && this.winWidth > 767) $win.resize();
696		if (this.menuIconCorner)
697			setTimeout($.proxy(this.$button, "addClass", this.closeClass), 1);
698		else {
699			this.$button[0].style.opacity = 0;
700			this.$button[1].style.opacity = 1;
701			this.$button.eq(1).addClass(this.closeClass);
702		}
703		if (this.overlay) { // calculate overlay max-height after transition
704			var topHeight = 0;
705			this.$lvls.prevAll().each(function() { topHeight += $(this).outerHeight() });
706			this.$lvls.css("maxHeight", this.$overlay.outerHeight() - topHeight);
707		} else if (this.effect == 8) { // fix for sidebar effect 8
708			this.$lvls.css('maxHeight', this.$cont.outerHeight() - this.$lvls.position().top);
709		}
710		this.$node.css("zIndex", 99);
711		if (this.$noTrans) {
712			this.$button.addClass(this.closeClass).show();
713			this.$noTrans.css(smTransition+'Duration', '');
714			delete this.$noTrans;
715		}
716		this.updateScrollbar();
717	},
718
719	closeMenu: function(e) {
720		if (e && $(e.target).closest(".sm-menu").length) return;
721		if (!this.$cont || !this.$cont.hasClass(this.openClass)) return;
722		if (this.popup) return this.onClosePopup();
723		$body.off(smTransitionEnd+".tr").one(smTransitionEnd+".tr", this.overlay ? ".sm-overlay-"+this.id : "", $.proxy(this, "onEndCloseMenu"));
724		if (!this.menuIconCorner) {
725			this.$button[1].style.display = "none";
726			this.$button[1].style.opacity = 0;
727		}
728		if (smMobile) this.$filter.blur();
729		this.$cont.removeClass(this.openClass);
730		this.$node.css("zIndex", "").trigger("closeMenu", this);
731		if (oldIE) this.onEndCloseMenu();
732	},
733
734	onEndCloseMenu: function(e) {
735		if (this.effect < 0) {
736			$.fn.width = Hlpr.width;
737			hackMediaWidths(0);
738			this.scroll = $win.scrollTop();
739			$html.removeClass('sm-reduce-width');
740			if (this.winWidth >= 768) $win.resize();
741		}
742		var s = this.$inner[0].style;
743		s.position = s.background = s.border = s.padding = s.margin = "";
744		$body[0].style.background = $body[0].style.position = $body[0].style.top = $body[0].style.bottom = "";
745		this.$content.css({height: '', background: ''});
746		$html.removeClass(this.fullClass);
747		//$html.css("overflowY", "");
748		if (smMobile) this.$node.css({top: "", position: ""});
749		$win.scrollTop(this.scroll);
750		this.$cont.removeClass("sm-effect-" + Math.abs(this.effect));
751
752		this.$content.css('maxWidth', ''); // Firefox bugfix (#18)
753		if (this.$button.length) {
754			if (!this.menuIconCorner || this.overlay || this.effectEx[this.effect]) {
755				if (this.menuIconCorner) this.$button.appendTo(this.$inner);
756				else this.$button[0].style.opacity = 1;
757				setTimeout($.proxy(this.$button, "removeClass", this.closeClass), 1);
758			}
759			else this.$button.removeClass(this.closeClass);
760		}
761	},
762
763	onResize: function(e) {
764		var isInCont = this.$node[0].parentNode != this.$parent[0],
765				$title = this.$title.last();
766		this.prevWinWidth = this.winWidth || $win.outerWidth();
767		this.winWidth = $win.outerWidth();
768		this.winHeight = $win.outerHeight();
769		// update button
770		if (!this.sidebar && this.winWidth < this.sidebarUnder && !isInCont) {
771			this.$button.css("display", "");
772			this.$node
773				.appendTo(this.$cont)
774				.trigger("moveToSidebar", this);
775		}
776
777		// update title position (don't update on init)
778		if (e && $title[0]) TweenLite.set($title[0], {x: $title._center(this.$back.length)});
779
780		// update dropmenu
781		var drop = this.drop;
782		this.drop = this.dropmenu && !smMobile && this.winWidth >= this.sidebarUnder;
783		if (drop != this.drop) {
784			if (this.drop) {
785				if (this.$head.length) {
786					this.$title.filter(":not(:first)").remove();
787					this.$title.length = 1;
788					TweenLite.set(this.$title[0], {css: {scale:1, x:this.$title.attr("class", "sm-title")._center(this.$back.length)}});
789				}
790				this.$lvls.children().attr("style", "display: none;");
791				this.opened = this.$lvls.children(":first").attr("style", "position: static;")[0];
792				this.prev = undefined;
793				this.$node
794					.on(this.dropEvent+".dm", "dt.parent", $.proxy(this, "onOpenDrop"))
795					.on(this.dropEvent+".dm", ">.sm-level", $.proxy(this, "onMouseEnterDrop"));
796				this.$drop
797					.on(this.dropEvent+".dm", "dt.parent", $.proxy(this, "onOpenDrop"))
798					.on(this.dropEvent+".dm", ".sm-level", $.proxy(this, "onMouseEnterDrop"));
799				if (this.dropEvent == "click") {
800					$body.on("click.dm", $.proxy(this, "closeAllDrops"));
801				} else {
802					this.$node
803						.on("mouseleave.dm", "dt.parent", $.proxy(this, "onCloseDrop"))
804						.on("mouseleave.dm", ">.sm-level", $.proxy(this, "onMouseLeaveDrop"));
805					this.$drop
806						.on("mouseleave.dm", "dt.parent", $.proxy(this, "onCloseDrop"))
807						.on("mouseleave.dm", ".sm-level", $.proxy(this, "onMouseLeaveDrop"));
808				}
809			} else {
810				this.$node.off(".dm");
811				this.$drop.off(".dm");
812				$body.off("click.dm");
813			}
814		}
815		// update background img pos
816		if (this.bg && this.bgX) {
817			var level = this.opened.children[0].attributes['class'].value.match(/\blevel(\d)\b/)[1];
818			this.$node[0].backgroundPosition = this.bgX * (level - 1) + "% 0";
819		}
820
821		if (this.popup) {
822			var maxH, mb = navigator.userAgent.match(/iPad/i) ? 25 : 5;
823			if (this.menuIconCorner) {  // fixed position
824				var off = this.$lvls.offset();
825									$body.on("click.dm", $.proxy(this, "closeAllDrops"));
826
827				maxH = (this.winHeight - (off.top - $win.scrollTop()) - mb);
828				this.opened.children[0].style.maxHeight = maxH - 2 * this.menuPadding + "px";
829				this.$lvls.css("maxHeight", maxH);
830				this.$node.css("maxWidth", this.winWidth - off.left - this.$button.width());
831			} else if (this.$node.css("visibility") != "hidden") { // module position
832				var btn = this.$button.offset(),
833						top = $win.scrollTop(),
834						originX = "0%";
835				if (this.winWidth/2 > btn.left || this.popup == 2) { // left
836					this.$node.css({
837						maxWidth: this.winWidth - btn.left - this.$button.width(),
838						left: btn.left,
839						right: ""
840					});
841				} else {  // right
842					originX = "100%";
843					this.$node.css({
844						maxWidth: btn.left,
845						left: "",
846						right: this.winWidth - btn.left - this.$button.width()
847					});
848				}
849				if (top + this.winHeight/2 > btn.top) { // top
850					TweenLite.set(this.$node[0], {transformOrigin: originX + " 0% 0", scaleX: 1, scaleY: 1});
851					maxH = this.winHeight - (btn.top - top) - this.$lvls.position().top - mb;
852					this.$node.css({top: btn.top, bottom: ""});
853				} else { // bottom
854					TweenLite.set(this.$node[0], {transformOrigin: originX + " 100% 0", scaleX: 1, scaleY: 1});
855					maxH = btn.top - top + this.$button.height() - this.$lvls.position().top;
856					this.$node.css({top: "", bottom: this.winHeight - btn.top - this.$button.height()});
857				}
858				this.opened.children[0].style.maxHeight = maxH - 2 * this.menuPadding + "px";
859				this.$lvls.css({maxHeight: maxH, height: $(this.opened.children).height() + 2 * this.menuPadding});
860			}
861		} else if (this.sidebar || this.sidebarUnder) {
862			// update button & sidebar
863			if (!this.sidebar && this.winWidth >= this.sidebarUnder && isInCont) {
864				this.$button.css("display", "none");
865				this.$node
866					.css({top: "", position: ""}) // mobile fix
867					.appendTo(this.$parent);
868				if (this.$cont.hasClass(this.openClass)) {
869					this.closeMenu();
870					this.onEndCloseMenu();
871				}
872				this.$node.trigger("moveToModulepos", this);
873			}
874			if (this.$node.css("display") == "block") { // if sidebar is open
875				this.$pusher.addClass("no-trans"); // disable transitions during resize
876				if (this.$pusher[0]) this.$pusher[0].offsetHeight; // trigger a reflow, flushing the CSS changes
877				this.$pusher.removeClass("no-trans");
878				// update menu height
879				if (this.sidebar || this.winWidth < this.sidebarUnder)
880					this.$lvls.css('maxHeight', window.innerHeight - this.$lvls.position().top);
881				else if (!this.treemenu)
882					this.$lvls.height($(this.opened.children).outerHeight() + 2 * this.menuPadding);
883				else
884					this.$lvls[0].style.height = "";
885			} else { // if sidebar is close
886				if (this.hideBurger && this.autoOpen == 2 && this.prevWinWidth < 768 && this.winWidth >= 768) this.openMenu();
887			}
888		} else if (this.overlay && $body.hasClass(this.openClass)) { // OVERLAY
889			var topHeight = 0;
890			this.$lvls.prevAll().each(function() { topHeight += $(this).outerHeight() });
891			this.$lvls.css('maxHeight', this.$overlay.outerHeight() - topHeight);
892		}
893
894		this.updateScrollbar();
895	},
896
897	onFilter: function(e) {
898		this.$filter.attr('value', this.$filter.val());
899		if (!e.target.value.length && $(this.opened.children).hasClass("sm-result")) this.onBack();
900		if (this.filterTimeout) this.filterTimeout = clearTimeout(this.filterTimeout);
901		if (e.target.value.length < this.filterMinChar) return;
902		this.filterTimeout = setTimeout($.proxy(this, "filter", e.target.value), this.filterDelay);
903	},
904
905	filter: function(keyword) {
906		keyword = keyword.trim().replace(/\s+/g, " ").replace(/([.?*+^$|{}()\\[\]])/g, "\\$1");
907		keyword = keyword.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
908		if (this.keyword == keyword) return;
909		if (!this.keyword) this.filterParent = this.opened;
910		this.keyword = keyword;
911		this.$filter.trigger("filter", [this, keyword]);
912		var i, $dt,
913				$result = this.$result.clone().appendTo(this.$lvls).children(),
914				submenu = $result[0].parentNode;
915		for (i = 0; i < this.$dt.length; i++) {
916			$dt = $(this.$dt[i]);
917			$filter_text = $dt.text().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
918			if ($filter_text.match( new RegExp(this.keyword, "i") )) {
919				var $clone = $dt.clone().removeClass("parent").addClass("notparent");
920				$clone[0].innerHTML = $clone[0].innerHTML.replace(new RegExp(">([^<]*)("+this.keyword+")([^>]*)<", "ig"), ">$1<strong>$2</strong>$3<");
921				$clone.appendTo($result);
922			}
923		}
924		var res = $result[0].children.length;
925		if (this.backItem && res == 1 || !res) {
926			var $no = $(this.opened).find("dt:first").clone()
927				.removeClass("parent opened").addClass("notparent").css("pointerEvents", "none");
928			$no.find("a").removeAttr("href").html(this.noResult);
929			$no.find(".desc, .sm-icon, .productnum").remove();
930			$no.appendTo($result);
931		}
932		submenu.parent = this.filterParent;
933		this.open(this.result, submenu);
934	},
935
936	onRedirect: function(e) {
937		e.stopPropagation();
938		if (this.disableClick) return e.preventDefault(); // disable click during swipe
939		var $a = e.currentTarget.tagName == "A" ? $(e.currentTarget) : $("a", e.currentTarget),
940				a = $a[0], event = $.Event("openMenuItem");
941		$a.trigger(event, this);
942		if (event.isDefaultPrevented()) return e.preventDefault();
943		var $scrollTo = $(), hash = a.href && a.href.split('#')[1];
944		if (hash && location.href.split('#')[0] == a.href.split('#')[0])
945			$scrollTo = hash == 'top' ? $body : $('#'+hash+', a[name="'+hash+'"]');
946		if ($scrollTo.length) { // scroll
947			e.preventDefault();
948			var $cont, cont, t, b;
949			if (this.$cont && this.$cont.hasClass(this.openClass) && this.effect > 0) { // sidebar
950				$cont = this.$content;
951				cont = {scroll: $cont.scrollTop()};
952				// t = $scrollTo.offset().top + cont.scroll - $cont.offset().top;
953				t = $.fn.menuspy.$elems.filter($scrollTo).data('menuspy').min;
954				b = this.$inner.outerHeight() - this.$content.height();
955			} else { // not sidebar
956				$cont = $win;
957				cont = {scroll: $cont.scrollTop()};
958				t = $scrollTo.offset().top;
959				b = $body.outerHeight() - this.winHeight;
960			}
961			t = Math.max(0, t - this.scrollOffset);
962			this.scroll = Math.round(t < b ? t : b);
963			var dur = this.scrollMinDur + Math.abs(cont.scroll - this.scroll) / this.scrollDistDiv;
964			TweenLite.to(cont, dur, {
965				scroll: this.scroll,
966				onUpdate: function onUpdateScroll() { $cont.scrollTop(cont.scroll) },
967				onComplete: this.popup && this.menuIconCorner && this.winWidth < this.hidePopupUnder ? function onEndScroll() {
968					if (!this.$button.eq(1).hasClass(this.closeClass)) return;
969					TweenLite.set(this.$button[0], {
970						x: this.menuIconX - this.$node.width(),
971						y: this.menuIconY
972					});
973					this.$button.eq(1).addClass("sm-hide");
974					if (!smMobile) this.$node.one("mouseenter", $.proxy(this, "onClickButton"));
975				}.bind(this) : function() { $a.closest('dt').addClass('active').siblings('.active').removeClass('active') }
976			});
977		} else {  // open url
978			if (!(smMobile && this.popup)) // fix for mobile popup menu-item opening
979				if (a == e.target.parentNode || a == e.target) return;
980			if (!a.click) {
981				var event = document.createEvent("HTMLEvents");
982				event.initEvent("click", false, true);
983				a.dispatchEvent(event);
984			} else a.click();
985		}
986	},
987
988	onOpenDrop: function(e) {
989		if (this.sidebar && !$body.hasClass(this.openClass)) return;
990		var item = e.currentTarget,
991				openTween = item.parentNode.parentNode.openTween,
992				closeTween = item.parentNode.parentNode.closeTween,
993				items = item.parentNode.children, i;
994		for (i = 0; i < items.length; i++)
995			if (items[i] != item && items[i].menu && items[i].menu.style.display == "block")
996				{ this.closeDrop(items[i].menu); break; }
997		item.menu.closeTimeout = clearTimeout(item.menu.closeTimeout);
998		if (openTween && openTween.isActive())
999			openTween.vars.onComplete = $.proxy(this, "openDrop", item);
1000		else if (closeTween && closeTween.isActive())
1001			closeTween.vars.onReverseComplete = $.proxy(this, "openDrop", item);
1002		else
1003			this.openDrop(item);
1004	},
1005
1006	onCloseDrop: function(e) {
1007		var item = e.currentTarget,
1008				openTween = item.parentNode.parentNode.openTween,
1009				closeTween = item.parentNode.parentNode.closeTween;
1010		if (openTween && openTween.vars.onComplete)
1011			delete openTween.vars.onComplete;
1012		else if (closeTween && closeTween.vars.onReverseComplete)
1013			delete closeTween.vars.onReverseComplete;
1014		else
1015			item.menu.closeTimeout = setTimeout($.proxy(this, "closeDrop", item.menu), this.anim.outDur * 500 || 200);
1016	},
1017
1018	onMouseEnterDrop: function(e) {
1019		if (this.sidebar && !$body.hasClass(this.openClass)) return;
1020		var menu = e.currentTarget,
1021				item = menu.parentItem;
1022		menu.style.zIndex = 99;
1023		do {
1024			if (menu.closeTween && menu.closeTween.isActive()) menu.closeTween.reverse();
1025			menu.closeTimeout = clearTimeout(menu.closeTimeout);
1026			menu = menu.parent;
1027			$(item).addClass("hover");
1028			item = menu.parentItem;
1029		} while ("closeTimeout" in menu);
1030	},
1031
1032	onMouseLeaveDrop: function(e) {
1033		var menu = e.currentTarget,
1034				item = menu.parentItem,
1035				items = menu.children[0].children,
1036				i = items.length, t = 1;
1037		while (i--)
1038			if (items[i].menu && items[i].menu.closeTimeout) {t++; break}
1039		do {
1040			menu.closeTimeout = setTimeout($.proxy(this, "closeDrop", menu), (this.anim.outDur * 500 || 200) * t++);
1041			menu = menu.parent;
1042			$(item).removeClass("hover");
1043			item = menu.parentItem;
1044		} while ("closeTimeout" in menu);
1045	},
1046
1047	closeAllDrops: function() {
1048		var $menu = this[this.sidebar ? "$node" : "$drop"].children(".sm-level[style*=block]:last");
1049		if ($menu.length) this.onMouseLeaveDrop({currentTarget: $menu[0]});
1050	},
1051
1052	openDrop: function(item) {
1053		var submenu = item.menu,
1054				openTween = item.parentNode.parentNode.openTween,
1055				closeTween = item.parentNode.parentNode.closeTween;
1056		if (openTween) delete openTween.vars.onComplete;
1057		if (closeTween) delete closeTween.vars.onReverseComplete;
1058		// replace nodes to be on top (zIndex)
1059		if (this.sidebar && submenu.parentNode != this.$node[0])
1060			$(submenu).appendTo(this.$node);
1061		if (!this.sidebar && submenu.parentNode != this.$drop[0])
1062			$(submenu).appendTo(this.$drop);
1063
1064		var p = $(item).offset(),
1065				w = $(item).outerWidth(),
1066				h = $(submenu).outerHeight(),
1067				t = $win.scrollTop(),
1068				m = t + this.winHeight - p.top - h;
1069		if (submenu.parent.parentNode == this.$lvls[0])
1070			this.dropdir = p.left + w/2 - this.winWidth/2 < 0 ? 1 : -1;
1071
1072		if (this.dropdir < 0) {
1073			p.right = $win.outerWidth() - p.left; // don't use this.winWidth
1074			if (this.sidebar && p.right < this.width) p.right = this.width - this.menuPadding;
1075			submenu.style.right = p.right + this.menuPadding + this.dropspace + "px";
1076			submenu.style.left = "";
1077		} else {
1078			if (p.left < 0) p.left = 0;
1079			submenu.style.left = w + p.left + this.menuPadding + this.dropspace + "px";
1080			submenu.style.right = "";
1081		}
1082
1083		if (this.dropFullHeight) {
1084			p.top = t;
1085			submenu.style.height = "100vh";
1086		} else if (m < 0) { // out of screen
1087			var ih = $(item).outerHeight(), y = p.top;
1088			p.top += Math.floor(m / ih) * ih;
1089			if (p.top + h <= y) p.top += ih;
1090			if (p.top < t) p.top = t;
1091			submenu.children[0].style.height = h >= this.winHeight ? "100vh" : "";
1092		}
1093		if (this.effect < 0) p.top -= $win.scrollTop(); // reduce_width bugfix (#21)
1094		submenu.style.top = p.top - (this.dropFullHeight ? 0 : this.menuPadding) + "px";
1095		submenu.style.zIndex = 99;
1096
1097		this.anim.inCSS.x = this.anim.inUnitX == "%" ? this.anim.inX * this.dropwidth / 100 : this.anim.inX;
1098		this.anim.inCSS.x *= this.dropdir;
1099		this.anim.inCSS.rotationY = this.dropdir * this.anim.inRotationY;
1100		TweenLite.set(submenu, {
1101			transformPerspective: this.anim.perspective,
1102			transformOrigin: Hlpr.calcOrigin(this.anim.inOrigin, this.dropwidth, this.dropdir)
1103		});
1104		var animTo = {
1105			overwrite: 1,
1106			css: this.baseCSS,
1107			ease: this.anim.inEase
1108		};
1109		if (submenu.style.display == "none") {
1110			submenu.openTween = TweenLite.fromTo(submenu, this.anim.inDur, {css: this.anim.inCSS}, animTo);
1111			// menu-item anim
1112			if (this.miAnim) {
1113				this.miCSS.x = this.miUnitX == "%" ? this.miX * w / 100 : this.miX;
1114				this.miCSS.x *= this.dropdir;
1115				this.miCSS.rotationY = this.miRotationY * this.dropdir;
1116				if (this.miShift)
1117					TweenMax.staggerFromTo($(">dl>dt", submenu), this.miDur, {css: this.miCSS}, {overwrite: 1, css: this.baseCSS, ease: this.miEase}, this.miShift);
1118				else
1119					TweenLite.fromTo(submenu.children[0], this.miDur, {css: this.miCSS}, {overwrite: 1, css: this.baseCSS, ease: this.miEase});
1120			}
1121			// icons anim
1122			if (this.iconAnim && !smMobile)
1123				TweenLite.fromTo($(".sm-icon img", submenu), 0.5, {scale: 0, opacity: 0}, {css: {scale: 1, opacity: 1}, ease: Back.easeOut, delay: 0.66*this.dur});
1124		} else {
1125			submenu.openTween = TweenLite.to(submenu, this.anim.inDur, animTo);
1126		}
1127
1128		submenu.style.display = "block";
1129		this.updateScrollbar(submenu);
1130		$(item).trigger("openParentItem", this);
1131	},
1132
1133	closeDrop: function(submenu) {
1134		if (submenu.style.display == 'none') return;
1135		submenu.style.zIndex = "";
1136		this.anim.outCSS.x = this.anim.outUnitX == "%" ? this.anim.outX * this.dropwidth / 100 : this.anim.outX;
1137		this.anim.outCSS.rotationY = this.dropdir * this.anim.outRotationY;
1138		TweenLite.set(submenu, {transformOrigin: Hlpr.calcOrigin(this.anim.outOrigin, this.dropwidth, this.dropdir)});
1139		submenu.closeTween = TweenLite.to(submenu, this.anim.outDur, {
1140			overwrite: 1,
1141			css: this.anim.outCSS,
1142			ease: this.anim.outEase,
1143			onComplete: Target.hide
1144		});
1145	},
1146
1147	onClickTree: function(e) {
1148		e.stopPropagation();
1149		e.preventDefault();
1150		//if (smMobile && e.type == 'click') return;
1151		var $item = $(e.currentTarget);
1152		this[$item.hasClass("opened") ? "closeTree" : "openTree"]($item);
1153	},
1154
1155	openTree: function($item) {
1156		var dur = this.dur;
1157		var $dd = $item.addClass("opened").next();
1158		if (this.popup) this.$lvls.height("auto");
1159		if (this.accordion) {
1160			$item.siblings("dt.opened").removeClass("opened");
1161			$dd.siblings("dd.opened").removeClass("opened").slideUp({ easing: this.outEase, duration: dur * 1000 });
1162		}
1163		$dd.slideDown({
1164			queue: false,
1165			easing: this.inEase,
1166			duration: dur * 1000,
1167			progress: this.bindUpdateScrollbar,
1168			complete: function() { $dd.attr('style', 'display: block;') }
1169		}).addClass('opened');
1170
1171		if (this.miAnim) {
1172			this.miCSS.x = this.miUnitX == "%" ? this.miX * this.$lvls.outerWidth() / 100 : this.miX;
1173			if (this.miShift)
1174				dur = new TimelineLite()
1175					.staggerFromTo($dd.find("dt").not("dd:not(.opened) dt"), this.miDur, {css: this.miCSS}, {overwrite: 1, css: this.baseCSS, ease: this.miEase}, this.miShift)
1176					.totalDuration();
1177			else
1178				TweenLite.fromTo($dd.find("dl").not("dd:not(.opened) dl"), this.miDur, {css: this.miCSS}, {overwrite: 1, css: this.baseCSS, ease: this.miEase});
1179		}
1180
1181		// icons anim
1182		if (this.iconAnim && !smMobile)
1183			TweenLite.fromTo($dd.find(".sm-icon img"), 0.5, {scale: 0, opacity: 0}, {css: {scale: 1, opacity: 1}, ease: Back.easeOut, delay: 0.66*this.dur});
1184
1185		$item.trigger("openParentItem", this);
1186	},
1187
1188	closeTree: function($item) {
1189		var $dd = $item.removeClass("opened").next();
1190		$dd.removeClass('opened').slideUp({
1191			queue: false,
1192			easing: this.outEase,
1193			duration: this.dur * 1000,
1194			progress: this.bindUpdateScrollbar,
1195			complete: function() { $dd.attr('style', '') }
1196		});
1197	},
1198
1199	onOpen: function(e) {
1200		if (this.drop) return;
1201		if (e.type == "touchend") e.preventDefault();
1202		if (e.currentTarget.parentNode.parentNode == this.prev) return;
1203		if (this.disableClick) return; // disable click during swipe
1204		var title = $(e.currentTarget).find("a").html().replace(/<.*>/, "");
1205		this.open(title, e.currentTarget.menu);
1206	},
1207
1208	open: function(title, submenu) {
1209		if (submenu.parentNode != this.$lvls[0])
1210			$(submenu).css({top:'', left:'', right:''}).appendTo(this.$lvls);
1211		var w = this.$lvls.outerWidth(),
1212				tl = new TimelineLite({paused: true, autoRemoveChildren: true, onComplete: $.proxy(this, "onEndSlide")});
1213		TweenLite.set(this.opened, {transformOrigin: Hlpr.calcOrigin(this.outOrigin, w)});
1214		TweenLite.set(submenu, {transformOrigin: Hlpr.calcOrigin(this.inOrigin, w)});
1215
1216		this.inCSS.x = this.inUnitX == "%" ? this.inX * w / 100 : this.inX;
1217		this.outCSS.x = this.outUnitX == "%" ? this.outX * w / 100 : this.outX;
1218
1219		// submenu anims
1220		tl.add(TweenLite.to(this.opened, this.dur, {css: this.outCSS, ease: this.outEase, onComplete: Target.scrollUp}), 0);
1221		tl.add(TweenLite.fromTo(submenu, this.dur, {css: this.inCSS}, {css: this.baseCSS, ease: this.inEase}), 0);
1222
1223		this.opened.style.zIndex = "";
1224		submenu.style.zIndex = 99;
1225		submenu.style.display = "block";
1226		if (!this.sidebar && this.winWidth >= this.sidebarUnder) {
1227			if (this.popup) submenu.children[0].style.maxHeight = this.$lvls[0].style.maxHeight;
1228			var h = $(submenu.children[0]).outerHeight() + 2 * this.menuPadding;
1229			tl.add(TweenLite.to(this.$lvls[0], this.dur, {css: {height: h}}), 0);
1230		}
1231
1232		if (!$(this.opened.children).hasClass("sm-result")) { // if filtering was started at search result, don't run it
1233			// header anims
1234			if (this.$head.length) {
1235				var $title = $('<span class="sm-title" title="'+title+'">'+title+'</span>').appendTo(this.$head),
1236						i = this.$title.length;
1237				if (!this.$title[0].style.position) // init title position
1238					TweenLite.set(this.$title[0], {css: {position: "absolute", x: this.$title._center(this.$back.length)}});
1239				if (this.$back.length) { // clean theme
1240					if (i == 1)
1241						tl.add(TweenLite.to(this.$back[0], this.dur, {css: {opacity: 1, display: 'block'}}), 0);
1242					if (this.$title[--i]) this.$title[i].style.position = "absolute",
1243						tl.add(TweenLite.to(this.$title[i], 2*this.dur, {css: {x: -w*0.6, opacity: -1, scale: 0}}), 0);
1244				} else { // flat theme
1245					this.$title.attr("class", "sm-back");
1246					if (this.$title[--i]) this.$title[i].style.position = "absolute",
1247						tl.add(TweenLite.to(this.$title[i], this.dur, {css: {x: 0, scale: backScale}}), 0);
1248					if (this.$title[--i])
1249						tl.add(TweenLite.to(this.$title[i], this.dur, {css: {x: -w*0.6}, ease: Linear.easeNone}), 0);
1250				}
1251				tl.add(TweenLite.fromTo($title, this.dur, {x: w}, {css: {x: $title._center(this.$back.length)}}), 0);
1252				this.$title.push($title[0]);
1253			}
1254
1255			// bg anim
1256			if (this.bg && this.bgX) {
1257				var level = this.opened.children[0].attributes['class'].value.match(/\blevel(\d)\b/)[1];
1258				tl.add(TweenLite.to(this.$node[0], this.dur, {css: {backgroundPosition: this.bgX * level + "% 0"}}), 0);
1259			}
1260		}
1261		// menu-item anim
1262		if (this.miAnim) {
1263			this.miCSS.x = this.miUnitX == "%" ? this.miX * w / 100 : this.miX;
1264			if (this.miShift)
1265				tl.staggerFromTo($(">dl>dt", submenu), this.miDur, {css: this.miCSS}, {css: this.baseCSS, ease: this.miEase}, this.miShift, 0);
1266			else
1267				tl.fromTo(submenu.children[0], this.miDur, {css: this.miCSS}, {css: this.baseCSS, ease: this.miEase}, 0);
1268		}
1269		// icons anim
1270		if (this.iconAnim && !smMobile)
1271			tl.add(TweenLite.fromTo($(".sm-icon img", submenu), 0.5, {scale: 0, opacity: 0}, {css: {scale: 1, opacity: 1}, ease: Back.easeOut}), 0.66*this.dur);
1272		this.openTL = tl;
1273
1274		if (this.backTL && this.backTL._first) delete this.backTL._first.vars.onComplete; // don't run Target.scrollUp
1275		TweenLite.set(this.$lvls[0], {perspective: this.perspective}); // blur fix for retina
1276		this.openTL.play();
1277		this.prev = this.opened;
1278		this.opened = submenu;
1279		this.updateScrollbar();
1280
1281		// don't trigger event when submenu is search result
1282		if (!$(submenu.children[0]).hasClass("sm-result")) $(submenu.parentItem).trigger("openParentItem", [this, title]);
1283	},
1284
1285	onResetFilter: function() {
1286		this.$filter.attr("value", "");
1287		if ($(this.opened.children).hasClass("sm-result")) this.onBack();
1288		this.$filter[0].focus();
1289	},
1290
1291	onBack: function(e) {
1292		if (e && e.type == "touchend") e.preventDefault();
1293		if (!this.opened.parent) return;
1294		this.initBackTimeline();
1295		this.back();
1296	},
1297
1298	back: function() {
1299		if (this.openTL && this.openTL._first) delete this.openTL._first.vars.onComplete; // don't run Target.scrollUp
1300		TweenLite.set(this.$lvls[0], {perspective: this.perspective}); // blur fix for retina
1301		this.backTL.resume();
1302		this.prev = this.opened;
1303		this.opened = this.opened.parent;
1304		this.updateScrollbar();
1305
1306		this.$title.length--;
1307		this.$filter.attr("value", "");
1308		this.keyword = "";
1309		$(this.opened).trigger("back", this);
1310	},
1311
1312	initBackTimeline: function() {
1313		var w = this.$lvls.addClass("sm-swipe").outerWidth(),
1314				tl = new TimelineLite({
1315					paused: true,
1316					autoRemoveChildren: true,
1317					onComplete: $.proxy(this, "onEndSlide"),
1318					onReverseComplete: function() { this.onEndSlide(), this.opened.parent.style.display = "none" }.bind(this)
1319				});
1320
1321		this.inCSS.x = this.inUnitX == "%" ? this.inX * w / 100 : this.inX;
1322
1323		TweenLite.set(this.opened, {transformOrigin: Hlpr.calcOrigin(this.inOrigin, w)});
1324		TweenLite.set(this.opened.parent, {transformOrigin: Hlpr.calcOrigin(this.outOrigin, w)});
1325
1326		// submenu anims
1327		tl.add(TweenLite.to(this.opened, this.dur, {css: this.inCSS, onComplete: Target.scrollUp}), 0);
1328		tl.add(TweenLite.to(this.opened.parent, this.dur, {css: this.baseCSS, ease: this.inEase}), 0);
1329
1330		this.opened.parent.style.display = "block";
1331		if (!this.sidebar && this.winWidth >= this.sidebarUnder) {
1332			if (this.popup) this.opened.parent.children[0].style.maxHeight = this.$lvls[0].style.maxHeight;
1333			tl.add(TweenLite.to(this.$lvls[0], this.dur, {
1334				css: {height: $(this.opened.parent.children[0]).outerHeight() + 2 * this.menuPadding},
1335				onComplete: this.prev.parentNode.children[0] == this.prev ? Target.autoHeight : undefined // auto height first level
1336			}), 0);
1337		}
1338
1339		// header anims
1340		if (this.$head.length) {
1341			var i = this.$title.length;
1342			tl.add(TweenLite.to(this.$title[--i], this.dur, {css: {x: w, opacity: 0}, onComplete: Target.remove}), 0);
1343			if (this.$back.length) { // clean theme
1344				if (i == 1) tl.add(TweenLite.to(this.$back[0], this.dur, {css: {opacity: 0, display: 'none'}}
1344), 0);
1345				tl.add(TweenLite.to(this.$title[--i], this.dur, {css: {x: $(this.$title[i])._center(true), opacity: 1, scale: 1}}), 0);
1346			} else { // flat theme
1347				tl.add(TweenLite.to(this.$title[--i], this.dur, {
1348					css: {x: $(this.$title[i])._center(), scale: 1, maxWidth: i ? this.titleWidth : "100%"},
1349					onComplete: Target.autoMaxWidth
1350				}), 0);
1351				if (this.$title[--i]) tl.add(TweenLite.to(this.$title[i], this.dur, {css: {x: 0}}), 0);
1352			}
1353		}
1354
1355		// bg anim
1356		if (this.bg && this.bgX) {
1357			var level = this.opened.children[0].attributes['class'].value.match(/\blevel(\d)\b/);
1358			level = level ? level[1] - 2 : 0;
1359			tl.add(TweenLite.to(this.$node[0], this.dur, {css: {backgroundPosition: this.bgX * level + "% 0"}}), 0);
1360		}
1361
1362		this.backTL = tl;
1363	},
1364
1365	onEndSlide: function() {
1366		// blur fix for retina
1367		$(this.opened).css({WebkitTransform: "", MozTransform: "", msTransform: "", transform: ""});
1368		this.$lvls.css({WebkitPerspective: "", MozPerspective: "", perspective: ""}).removeClass("sm-swipe");
1369		this.disableClick = false; // enable click after swipe
1370	},
1371
1372	updateScrollbar: function(menu) {
1373		if (smMobile) return;
1374		var $dl = $( (menu || this.opened).children[0] );
1375		$dl.perfectScrollbar($dl.data("perfectScrollbar") ? "update" : undefined);
1376	}
1377
1378};
1379
1380// depreciated functions
1381VerticalSlideMenu.prototype.openPopup = VerticalSlideMenu.prototype.openMenu;
1382VerticalSlideMenu.prototype.closePopup = VerticalSlideMenu.prototype.closeMenu;
1383VerticalSlideMenu.prototype.openSidebar = VerticalSlideMenu.prototype.openMenu;
1384VerticalSlideMenu.prototype.closeSidebar = VerticalSlideMenu.prototype.closeMenu;
1385
1386$html.on('click.sm', '.sm-toggle', function(e) {
1387	var id = this.className.match(/\bsm-(\d+)\b/);
1388	id = id ? id[1] : ($('.sm-menu:first').attr('id') || '').split('_')[1];
1389	var sm = window['sm'+id];
1390	if (sm) {
1391		e.preventDefault();
1392		sm.$cont.hasClass(sm.openClass) ? sm.closeMenu(e) : sm.openMenu(e);
1393	}
1394});
1395
1396(function initMenuSpy() {
1397	var $elems = $();
1398	$win
1399		.on("load.menuspy resize.menuspy", onResize)
1400		.on("scroll.menuspy", onScroll)
1401	;
1402
1403	function onScroll() {
1404		if (!$elems.length) return;
1405		var y = $win.scrollTop();
1406		$elems.each(function(i) {
1407			var $elem = $(this);
1408			var o = $elem.data('menuspy');
1409			if (y >= o.min && y < o.max) {
1410				if (!o.inside) {
1411					o.inside = true;
1412					$elem.trigger("scrollEnter.menuspy", {scrollTop: y, enters: ++o.enters, leaves: o.leaves});
1413				}
1414			} else if (o.inside) {
1415				o.inside = false;
1416				$elem.trigger("scrollLeave.menuspy", {scrollTop: y, enters: o.enters, leaves: ++o.leaves});
1417			}
1418		});
1419	}
1420
1421	function onResize() {
1422		$elems.each(function() {
1423			var $elem = $(this);
1424			var o = $elem.data("menuspy");
1425			o.min = $elem.offset().top;
1426			o.max = $elem.outerHeight() + o.min;
1427		});
1428		onScroll();
1429	}
1430
1431	$.fn.menuspy = function(options) {
1432		var defaults = {
1433			offset: 0,
1434			enters: 0,
1435			leaves: 0,
1436			inside: false
1437		};
1438		return this.each(function() {
1439			var $elem = $(this);
1440			var top = $elem.offset().top;
1441			$elem.data("menuspy", $.extend({
1442				min: top,
1443				max: top + $elem.outerHeight(),
1444			}, defaults, options));
1445			$elems.filter(this).length || $elems.push(this);
1446		});
1447	};
1448
1449	$.fn.menuspy.$elems = $elems;
1450})();
1451
1452})(window.jq183||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.