1// page init 2;(function () { 3 'use strict'; 4 var requestAnimationFrame = window.requestAnimationFrame || 5 window.mozRequestAnimationFrame || 6 window.webkitRequestAnimationFrame || 7 window.msRequestAnimationFrame || 8 function (f) { 9 return setTimeout(f, 17) 10 }; 11 12 var cancelAnimationFrame = window.cancelAnimationFrame || 13 window.mozCancelAnimationFrame || 14 function (requestId) { 15 clearTimeout(requestId); 16 }; 17 /** 18 * Property Decorator constant 19 * @param value 20 * @returns {{enumerable: boolean, value: *}} 21 */ 22 23 function constant(value) { 24 return { 25 enumerable: true, 26 value: value 27 }; 28 } 29 30 function mutable(value) { 31 return { 32 enumerable: true, 33 writable: true, 34 value: value 35 }; 36 } 37 38 /** 39 * Property Decorator Get/Set 40 * @param getter 41 * @param setter 42 * @returns {{enumerable: boolean, get: *, set: *}} 43 */ 44 function getterSetter(getter, setter) { 45 return { 46 enumerable: true, 47 get: getter, 48 set: setter 49 }; 50 } 51 52 function abstractMethod() { 53 throw new Error('This method has not been configured!'); 54 } 55 56 /** 57 * @param $ {JQueryStatic} 58 */ 59 jQuery(document).ready(function ($) { 60 var $body = $('body'), 61 $window = $(window), 62 $noPlace = $('<span></span>'), 63 noPlace = $noPlace.get(0); 64 var $contactForm = $('.contactform').first(); 65 66 function throttleResize(callback) { 67 var id = 0; 68 69 function throttle() { 70 cancelAnimationFrame(id); 71 id = requestAnimationFrame(callback); 72 } 73 return { 74 start: function () { 75 $window.on('resize', throttle); 76 }, 77 stop: function () { 78 $window.off('resize', throttle); 79 cancelAnimationFrame(id); 80 id = 0; 81 } 82 }; 83 } 84 85 function scrollTo($el) { 86 return function () { 87 var $moduleSticky = $('.ple_module_sticky'), moduleStickyHeight = 0; 88 if($moduleSticky.length) $moduleSticky.each(function() { moduleStickyHeight += $(this).outerHeight(true); }); 89 90 $('html, body').animate({ 91 scrollTop: $el.offset().top - moduleStickyHeight 92 }, 'slow'); 93 }; 94 } 95 96 function getPLESection($target) { 97 return $target.closest('.section[data-control="section"]') 98 } 99 100 /** 101 * @param {HTMLElement} child 102 * @param {Element} container 103 * @returns {Number} 104 */ 105 function absoluteOffsetFrom(child, container) { 106 var total = 0; 107 for ( 108 var currentChild = child; currentChild && currentChild !== container && container.contains(currentChild); currentChild = currentChild.offsetParent 109 ) { 110 total += currentChild.offsetTop; 111 } 112 return total; 113 } 114 115 function positionOnSection($menuBlock) { 116 var menuBlock = $menuBlock.get(0), 117 $sectionBlock = getPLESection($menuBlock), 118 sectionBlock = $sectionBlock.get(0) || noPlace; 119 return function () { 120 var relativeParent = menuBlock.offsetParent; 121 $menuBlock.css({ 122 width: $body.width() + 'px', 123 left: -relativeParent.getBoundingClientRect().left, 124 top: $sectionBlock.outerHeight() - absoluteOffsetFrom(relativeParent, sectionBlock) 125 }); 126 } 127 } 128 129 /** 130 * use common script for group menu option 131 * replace the global scope nav menu variables 132 * prepare the menu and add the more button 133 */ 134 function groupNavMoreButton($el) { 135 menuHolder = $el; 136 menu = $el.find('.more_nav'); 137 currentMenu = menu; 138 currentMenuWidth = menuHolder.outerWidth(true); 139 menuChildrenCount = currentMenu.children().length; 140 origMenuContent = ''; 141 moreButton = $('<li class="navigation__item navigation__item--default has-drop-down more"> <a href="#" class="navigation__item--link">More</a> <ul class="navigation__list navigation__list--sub"></ul> </li>'); 142 allMenus = []; 143 menuPrepare(); 144 addMoreButton(); 145 } 146 147 (function NavigationLayoutA() { 148 var expandedClass = 'navigation-open', 149 megaMenuClass = 'mega-menu', 150 expandedSelector = '.' + expandedClass; 151 $('.component.navigation .navigation-a').each(function (index, navEl) { 152 new DesktopMenu(navEl); 153 }); 154 155 /** 156 * @param element {Element} 157 * @constructor 158 */ 159 function DesktopMenu(element) { 160 var dis = this; 161 var watchingBody = false; 162 this.el = element; 163 /** 164 * @type {jQuery} 165 */ 166 this.$el = $(element); 167 // call groupNavMoreButton() before manipulation 168 this.$el.data("more-button") === 1 ? groupNavMoreButton(this.$el) : ''; 169 this.$ul = this.$el.children('ul:first-child');
170 var $navItems = this.$el.children('ul').children('li'); 171 var focusTree = { 172 prev: function ($a) { 173 $a.parent('li').prev('li').children('a').focus(); 174 }, 175 next: function ($a) { 176 $a.parent('li').next('li').children('a').focus(); 177 }, 178 parent: function ($a) { 179 $a.parent('li').parent('ul').siblings('a').focus(); 180 }, 181 child: function ($a) { 182 $a.siblings('ul').find('> li:first-child > a:first-child').focus(); 183 } 184 }; 185 186 function isTopRow(el) { 187 return dis.$ul.children('li').children('a').is(el); 188 } 189 var keyboardNav = { 190 ArrowDown: function (event, $a) { 191 if (isTopRow(event.target)) { 192 focusTree.child($a); 193 } else { 194 focusTree.next($a); 195 } 196 }, 197 ArrowUp: function (event, $a) { 198 if (!isTopRow(event.target)) { 199 focusTree.prev($a); 200 } 201 }, 202 ArrowLeft: function (event, $a) { 203 if (isTopRow(event.target)) { 204 focusTree.prev($a); 205 } else { 206 focusTree.parent($a); 207 } 208 }, 209 ArrowRight: function (event, $a) { 210 if (isTopRow(event.target)) { 211 focusTree.next($a) 212 } else { 213 focusTree.child($a); 214 } 215 } 216 }; 217 218 219 this.$el.on('keydown', '.navigation__item--link:focus', function kbEventRouter(event) { 220 keyboardNav.hasOwnProperty(event.key) && keyboardNav[event.key](event, $(event.target)); 221 }); 222 223 224 this.subMenuR = $navItems.toArray().map(function (li) { 225 return new((li.classList.contains(megaMenuClass)) ? 226 DesktopSubMenuMegaMenu : 227 DesktopSubMenuItemDefault 228 )(li, dis); 229 }); 230 231 function offClick(evt) { 232 if (!dis.el.contains(evt.target)) { 233 dis.closeAll(); 234 } 235 } 236 237 this.watchAfterOpen = function () { 238 if (!watchingBody) { 239 watchingBody = true; 240 $body.on('click touchstart', offClick); 241 } 242 }; 243 244 this.ignoreAfterClose = function () { 245 if (watchingBody) { 246 watchingBody = false; 247 $body.off('click touchstart', offClick); 248 } 249 }; 250 251 } 252 253 Object.defineProperties(DesktopMenu, {}); 254 Object.defineProperties(DesktopMenu.prototype, { 255 hasFocus: getterSetter(function () { 256 return Boolean(this.$el.find('a:focus').length); 257 }), 258 expandedClass: constant(expandedClass), 259 expandedSelector: constant(expandedSelector), 260 markOpened: constant(function () { 261 this.watchAfterOpen(); 262 }), 263 closeAll: constant(function () { 264 this.$el.find(this.expandedSelector).removeClass(this.expandedClass); 265 this.ignoreAfterClose(); 266 }), 267 watchAfterOpen: mutable(abstractMethod), 268 ignoreAfterClose: mutable(abstractMethod) 269 }); 270 271 272 /** 273 * @param element {HTMLLIElement} 274 * @param root {DesktopMenu} 275 * @param parent {=DesktopSubMenuItem} 276 * @constructor 277 */ 278 function DesktopSubMenuItemDefault(element, root, parent) { 279 var dis = this; 280 this.root = root; 281 this.parent = parent || null; 282 this.el = element; 283 this.$el = $(element); 284 this.$a = this.$el.children('a'); 285 this.$ul = this.$el.children('ul'); 286 287 288 this.$a.on('focus', function () { 289 dis.clearExpand(); 290 dis.expand(); 291 }); 292 this.$ul.find('a').last().on('blur', function () { 293 dis.contract(); 294 }); 295 296 if (this.$ul.length) { 297 this.$el.hover( 298 function over() { 299 dis.expand(); 300 }, 301 function out(evt) { 302 dis.contract(); 303 } 304 ); 305 this.$a.on('touchend', function (evt) { 306 if (!dis.isExpanded) { 307 dis.clearExpand(); 308 dis.expand(); 309 return false; 310 } 311 }); 312 313 this.subMenuR = this.$ul.children('li').toArray().map(function (li) { 314 return new DesktopSubMenuItemDefault(li, root, dis); 315 }); 316 } else { 317 this.subMenuR = []; 318 } 319 320 } 321 Object.defineProperties(DesktopSubMenuItemDefault, {}); 322 Object.defineProperties(DesktopSubMenuItemDefault.prototype, { 323 expandedClass: constant(expandedClass), 324 expandedSelector: constant(expandedSelector), 325 isExpanded: getterSetter(function () { 326 return this.el.classList.contains(this.expandedClass); 327 }), 328 expand: constant(function () {
329 this.root.markOpened(); 330 // Set submenu visible if there is one 331 this.$el.addClass(this.expandedClass); 332 // echo up the tree 333 this.$a.parents('li').addClass(this.expandedClass); 334 }), 335 contract: constant(function () { 336 this.$el.removeClass(this.expandedClass) 337 .find(this.expandedSelector).removeClass(this.expandedClass); 338 }), 339 clearExpand: constant(function () { 340 this.root.closeAll(); 341 }) 342 }); 343 344 /** 345 * @param element {HTMLLIElement} 346 * @param root {DesktopMenu} 347 * @param parent {=DesktopSubMenuItem} 348 * @constructor 349 */ 350 function DesktopSubMenuMegaMenu(element, root, parent) { 351 var dis = this; 352 this.root = root; 353 this.parent = parent || null; 354 this.el = element; 355 this.$el = $(element); 356 this.$a = this.$el.children('a'); 357 this.$ul = this.$el.children('ul'); 358 this.subMenuR = []; 359 360 this.resizeWatcher = throttleResize(function () { 361 dis.correctPosition(); 362 }); 363 364 function revealForKBNav() { 365 dis.clearExpand(); 366 dis.expand(); 367 } 368 this.$a.on('focus', revealForKBNav); 369 370 this.$ul.on('focus', 'a', revealForKBNav); 371 this.$ul.find('a').last().on('blur', function () { 372 dis.contract(); 373 }); 374 375 376 if (this.$ul.length) { 377 this.$el.hover( 378 function over() { 379 dis.expand(); 380 }, 381 function out(evt) { 382 dis.contract(); 383 } 384 ); 385 this.$a.on('touchend', function (evt) { 386 if (!dis.isExpanded) { 387 dis.clearExpand(); 388 dis.expand(); 389 return false; 390 } 391 }); 392 } 393 394 } 395 396 Object.defineProperties(DesktopSubMenuMegaMenu, {}); 397 Object.defineProperties(DesktopSubMenuMegaMenu.prototype, { 398 expandedClass: constant(expandedClass), 399 expandedSelector: constant(expandedSelector), 400 isExpanded: getterSetter(function () { 401 return this.el.classList.contains(this.expandedClass); 402 }), 403 expand: constant(function () { 404 this.root.markOpened(); 405 // Set submenu visible if there is one 406 this.$el.addClass(this.expandedClass); 407 this.correctPosition(); 408 this.resizeWatcher.start(); 409 }), 410 correctPosition: constant(function () { 411 this.$ul.css({ 412 width: $body.outerWidth() + 'px', 413 left: -(this.$ul.offsetParent().get(0) || $body).getBoundingClientRect().left 414 }); 415 }), 416 contract: constant(function () { 417 this.$el.removeClass(this.expandedClass); 418 this.resizeWatcher.stop(); 419 }), 420 clearExpand: constant(function () { 421 this.root.closeAll(); 422 }), 423 resizeWatcher: mutable(null) 424 }); 425 426 })(); 427 428 (function NavigationLayoutB() { 429 function NavigationB(nav) { 430 var watchingBody = false, 431 dis = this; 432 var resizeWatcher = throttleResize(function () { 433 dis.correctPosition(); 434 }); 435 436 /** 437 * @type {jQuery} 438 */ 439 this.$el = $(nav); 440 this.$button = this.$el.children(NavigationB.buttonSelector).first(); 441 this.button = this.$button.get(0); 442 this.$buttonIcon = this.$button.children(NavigationB.buttonIconSelector).first(); 443 this.$buttonText = this.$button.children(NavigationB.buttonTextSelector).first(); 444 this.$navMenu = this.$el.children(NavigationB.navMenuSelector).first(); 445 446 function offClick(evt) { 447 if (!nav.contains(evt.target)) { 448 dis.close(); 449 } 450 } 451 452 453 this.$button.on('click', function (evt) { 454 dis.toggle(); 455 }); 456 this.watchAfterOpen = function () { 457 if (!watchingBody) { 458 watchingBody = true; 459 $body.on('click touchstart', offClick); 460 resizeWatcher.start(); 461 } 462 }; 463 464 this.ignoreAfterClose = function (evt) { 465 if (watchingBody) { 466 watchingBody = false; 467 $body.off('click touchstart', offClick); 468 resizeWatcher.stop(); 469 } 470 }; 471 472 Object.defineProperties(this, { 473 correctPosition: constant(positionOnSection(this.$navMenu)) 474 }); 475 } 476 Object.defineProperties(NavigationB, { 477 buttonSelector: constant('.navigation__button'), 478 buttonTextSelector: constant('.navigation__button--text'), 479 buttonIconSelector: constant('.navigation__button--icon'), 480 buttonIconActiveCSS: constant('is-active'), 481 navMenuSelector: constant('.navigation__menu--container'), 482 dropDownActiveCss: constant('navigation-open'), 483 slideDownClass: constant('slideDown'), 484 fadeOutClass: constant('fadeOut') 485 }); 486 Object.defineProperties(NavigationB.prototype, { 487 $el: mutable($noPlace), 488 $button: mutable($noPlace), 489 button: mutable(noPlace), 490 $buttonIcon: mutable($noPlace), 491 $buttonText: mutable($noPlace), 492 $navMenu: mutable($noPlace), 493 state: mutable(false), 494 busy: mutable(false), 495 open: constant(function () { 496 var dis = this; 497 if (!this.state && !this.busy) { 498 this.state = true; 499 this.busy = true; 500 this.watchAfterOpen(); 501 this.$button.attr('ariaExpanded', 'true'); 502 this.$buttonIcon.addClass(NavigationB.buttonIconActiveCSS); 503 this.$buttonText.text(this.$buttonText.data('closeDescription')); 504 this.$navMenu.addClass(NavigationB.dropDownActiveCss); 505 this.$navMenu.addClass(NavigationB.slideDownClass); 506 this.button.disabled = true; 507 this.$navMenu.one("animationend animationcancel", function () {
508 dis.$navMenu.focus(); 509 dis.$navMenu.removeClass(NavigationB.slideDownClass); 510 dis.busy = false; 511 dis.button.disabled = false; 512 }); 513 this.correctPosition(); 514 } 515 }), 516 close: constant(function () { 517 var dis = this; 518 if (this.state && !this.busy) { 519 this.state = false; 520 this.busy = true; 521 this.ignoreAfterClose(); 522 this.$buttonIcon.removeClass(NavigationB.buttonIconActiveCSS); 523 this.$buttonText.text(this.$buttonText.data('openDescription')); 524 this.$button.attr('ariaExpanded', 'false'); 525 this.$navMenu.addClass(NavigationB.fadeOutClass); 526 this.button.disabled = true; 527 this.$navMenu.one("animationend animationcancel", function () { 528 dis.$navMenu.removeClass(NavigationB.fadeOutClass); 529 dis.$navMenu.removeClass(NavigationB.dropDownActiveCss); 530 dis.busy = false; 531 dis.button.disabled = false; 532 }); 533 } 534 }), 535 toggle: constant(function () { 536 if (this.state) { 537 this.close(); 538 } else { 539 this.open(); 540 } 541 }), 542 watchAfterOpen: mutable(Function.prototype), 543 ignoreAfterClose: mutable(Function.prototype) 544 }); 545 546 function ExpandableLink(li) { 547 var dis = this; 548 /** 549 * @type {JQuery} 550 */ 551 this.$el = $(li); 552 this.$el.data(ExpandableLink.dataAttribute, this); 553 this.$siblings = this.$el.siblings(ExpandableLink.identifier); 554 this.$a = this.$el.children(ExpandableLink.linkSelector).first(); 555 this.$ul = this.$el.children(ExpandableLink.subMenuSelector).first(); 556 this.$button = this.$el.children(ExpandableLink.expansionSelector).first(); 557 this.$buttonIcon = this.$button.children(ExpandableLink.expansionIconSelector).first(); 558 this.$buttonText = this.$button.children(ExpandableLink.expansionTextSelector).first(); 559 560 this.$button.on('click', function () { 561 dis.toggle(); 562 }); 563 } 564 Object.defineProperties(ExpandableLink, { 565 expandedClass: constant('is-active'), 566 identifier: constant('.navigation__item.has-sub-items'), 567 linkSelector: constant('.navigation__item--link'), 568 expansionSelector: constant('.navigation__item--expand'), 569 expansionIconSelector: constant('.icon'), 570 expansionTextSelector: constant('.sr-only'), 571 subMenuSelector: constant('.navigation__list'), 572 dataAttribute: constant('ExpandableLink'), 573 closeIconClass: constant('icon-minus'), 574 openIconClass: constant('icon-plus') 575 }); 576 Object.defineProperties(ExpandableLink.prototype, { 577 $el: mutable($noPlace), 578 $a: mutable($noPlace), 579 $siblings: mutable($noPlace), 580 $button: mutable($noPlace), 581 $buttonIcon: mutable($noPlace), 582 $buttonText: mutable($noPlace), 583 state: mutable(false), 584 expand: constant(function () { 585 if (!this.state) { 586 this.state = true; 587 this.$button.prop('title', this.closeDescription); 588 this.$buttonText.text(this.closeDescription); 589 this.$buttonIcon.removeClass(ExpandableLink.openIconClass) 590 .addClass(ExpandableLink.closeIconClass); 591 this.$el.addClass(ExpandableLink.expandedClass); 592 this.$siblings.each(function () { 593 /** 594 * @type {ExpandableLink} 595 */ 596 var expandableLink = $.data(this, ExpandableLink.dataAttribute); 597 if (expandableLink) { 598 expandableLink.contract(); 599 } 600 }); 601 } 602 }), 603 contract: constant(function () { 604 if (this.state) { 605 this.state = false; 606 this.$button.prop('title', this.openDescription); 607 this.$buttonText.text(this.openDescription); 608 609 this.$buttonIcon.removeClass(ExpandableLink.closeIconClass) 610 .addClass(ExpandableLink.openIconClass); 611 this.$el.removeClass(ExpandableLink.expandedClass); 612 } 613 }), 614 toggle: constant(function () { 615 if (this.state) { 616 this.contract(); 617 } else { 618 this.expand(); 619 } 620 }), 621 openDescription: mutable('View more'), 622 closeDescription: mutable('View less') 623 }); 624 625 var navB = $('.component.navigation .navigation-b'); 626 navB.each(function () { 627 new NavigationB(this); 628 }); 629 navB.find(ExpandableLink.identifier).each(function () { 630 new ExpandableLink(this); 631 }); 632 })(); 633 634 (function NavigationLayoutC() { 635 var navC = $('.component.navigation .navigation-c'); 636 637 function NavigationC(nav) { 638 var watchingBody = false, 639 dis = this; 640 var resizeWatcher = throttleResize(function () { 641 dis.correctPosition(); 642 }); 643 /** 644 * @type {jQuery} 645 */ 646 this.$el = $(nav); 647 this.$button = this.$el.children(NavigationC.buttonSelector).first(); 648 this.button = this.$button.get(0); 649 this.$buttonIcon = this.$button.children(NavigationC.buttonIconSelector).first(); 650 this.$buttonText = this.$button.children(NavigationC.buttonTextSelector).first(); 651 this.$navMenu = this.$el.children(NavigationC.navMenuSelector).first(); 652 653 function offClick(evt) { 654 if (!nav.contains(evt.target)) { 655 dis.close(); 656 } 657 } 658 659 this.$button.on('click', function (evt) { 660 dis.toggle(); 661 }); 662 this.watchAfterOpen = function () { 663 if (!watchingBody) { 664 watchingBody = true; 665 $body.on('click touchstart', offClick); 666 resizeWatcher.start(); 667 } 668 }; 669 670 this.ignoreAfterClose = function (evt) { 671 if (watchingBody) { 672 watchingBody = false;
673 $body.off('click touchstart', offClick); 674 resizeWatcher.stop(); 675 } 676 }; 677 Object.defineProperties(this, { 678 correctPosition: constant(positionOnSection(this.$navMenu)) 679 }); 680 } 681 Object.defineProperties(NavigationC, { 682 buttonSelector: constant('.navigation__button'), 683 buttonTextSelector: constant('.navigation__button--text'), 684 buttonIconSelector: constant('.navigation__button--icon'), 685 buttonIconActiveCSS: constant('is-active'), 686 navMenuSelector: constant('.navigation__menu--container'), 687 dropDownActiveCss: constant('navigation-open'), 688 slideDownClass: constant('slideDown'), 689 fadeOutClass: constant('fadeOut') 690 }); 691 Object.defineProperties(NavigationC.prototype, { 692 $el: mutable($noPlace), 693 $button: mutable($noPlace), 694 button: mutable(noPlace), 695 $buttonIcon: mutable($noPlace), 696 $buttonText: mutable($noPlace), 697 $navMenu: mutable($noPlace), 698 state: mutable(false), 699 busy: mutable(false), 700 open: constant(function () { 701 var dis = this; 702 if (!this.state && !this.busy) { 703 this.state = true; 704 this.busy = true; 705 this.watchAfterOpen(); 706 this.$button.attr('ariaExpanded', 'true'); 707 this.$buttonIcon.addClass(NavigationC.buttonIconActiveCSS); 708 this.$buttonText.text(this.$buttonText.data('closeDescription')); 709 this.$navMenu.addClass(NavigationC.dropDownActiveCss); 710 this.$navMenu.addClass(NavigationC.slideDownClass); 711 this.button.disabled = true; 712 this.$navMenu.one("animationend animationcancel", function () { 713 dis.$navMenu.focus(); 714 dis.$navMenu.removeClass(NavigationC.slideDownClass); 715 dis.busy = false; 716 dis.button.disabled = false; 717 }); 718 this.correctPosition() 719 } 720 }), 721 close: constant(function () { 722 var dis = this; 723 if (this.state && !this.busy) { 724 this.state = false; 725 this.busy = true; 726 this.ignoreAfterClose(); 727 this.$buttonIcon.removeClass(NavigationC.buttonIconActiveCSS); 728 this.$buttonText.text(this.$buttonText.data('openDescription')); 729 this.$button.attr('ariaExpanded', 'false'); 730 this.$navMenu.addClass(NavigationC.fadeOutClass); 731 this.button.disabled = true; 732 this.$navMenu.one("animationend animationcancel", function () { 733 dis.$navMenu.removeClass(NavigationC.fadeOutClass); 734 dis.$navMenu.removeClass(NavigationC.dropDownActiveCss); 735 dis.busy = false; 736 dis.button.disabled = false; 737 }); 738 } 739 }), 740 toggle: constant(function () { 741 if (this.state) { 742 this.close(); 743 } else { 744 this.open(); 745 } 746 }), 747 watchAfterOpen: mutable(Function.prototype), 748 ignoreAfterClose: mutable(Function.prototype) 749 }); 750 751 navC.each(function () { 752 new NavigationC(this); 753 }); 754 })(); 755 756 757 (function Searchbar() { 758 var searchbar = $('.searchbar-a'); 759 760 function SearchbarA(sbar) { 761 var watchingBody = false, 762 dis = this; 763 var resizeWatcher = throttleResize(function () { 764 dis.correctPosition(); 765 }); 766 /** 767 * @type {jQuery} 768 */ 769 this.$el = $(sbar); 770 this.$button = this.$el.find(SearchbarA.buttonSelector).first(); 771 this.button = this.$button.get(0); 772 this.$closeButton = this.$el.find(SearchbarA.closeButtonSelector).first(); 773 this.closeButton = this.$button.get(0); 774 this.$flyout = this.$el.find(SearchbarA.flyoutSelector).first(); 775 this.$searchInput = this.$flyout.find(SearchbarA.searchInputSelector).first(); 776 777 function offClick(evt) { 778 if (!sbar.contains(evt.target)) { 779 dis.close(); 780 } 781 } 782 783 this.$button.on('click', function (evt) { 784 dis.toggle(); 785 }); 786 this.$closeButton.on('click', function (evt) { 787 dis.close(); 788 }); 789 this.watchAfterOpen = function () { 790 if (!watchingBody) { 791 watchingBody = true; 792 $body.on('click touchstart', offClick); 793 resizeWatcher.start(); 794 } 795 }; 796 797 this.ignoreAfterClose = function (evt) { 798 if (watchingBody) { 799 watchingBody = false; 800 $body.off('click touchstart', offClick); 801 resizeWatcher.stop(); 802 } 803 }; 804 Object.defineProperties(this, { 805 correctPosition: constant(positionOnSection(this.$flyout)) 806 }); 807 } 808 Object.defineProperties(SearchbarA, { 809 buttonSelector: constant('.searchbar-a__btnsearch'), 810 closeButtonSelector: constant('.searchbar-a__closeflyoutbtn'), 811 dropDownActiveCss: constant('searchbar-open'), 812 flyoutSelector: constant('.searchbar-a__flyout'), 813 searchInputSelector: constant('[name="query"]'), 814 slideDownClass: constant('slideDown'), 815 fadeOutClass: constant('fadeOut') 816 }); 817 Object.defineProperties(SearchbarA.prototype, { 818 $el: mutable($noPlace), 819 $button: mutable($noPlace), 820 button: mutable(noPlace), 821 $closeButton: mutable($noPlace), 822 closeButton: mutable(noPlace), 823 $flyout: mutable($noPlace), 824 $searchInput: mutable($noPlace), 825 state: mutable(false), 826 busy: mutable(false), 827 open: constant(function () { 828 var dis = this; 829 if (!this.state && !this.busy) { 830 this.state = true; 831 this.busy = true; 832 this.watchAfterOpen(); 833 this.$button.attr('ariaExpanded', 'true'); 834 this.$flyout.addClass(SearchbarA.dropDownActiveCss); 835 this.$flyout.addClass(SearchbarA.slideDownClass); 836 this.button.disabled = true; 837 this.$flyout.one("animationend animationcancel", function () { 838 dis.$searchInput.focus(); 839 dis.$flyout.removeClass(SearchbarA.slideDownClass); 840 dis.busy = false;
841 dis.button.disabled = false; 842 }); 843 this.correctPosition(); 844 } 845 }), 846 close: constant(function () { 847 var dis = this; 848 if (this.state && !this.busy) { 849 this.state = false; 850 this.busy = true; 851 this.ignoreAfterClose(); 852 this.$button.attr('ariaExpanded', 'false'); 853 this.$flyout.addClass(SearchbarA.fadeOutClass); 854 this.button.disabled = true; 855 this.$flyout.one("animationend animationcancel", function () { 856 dis.$flyout.removeClass(SearchbarA.fadeOutClass); 857 dis.$flyout.removeClass(SearchbarA.dropDownActiveCss); 858 dis.busy = false; 859 dis.button.disabled = false; 860 }); 861 } 862 }), 863 toggle: constant(function () { 864 if (this.state) { 865 this.close(); 866 } else { 867 this.open(); 868 } 869 }), 870 watchAfterOpen: mutable(Function.prototype), 871 ignoreAfterClose: mutable(Function.prototype) 872 }); 873 874 searchbar.each(function () { 875 new SearchbarA(this); 876 }); 877 })(); 878 879 // Provide a mobile view 880 (function MobileView($desktopContactForm) { 881 var $mobileSection; 882 /** 883 * @typedef MobileSectionClusterItem 884 * @property {JQuery} $el - Element to be inserted 885 * @property {Node} source 886 * 887 * @type MobileSectionClusterItem[]*/ 888 var mobileSectionCluster = []; 889 var x21MobileClass = 'x21-mobile-controls'; 890 var $desktopNav = $('.section-navigation-mobile, .section-navigation-mobile--no-hide') 891 .find('.component.navigation') 892 .find('.navigation-a, .navigation-b, .navigation-c').first(); 893 var $desktopPhone = $('.section-phone-mobile .phone__container').find('.phone__number').first(); 894 var $desktopMap = $('.map__container').first(); 895 896 if ([$desktopNav, $desktopPhone, $desktopContactForm, $desktopMap].every(function isEmpty($el) { 897 return $el.length === 0; 898 })) { 899 return; 900 } 901 902 if ($desktopNav.length) { 903 904 $mobileSection = $('<div class="' + x21MobileClass + ' d-md-none sticky-top"></div>').prependTo($body); 905 906 (function MmenuButton() { 907 var $mobileNav, $menuButton, mmenuAPI; 908 $mobileNav = $desktopNav.clone(); 909 910 // Remove any buttons 911 $mobileNav.find('button').remove(); 912 913 // Strip call classes 914 $mobileNav.removeClass().addClass('d-md-none').find('ul, li, a').removeClass(); 915 $desktopNav.addClass('d-none d-md-block'); 916 917 // Build the menu button 918 $menuButton = $( 919 '<button type="button" title="Show Navigation Menu">' + ( 920 '<i aria-hidden="true" class="icon icon-bars"></i><span> Menu</span>') + 921 '</button>' 922 ).appendTo($mobileSection); 923 924 // Initialize MMenu Plugin 925 $mobileNav.mmenu({ 926 extensions: ['pagedim-black'], 927 counters: true, 928 navbars: [{ 929 position: 'top', 930 content: ['prev', 'title', 'close'] 931 }, { 932 position: 'top', 933 content: ['searchfield'] 934 }] 935 }); 936 937 mmenuAPI = $mobileNav.data('mmenu'); 938 939 // Bind the menu button to the API 940 $menuButton.on('click', function () { 941 mmenuAPI.open(); 942 }); 943 })(); 944 } 945 946 function mobileLinkTemplate(classSubName, description, icon, href) { 947 var $el = $('<div class="' + x21MobileClass + '__' + classSubName + '"></div>'); 948 var $link = $('<a title="' + description + '" class="btn btn-link">' + ( 949 '<i class="icon ' + icon + '" aria-hidden="true"></i>' + 950 '<span class="sr-only">' + description + '</span>' 951 ) + '</a>').attr('href', href).appendTo($el); 952 return { 953 $el: $el, 954 $link: $link 955 }; 956 } 957 958 if ($desktopPhone.length) { 959 mobileSectionCluster.push(((function PhoneLink($selector) { 960 var sectionId = getPLESection($selector).attr('id') || ''; 961 // Valid separators are (-.), valid digits are 0-9 962 // See: //tools.ietf.org/html/rfc3966#section-3 'global-number-digits' 963 var tel = $selector.text().trim().replace(/([^0123456789\-.()])/g, ''); 964 var template = mobileLinkTemplate('phone', 'Call', 'icon-phone', 'tel:+' + tel); 965 return { 966 $el: template.$el, 967 source: $selector.get(0) 968 }; 969 })($desktopPhone))); 970 } 971 972 function mobileAnchorLink($selector, classSubName, description, icon) { 973 var sectionId = getPLESection($selector).attr('id') || ''; 974 var template = mobileLinkTemplate(classSubName, description, icon, '#' + sectionId); 975 template.$link.on('click', scrollTo($selector)); 976 return { 977 $el: template.$el, 978 source: $selector.get(0) 979 }; 980 } 981 982 if ($desktopMap.length) { 983 mobileSectionCluster.push(mobileAnchorLink( 984 $desktopMap, 'location', 'Location', 'icon icon-location-arrow' 985 )); 986 } 987 988 if ($desktopContactForm.length) { 989 mobileSectionCluster.push(mobileAnchorLink( 990 $desktopContactForm, 'contact-us', 'Contact Us', 'icon-envelope-o' 991 )); 992 } 993 994 if (mobileSectionCluster.length) {
995 mobileSectionCluster.sort(function (a, b) { 996 return a.source.compareDocumentPosition(b.source) & Node.DOCUMENT_POSITION_PRECEDING ? 1 : -1; 997 }).reduce(function ($target, value) { 998 return $target.append(value.$el); 999 }, $('<div class="' + x21MobileClass + '__quick-links"></div>')).appendTo($mobileSection); 1000 } 1001 })($contactForm); 1002 1003 // Add Browserlock 1004 (function BrowserLock($target) { 1005 if ($target.length) { 1006 // Create ContactForm lock 1007 var $contactFormLock = $('<div>') 1008 .addClass('contactform-lock'); 1009 1010 // Add ContactForm click event 1011 $contactFormLock.on('click', scrollTo($target)); 1012 1013 // Draw browser lock containers, prepend to body 1014 var $lockContainer = $('<div>') 1015 .addClass('browser-lock') 1016 .append($contactFormLock); 1017 1018 $('body').prepend($lockContainer); 1019 } 1020 })($contactForm); 1021 1022 // Preview link fix 1023 (function cleanUpRedactorLinks() { 1024 // Check if we're in an iFrame or not (editor mode or preview link) 1025 if (self != top) 1026 return; 1027 1028 var link = $('link[rel=canonical]').attr('href'); 1029 if (link != undefined) { 1030 var parts = link.split('/'), 1031 index = parts.indexOf('my.martindalenolo.com'); 1032 if (index >= 0) { 1033 var siteId = parts[index + 1]; 1034 if (!siteId) 1035 return; 1036 1037 var $links = $('div[data-component-name=editable] a[data-source-type=my-pages]'); 1038 if ($links == undefined || $links.length == 0) 1039 return; 1040 1041 $links.each(function () { 1042 var currentLink = $(this).attr('href'); 1043 1044 // Check for edge-case of absolute my-page 1045 var index = currentLink.split('/').indexOf(siteId); 1046 if (index >= 0) 1047 return; 1048 1049 var newLink = '/' + siteId + currentLink; 1050 $(this).attr('href', newLink); 1051 }) 1052 } 1053 } 1054 })(); 1055 1056 (function setBrowserlock() { 1057 var rawSettings = $('#ple-data-json').text(); 1058 var settings = JSON.parse(rawSettings); 1059 var browserlockSettings = settings.fixedSection; 1060 1061 //#SMBWMGR-8501 this check can be removed after merging - /editor/merge_requests/1922 1062 if (typeof browserlockSettings !== 'undefined' && browserlockSettings !== null) { 1063 1064 var $browserLockContainer = $('#' + browserlockSettings.id).find('.ple__browserlock-modules'); 1065 var $browserLockModules = $browserLockContainer.find('> .row .row'); 1066 1067 if (browserlockSettings.spacing.space == 'custom' && browserlockSettings.spacing.offsetDistance && parseInt(browserlockSettings.spacing.offsetDistance) != NaN) { 1068 $browserLockModules.each(function (index, element) { 1069 $(element).css({ 1070 marginBottom: browserlockSettings.spacing.offsetDistance 1071 }); 1072 }); 1073 } 1074 1075 if (browserlockSettings.placement.place != null) { 1076 switch (browserlockSettings.placement.place) { 1077 case 'center-right': 1078 $browserLockContainer.css({ 1079 top: '50%', 1080 transform: 'translateY(-50%)' 1081 }); 1082 break; 1083 1084 case 'bottom-right': 1085 $browserLockContainer.css({ 1086 top: 'auto' 1087 }); 1088 1089 if (parseInt(browserlockSettings.placement.offsetDistance) != NaN && !browserlockSettings.placement.useDefaultOffset) { 1090 $browserLockContainer.css({ 1091 bottom: browserlockSettings.placement.offsetDistance 1092 }); 1093 } 1094 break; 1095 1096 case 'top-right': 1097 default: 1098 if (parseInt(browserlockSettings.placement.offsetDistance) != NaN && !browserlockSettings.placement.useDefaultOffset) { 1099 $browserLockContainer.css({ 1100 top: browserlockSettings.placement.offsetDistance 1101 }); 1102 } 1103 break; 1104 } 1105 } 1106 } else { 1107 console.log("browserlock Settings is undefinded") 1108 } 1109 })(); 1110 1111 (function backToTop() { 1112 var $bttButton = $('.ple__back-to-top'); 1113 var activeClass = 'active'; 1114 var showPoint = 400; 1115 1116 $bttButton.on('click', function (event) { 1117 event.preventDefault(); 1118 1119 $([document.documentElement, document.body]).animate({ 1120 scrollTop: 0 1121 }, 1000); 1122 }); 1123 1124 $(window).scroll(function () { 1125 var window_x = $(window).scrollTop(); 1126 if (window_x >= showPoint) { 1127 $bttButton.addClass(activeClass); 1128 } else { 1129 $bttButton.removeClass(activeClass); 1130 } 1131 }); 1132 })(); 1133 1134 (function scrollToContactForm() { 1135 var $ctaEl = $('.scrollToContactForm .cta__button'), $target = $(".contactFormSection"); 1136 $ctaEl.each(function() { $(this).removeAttr("href"); $(this).on('click', scrollTo($target));}); 1137 })(); 1138 1139 (function featuredBlocksEqualizeHeights() { 1140 var maxHeight = 0, 1141 $fbContainer = $('.featuredblocks-grid-e'), 1142 $fbItems = $fbContainer.find('.featuredblock__item'); 1143 1144 $fbItems.children().height('auto').each(function () { 1145 maxHeight = Math.max(maxHeight, $(this).outerHeight(true)); 1146 }).height(maxHeight); 1147 1148 $fbItems.css('min-height', maxHeight + 'px'); 1149 $fbContainer.addClass('blockTransitionNone'); 1150 })(); 1151 1152 (function objectFitIEFix() { 1153 var isIE = /*@cc_on!@*/false || !!document.documentMode, // Internet Explorer 6-11 1154 isEdge = !isIE && !!window.StyleMedia; // Edge 20+ 1155 // Check if Internet Explorer 6-11 OR Edge 20+ 1156 if(isIE || isEdge) { 1157 var $container = $('.wrap__where_is_your_pain').find(".image"), 1158 imgUrl = $container.find("img").prop("src"); 1159 if (imgUrl) { 1160 $container.css("backgroundImage", 'url(' + imgUrl + ')').addClass("custom-object-fit"); 1161 } 1162 } 1163 })(); 1164 1165 1166 1167 (function mobileHeader() { 1168 1169 function mobileHeaderInit() { 1170 1171 var $mhContainer = $('.mobileHeader-container'), 1172 mhHeaight = $mhContainer.height(), 1173 $navButton = $('#mobileHeaderNavBtn'), 1174 $navMenu = $('#mobileHeaderNav'), 1175 $homeIcon = $(".mobileHeader-homeIcon"),
1176 $formIcon = $(".formIconLink"), 1177 $mapIcon = $(".locIconLink"), 1178 $phoneIcon = $(".phoneIconLinkExpand"), 1179 $mapContainer = $('.map__container').first(), 1180 $formContainer = $('.form__container').first(), 1181 $locList = $('.location-list'), 1182 $pleMSticky = $(".ple_module_sticky"), 1183 navAPI; 1184 1185 // if there is no form module in the view target the contactform module 1186 if(!$formContainer.length) $formContainer = $('.contactform__container').first(); 1187 1188 // if sticky module is present set top space to the first elem for the mobileHeader 1189 if ($pleMSticky.length) { 1190 var $pleMStickEl = $pleMSticky.first().find('.container'); 1191 $(window).resize(function (){ 1192 mhHeaight = $mhContainer.height(); 1193 $pleMStickEl.css('padding-top', mhHeaight); 1194 }); 1195 } 1196 1197 if($navMenu.children().length) { 1198 // init mMenu 1199 $navMenu.mmenu({ 1200 extensions: ['pagedim-black'], 1201 counters: true, 1202 navbars: [{ 1203 position: 'top', 1204 content: ['prev', 'title', 'close'] 1205 }] 1206 }); 1207 // bind menu button
1208 navAPI = $navMenu.data('mmenu'); 1209 $navButton.on('click', function () { 1210 navAPI.open(); 1211 }); 1212 } 1213 1214 // handle click events for the icons 1215 ($formContainer.length) ? $formIcon.click(scrollToEl($formContainer)) : $formIcon.fadeOut(); 1216 ($mapContainer.length) ? $mapIcon.click(scrollToEl($mapContainer)) : $mapIcon.fadeOut(); 1217 $phoneIcon.click(function () { $locList.addClass("expand"); }); 1218 $('.mobileHeader-locWrap').find('.icon-times').click(function () { $locList.removeClass("expand"); }); 1219 1220 // replace the mm title with the home icon 1221 if($homeIcon.length) { 1222 $(".mobileHeader-homeIcon .mm-homeIcon").insertBefore($('#mobileHeaderNav .mm-navbar-top .mm-title')); 1223 } 1224 1225 // set the section bg class 1226 if($mhContainer.is('[class*=section__bg]')){ 1227 var classList = $mhContainer.prop("classList"); 1228 $.each(classList, function(index, item) { 1229 if (item.startsWith("section__bg")) { 1230 $navMenu.addClass(item); 1231 } 1232 }); 1233 } 1234 1235 $( window ).load(function() { 1236 alignLogo(); 1237 }); 1238 1239 $( window ).resize(function() { 1240 alignLogo(); 1241 }); 1242 1243 } 1244 1245 // align logo with if only navigation is present or only one icon is present on the right hand side 1246 function alignLogo() { 1247 if (!$('.mobileHeader-form').length && !$('.mobileHeader-location').length && !$('.mobileHeader-phone').length && $('#mobileHeaderNavBtn').length) { 1248 var hamburgerMenuWidth = $('.mobileHeader-navigation #mobileHeaderNavBtn').outerWidth(true); 1249 $('.mobileHeader-logo').css('margin-right', hamburgerMenuWidth); 1250 } 1251 if (($('.mobileHeader-form').length || $('.mobileHeader-location').length || $('.mobileHeader-phone').length) && !$('#mobileHeaderNavBtn').length) { 1252 var totalWidthIcons = $('.mobileHeader-form').width() + $('.mobileHeader-location').width() + $('.mobileHeader-phone').width(); 1253 if(totalWidthIcons < 46) { 1254 $('.mobileHeader-logo').css('margin-left', totalWidthIcons); 1255 } 1256 } 1257 } 1258 1259 function scrollToEl($el) { 1260 return function () { 1261 var $moduleSticky = $('.ple_module_sticky'), moduleStickyHeight = 0; 1262 if ($moduleSticky.length) $moduleSticky.each(function () { moduleStickyHeight += $(this).find('.container').outerHeight(true); }); 1263 if (moduleStickyHeight == 0) moduleStickyHeight = $('.mobileHeader-container').height(); 1264 $('html, body').animate({ scrollTop: $el.offset().top - moduleStickyHeight }, 'slow'); 1265 }; 1266 } 1267 1268 if ($('.mobileHeader-container').length) mobileHeaderInit(); 1269 1270 })(); 1271 1272 (function leadsFormTxtAreaExpand() { 1273 if ($(".form").length) { 1274 $("body").on("SMBLeadsFormReady", function () { 1275 var form = $(".contact-us-im .form"); 1276 form.each(function () { 1277 var formGroup = $(this).find('.form__group').not('.form-input-comments, .form-input-message'); 1278 formGroup.wrapAll("<div class='outerWrap' />"); 1279 }); 1280 }); 1281 } 1282 })(); 1283 1284 (function staffDesign() { 1285 $('.staff-b').closest('.container').addClass('fwStacked') 1286 })(); 1287 1288 (function hideEmptyPeerReview() { 1289 $(window).load(function () { 1290 if (!$('.isEditor').length) { 1291 1292 var $clientRC = $('#mh-client-rating-container'), 1293 $ratingC = $('#mh-rating-container'); 1294 1295 setTimeout(function () { 1296 if ($clientRC.length && $ratingC.length) { 1297 if ($clientRC.is(':empty') && $ratingC.is(':empty')) { 1298 $ratingC.closest('.stacked').hide(); 1299 } 1300 } else { 1301 if (!$clientRC.length && !$ratingC.length) { 1302 $('.comp-peerreview').closest('.stacked').hide(); 1303 } 1304 if ($clientRC.is(':empty') || $ratingC.is(':empty')) { 1305 $ratingC.closest('.stacked').hide(); 1306 $clientRC.closest('.stacked').hide(); 1307 } 1308 } 1309 },1600); 1310 } 1311 }); 1312 })(); 1313 1314 (function navigationVisibility() { 1315 var mhEnabled = $('.mobileHeader-container').length; 1316 if (mhEnabled) { 1317 var tabletFlag = $('#tabletHeaderEnabledFlag').val(), 1318 $navSection = $('.section-navigation .navigation'); 1319 if (tabletFlag == 1) { 1320 $navSection.addClass('d-none d-lg-block'); 1321 } else { 1322 $navSection.addClass('d-none d-md-block'); 1323 } 1324 } 1325 })(); 1326 }); 1327})();
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.