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