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.