vendor: 111 bytes, lines 1-5
1/******/ (() => { // webpackBootstrap 2/******/ "use strict"; 3/******/ var __webpack_modules__ = ({ 4 5/***/ "./
5js/components/Accordion.js": 6/*!****************************************!*\ 7 !*** ./js/components/Accordion.js ***! 8 \****************************************/ 9/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 10 11__webpack_require__.r(__webpack_exports__); 12/* harmony export */ __webpack_require__.d(__webpack_exports__, { 13/* harmony export */ "default": () => (/* binding */ initAccordion) 14/* harmony export */ }); 15/* harmony import */ var _BaseComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BaseComponent */ "./js/components/BaseComponent.js"); 16 17 18const itemSelector = '[data-accordion-item]'; 19 20class Accordion extends _BaseComponent__WEBPACK_IMPORTED_MODULE_0__["default"] { 21 selectors = { 22 parent: '[data-accordion]', 23 button: '[data-accordion-button]', 24 panel: '[data-accordion-panel]', 25 }; 26 27 stateClasses = { 28 isOpen: 'is-open', 29 isAnimating: 'is-animating', 30 }; 31 32 stateAttributes = { 33 dataCollapsible: 'data-collapsible', 34 }; 35 36 cssVariables = { 37 height: '--accordion-height', 38 }; 39 40 constructor(rootElement) { 41 super(); 42 this.rootElement = rootElement; 43 this.parent = this.rootElement.closest(this.selectors.parent); 44 this.button = this.rootElement.querySelector(this.selectors.button); 45 this.panel = this.rootElement.querySelector(this.selectors.panel); 46 this.isCollapsible = this.parent?.hasAttribute(this.stateAttributes.dataCollapsible); 47 this.isAnimating = false; 48 this.state = this.getProxyState({ 49 isOpen: this.rootElement.classList.contains(this.stateClasses.isOpen), 50 }); 51 rootElement._instance = this; 52 53 this.bindEvents(); 54 } 55 56 updateUI() { 57 const { isOpen } = this.state; 58 59 this.rootElement.classList.toggle(this.stateClasses.isOpen, isOpen); 60 61 if (this.isCollapsible && isOpen) { 62 this.closeOtherInstances(); 63 } 64 } 65 66 toggle() { 67 this.state.isOpen = !this.state.isOpen; 68 } 69 70 close() { 71 this.state.isOpen = false; 72 } 73 74 closeOtherInstances() { 75 const siblings = this.parent.querySelectorAll(`:scope > ${itemSelector}.${this.stateClasses.isOpen}`); 76 siblings.forEach((sibling) => sibling !== this.rootElement && sibling._instance.handleAnimation()); 77 } 78 79 onButtonClick = (e) => { 80 this.state.isOpen && e.preventDefault(); 81 82 const isSafari = document.documentElement.classList.contains('ua-safari'); 83 isSafari ? this.setSafariHeight() : this.setHeight(); 84 85 if (!this.isAnimating) { 86 this.isAnimating = true; 87 } else { 88 e.preventDefault(); 89 this.rootElement.classList.remove(this.stateClasses.isAnimating); 90 this.rootElement.open = false; 91 this.isAnimating = false; 92 this.close(); 93 return; 94 } 95 96 this.handleAnimation(); 97 }; 98 99 handleAnimation() { 100 this.rootElement.classList.add(this.stateClasses.isAnimating); 101 this.toggle(); 102 this.rootElement.addEventListener('animationend', this.animationEndHandler, { once: true }); 103 } 104 105 animationEndHandler = () => { 106 this.rootElement.classList.remove(this.stateClasses.isAnimating); 107 this.isAnimating = false; 108 this.rootElement.open = this.state.isOpen; 109 }; 110 111 setHeight() { 112 this.panel.style.setProperty(this.cssVariables.height, `${this.panel.scrollHeight}px`); 113 } 114 115 setSafariHeight() { 116 if (!this.rootElement.open) { 117 this.rootElement.open = true; 118 this.setHeight(); 119 this.rootElement.open = false; 120 } else { 121 this.setHeight(); 122 } 123 } 124 125 bindEvents() { 126 this.button.addEventListener('click', this.onButtonClick); 127 } 128} 129 130class initAccordion { 131 constructor() { 132 this.init(); 133 } 134 135 init() { 136 const items = document.querySelectorAll(itemSelector); 137 items.forEach((item) => new Accordion(item)); 138 } 139} 140 141 142/***/ }), 143
144/***/ "./js/components/BaseComponent.js": 145/*!********************************************!*\ 146 !*** ./js/components/BaseComponent.js ***! 147 \********************************************/ 148/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 149 150__webpack_require__.r(__webpack_exports__); 151/* harmony export */ __webpack_require__.d(__webpack_exports__, { 152/* harmony export */ "default": () => (/* binding */ BaseComponent) 153/* harmony export */ }); 154class BaseComponent { 155 constructor() { 156 if (this.constructor === BaseComponent) { 157 throw new Error('Cannot create an instance of an abstract class.'); 158 } 159 } 160 161 getProxyState(initialState) { 162 return new Proxy(initialState, { 163 get: (target, prop) => { 164 return target[prop]; 165 }, 166 set: (target, prop, newValue) => { 167 const oldValue = target[prop]; 168 169 target[prop] = newValue; 170 171 if (newValue !== oldValue) { 172 this.updateUI(); 173 } 174 175 return true; 176 }, 177 }); 178 } 179 180 updateUI() { 181 throw new Error('updateUI() method must be implemented in the child class.'); 182 } 183} 184 185 186/***/ }), 187
188/***/ "./js/components/Dialog.js": 189/*!*************************************!*\ 190 !*** ./js/components/Dialog.js ***! 191 \*************************************/ 192/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 193 194__webpack_require__.r(__webpack_exports__); 195/* harmony export */ __webpack_require__.d(__webpack_exports__, { 196/* harmony export */ "default": () => (/* binding */ initDialog) 197/* harmony export */ }); 198/* harmony import */ var _BaseComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BaseComponent */ "./js/components/BaseComponent.js"); 199/* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/utils */ "./js/utils/utils.js"); 200 201 202 203const dialogSelector = '[data-dialog]'; 204 205class Dialog extends _BaseComponent__WEBPACK_IMPORTED_MODULE_0__["default"] { 206 stateClasses = { 207 isVisible: 'is-visible', 208 isClosing: 'close-dialog-animation', 209 }; 210 211 stateAttributes = { 212 dataModal: 'data-modal', 213 dataDialogStack: 'data-dialog-stack', 214 }; 215 216 selectors = { 217 nativeSubmitForm: 'form.form--native-submit[method="dialog"]', 218 }; 219 220 static openModals = []; 221 static stackable = false; 222 static overlay = null; 223 static immediateClose = false; 224 225 constructor(rootElement) { 226 super(); 227 this.rootElement = rootElement; 228 this.dialogName = this.rootElement.dataset.dialog; 229 this.isModal = this.rootElement.hasAttribute(this.stateAttributes.dataModal); 230 this.openButtons = document.querySelectorAll(`[data-dialog-open="${this.dialogName}"]`); 231 this.closeButtons = document.querySelectorAll(`[data-dialog-close="${this.dialogName}"]`); 232 this.nativeSubmitForms = this.rootElement.querySelectorAll(this.selectors.nativeSubmitForm); 233 this.state = this.getProxyState({ isOpen: false }); 234 rootElement._instance = this; 235 236 this.dialog = this.rootElement.querySelector('.dialog'); 237 this.backdropFilterResizeObserver = null; 238 239 this.isModal && !Dialog.stackable && this.createOverlay(); 240 this.bindEvents(); 241 } 242 243 updateUI() { 244 const { isOpen } = this.state; 245 const { isVisible } = this.stateClasses; 246 247 (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.lockScrolling)(isOpen, 'dialog'); 248 249 if (isOpen) { 250 if (!this.isModal) return this.rootElement.show(); 251 252 this.rootElement.showModal(); 253 !Dialog.openModals.includes(this) && Dialog.openModals.push(this); 254 255 if (Dialog.stackable) { 256 this.rootElement.setAttribute(this.stateAttributes.dataDialogStack, Dialog.openModals.length); 257 } else { 258 const backdropFilter = Dialog.overlay.querySelector('.overlay__filter'); 259 backdropFilter.style.setProperty('width', this.dialog.clientWidth + 'px'); 260 backdropFilter.style.setProperty('height', this.dialog.clientHeight + 'px'); 261 Dialog.overlay.classList.add(isVisible); 262 Dialog.openModals.length === 2 && Dialog.openModals[0].close(); 263 264 // Resize observer on dialog to update filter size 265 this.backdropFilterResizeObserver = new ResizeObserver((entries) => { 266 backdropFilter.style.setProperty('width', entries[0].target.clientWidth + 'px'); 267 backdropFilter.style.setProperty('height', entries[0].target.clientHeight + 'px'); 268 }); 269 this.backdropFilterResizeObserver.observe(this.dialog); 270 } 271 272 // Remove initial focus from the first interactive element inside the dialog 273 this.rootElement.focus(); 274 } else { 275 if (!this.isModal) return this.rootElement.close(); 276 277 !Dialog.immediateClose && this.rootElement.close(); 278 Dialog.stackable && this.rootElement.removeAttribute(this.stateAttributes.dataDialogStack); 279 Dialog.openModals.splice(Dialog.openModals.indexOf(this), 1); 280 this.backdropFilterResizeObserver.disconnect(); 281 } 282 } 283 284 open() { 285 this.state.isOpen = true; 286 } 287 288 close() { 289 this.startClosingAnimation(); 290 } 291 292 startClosingAnimation() { 293 this.rootElement.classList.add(this.stateClasses.isClosing); 294 Dialog.hideOverlay(); 295 this.rootElement.addEventListener('animationend', this.animationEndHandler, { once: true }); 296 } 297 298 animationEndHandler = () => { 299 this.completeClosingAnimation(); 300 }; 301 302 completeClosingAnimation() { 303 this.state.isOpen = false;
304 this.rootElement.classList.remove(this.stateClasses.isClosing); 305 } 306 307 handleImmediateClose() { 308 if (!this.isModal) return; 309 Dialog.immediateClose = true; 310 if (Dialog.stackable) { 311 Dialog.openModals[Dialog.openModals.length - 1].close(); 312 } else { 313 this.rootElement.close(); 314 Dialog.hideOverlay(); 315 } 316 this.state.isOpen = false; 317 this.rootElement.classList.remove(this.stateClasses.isClosing); 318 this.rootElement.removeEventListener('animationend', this.animationEndHandler); 319 Dialog.immediateClose = false; 320 } 321 322 createOverlay() { 323 if (!Dialog.overlay) { 324 Dialog.overlay = (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.createOverlay)('dialog-overlay', document.body, true); 325 } 326 } 327 328 onOpenButtonClick = () => { 329 this.open(); 330 }; 331 332 onCloseButtonClick = () => { 333 this.close(); 334 }; 335 336 static closeAll() { 337 Dialog.openModals.forEach((dialog) => dialog.close()); 338 } 339 340 static hideOverlay() { 341 if (!Dialog.stackable && Dialog.openModals.length === 1) { 342 Dialog.overlay.classList.remove('is-visible'); 343 } 344 } 345 346 static handleEscapeKey(e) { 347 if (e.key === 'Escape' && Dialog.openModals.length > 0) { 348 if (Dialog.stackable) { 349 return Dialog.openModals[Dialog.openModals.length - 1].handleImmediateClose(); 350 } 351 // Force close all dialogs despite current animation state 352 while (Dialog.openModals.length > 0) { 353 Dialog.openModals[Dialog.openModals.length - 1].handleImmediateClose(); 354 } 355 } 356 } 357 358 bindEvents() { 359 this.openButtons.forEach((openButton) => { 360 openButton.addEventListener('click', this.onOpenButtonClick); 361 }); 362 this.closeButtons.forEach((closeButton) => { 363 closeButton.addEventListener('click', this.onCloseButtonClick); 364 }); 365 this.nativeSubmitForms[0] && 366 this.nativeSubmitForms.forEach((form) => { 367 form.addEventListener('submit', (e) => { 368 e.preventDefault(); 369 this.close(); 370 }); 371 }); 372 373 // Overlay lays beneath transparent .dialog-container::backdrop 374 this.rootElement.addEventListener('click', (e) => { 375 e.target === this.rootElement && this.close(); 376 }); 377 } 378} 379 380class initDialog { 381 constructor() { 382 this.init(); 383 } 384 385 init() { 386 const dialogs = document.querySelectorAll(dialogSelector);
387 dialogs.forEach((dialog) => new Dialog(dialog)); 388 389 document.addEventListener('keydown', Dialog.handleEscapeKey); 390 } 391} 392 393 394/***/ }), 395
396/***/ "./js/components/Drawer.js": 397/*!*************************************!*\ 398 !*** ./js/components/Drawer.js ***! 399 \*************************************/ 400/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 401 402__webpack_require__.r(__webpack_exports__); 403/* harmony export */ __webpack_require__.d(__webpack_exports__, { 404/* harmony export */ "default": () => (/* binding */ initDrawer) 405/* harmony export */ }); 406/* harmony import */ var _BaseComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BaseComponent */ "./js/components/BaseComponent.js"); 407/* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/utils */ "./js/utils/utils.js"); 408 409 410 411const drawerSelector = '[data-drawer]'; 412 413class Drawer extends _BaseComponent__WEBPACK_IMPORTED_MODULE_0__["default"] { 414 stateClasses = { 415 isActive: 'is-active', 416 isVisible: 'is-visible', 417 }; 418 419 stateAttributes = { 420 ariaExpanded: 'aria-expanded', 421 }; 422 423 static openDrawers = []; 424 static resizeListener = false;
425 static overlay = null; 426 static isAnimating = false; 427 static animationDelay = 300; 428 429 constructor(rootElement) { 430 super(); 431 this.rootElement = rootElement; 432 this.drawerName = this.rootElement.dataset.drawer; 433 this.toggleButtons = document.querySelectorAll(`[data-drawer-toggle="${this.drawerName}"]`); 434 this.closeButtons = document.querySelectorAll(`[data-drawer-close="${this.drawerName}"]`); 435 this.state = this.getProxyState({ isOpen: false }); 436 437 this.createOverlay(); 438 this.bindEvents(); 439 } 440 441 updateUI() { 442 const { isOpen } = this.state; 443 const { isActive, isVisible } = this.stateClasses; 444 const { ariaExpanded } = this.stateAttributes; 445 446 this.toggleButtons.forEach((toggleButton) => { 447 toggleButton.setAttribute(ariaExpanded, isOpen); 448 }); 449 450 if (isOpen) { 451 this.rootElement.classList.add(isActive); 452 !Drawer.openDrawers.includes(this) && Drawer.openDrawers.push(this); 453 } else { 454 this.rootElement.classList.remove(isActive); 455 Drawer.openDrawers = Drawer.openDrawers.filter((drawer) => drawer !== this); 456 } 457 458 (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.lockScrolling)(Drawer.openDrawers.length === 1, 'drawer', Drawer.animationDelay); 459 460 if (Drawer.openDrawers.length === 0) { 461 Drawer.overlay.classList.remove(isVisible); 462 this.removeResizeListener(); 463 } else { 464 Drawer.overlay.classList.add(isVisible); 465 this.addResizeListener(); 466 } 467 } 468 469 toggle() { 470 this.state.isOpen = !this.state.isOpen; 471 } 472 473 close() { 474 this.state.isOpen = false; 475 } 476 477 createOverlay() { 478 if (!Drawer.overlay) { 479 Drawer.overlay = (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.createOverlay)('drawer-overlay'); 480 } 481 } 482 483 addResizeListener() { 484 if (!Drawer.resizeListener) { 485 window.addEventListener('resize', Drawer.closeAll); 486 Drawer.resizeListener = true; 487 } 488 } 489 490 removeResizeListener() { 491 if (Drawer.resizeListener) { 492 window.removeEventListener('resize', Drawer.closeAll); 493 Drawer.resizeListener = false;
494 } 495 } 496 497 onToggleButtonClick = () => { 498 if (Drawer.isAnimating) return; 499 500 if (!this.state.isOpen) { 501 this.toggle(); 502 } else { 503 Drawer.isAnimating = true; 504 this.toggle(); 505 setTimeout(() => { 506 Drawer.isAnimating = false; 507 }, Drawer.animationDelay); 508 } 509 }; 510 511 onCloseButtonClick = () => { 512 this.close(); 513 }; 514 515 static closeAll() { 516 Drawer.openDrawers.forEach((drawer) => drawer.close()); 517 } 518 519 bindEvents() { 520 this.toggleButtons.forEach((toggleButton) => { 521 toggleButton.addEventListener('click', this.onToggleButtonClick); 522 }); 523 this.closeButtons.forEach((closeButton) => { 524 closeButton.addEventListener('click', this.onCloseButtonClick); 525 }); 526 Drawer.overlay.addEventListener('click', Drawer.closeAll); 527 } 528} 529 530class initDrawer { 531 constructor() { 532 this.init(); 533 } 534 535 init() { 536 const drawers = document.querySelectorAll(drawerSelector); 537 drawers.forEach((drawer) => new Drawer(drawer)); 538 539 // Close all drawers when clicking on an anchor link 540 document.addEventListener('click', (e) => { 541 if (e.target.matches(`${drawerSelector} a[href*="#"]:not([href="#!"]`)) { 542 Drawer.closeAll(); 543 } 544 }); 545 } 546} 547 548 549/***/ }), 550
551/***/ "./js/components/Dropdown.js": 552/*!***************************************!*\ 553 !*** ./js/components/Dropdown.js ***! 554 \***************************************/ 555/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 556 557__webpack_require__.r(__webpack_exports__); 558/* harmony export */ __webpack_require__.d(__webpack_exports__, { 559/* harmony export */ "default": () => (/* binding */ initDropdown) 560/* harmony export */ }); 561/* harmony import */ var _BaseComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BaseComponent */ "./js/components/BaseComponent.js"); 562/* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/utils */ "./js/utils/utils.js"); 563 564 565const { computePosition, offset, flip, shift } = window.FloatingUIDOM; 566 567const dropdownSelector = '[data-dropdown]'; 568 569class Dropdown extends _BaseComponent__WEBPACK_IMPORTED_MODULE_0__["default"] { 570 selectors = { 571 button: '[data-dropdown-button]', 572 container: '[data-dropdown-container]', 573 }; 574 575 stateClasses = { 576 isVisible: 'is-visible', 577 }; 578 579 stateAttributes = { 580 ariaExpanded: 'aria-expanded', 581 dropdownHover: 'data-dropdown-hover', 582 }; 583 584 featureAttributes = { 585 fixedDropdown: 'data-fixed-dropdown', 586 }; 587 588 static openDropdowns = []; 589 static globalClickListener = false;
590 static hoverableMQ = window.matchMedia('(min-width: 830px)'); 591 static hoverSupport = Dropdown.evaluateHoverSupport(); 592 593 constructor(rootElement) { 594 super(); 595 this.rootElement = rootElement; 596 this.button = this.rootElement.querySelector(this.selectors.button); 597 this.container = this.rootElement.querySelector(this.selectors.container); 598 this.offset = parseInt((0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.getCssValue)(this.rootElement, '--dropdown-offset'), 10) || 0; 599 this.isHoverable = Dropdown.hoverSupport && this.rootElement.hasAttribute(this.stateAttributes.dropdownHover); 600 this.isToggler = this.rootElement.dataset.dropdownHover === 'toggler'; 601 this.hoverListeners = null; 602 this.containerClone = null; 603 this.isFixedDropdown = this.rootElement.hasAttribute(this.featureAttributes.fixedDropdown); 604 this.state = this.getProxyState({ isOpen: false }); 605 rootElement._instance = this; 606 607 this.bindEvents(); 608 } 609 610 updateUI() { 611 const { isOpen } = this.state; 612 const { isVisible } = this.stateClasses; 613 const { ariaExpanded } = this.stateAttributes; 614 const isSubDropdown = this.rootElement.parentElement.closest(dropdownSelector); 615 616 if (Dropdown.openDropdowns.length && !isSubDropdown) { 617 Dropdown.closeAll(); 618 } 619 620 this.button.setAttribute(ariaExpanded, isOpen); 621 622 if (isOpen) { 623 if (this.isFixedDropdown) { 624 this.createContainerClone(); 625 } else { 626 this.positionContainer(this.container); 627 this.container.classList.add(isVisible); 628 } 629 Dropdown.openDropdowns.push(this); 630 } else { 631 if (this.isFixedDropdown && this.containerClone) { 632 document.body.removeChild(this.containerClone); 633 this.containerClone = null; 634 } 635 this.container.classList.remove(isVisible); 636 Dropdown.openDropdowns.splice(Dropdown.openDropdowns.indexOf(this), 1); 637 } 638 } 639 640 createContainerClone() { 641 this.containerClone = this.container.cloneNode(true); 642 this.containerClone.style.position = 'fixed'; 643 document.body.appendChild(this.containerClone); 644 this.positionContainer(this.containerClone); 645 this.containerClone.classList.add(this.stateClasses.isVisible); 646 } 647 648 positionContainer(element) { 649 const isSubDropdown = this.rootElement.parentElement.closest(dropdownSelector); 650 // Floating UI 651 computePosition(this.button, element, { 652 middleware: [flip(), offset(this.offset), shift({ padding: 10 })], 653 placement: isSubDropdown ? 'right' : 'bottom', 654 }).then(({ x, y }) => { 655 element.style.left = `${x}px`; 656 element.style.top = `${y}px`; 657 }); 658 } 659 660 toggle() { 661 this.state.isOpen = !this.state.isOpen; 662 } 663 664 open() { 665 this.state.isOpen = true; 666 } 667 668 close() { 669 this.state.isOpen = false; 670 if (this.containerClone) { 671 document.body.removeChild(this.containerClone); 672 this.containerClone = null; 673 } 674 } 675 676 static closeAll() { 677 Dropdown.openDropdowns.forEach((dropdown) => { 678 dropdown.close(); 679 }); 680 } 681 682 addHoverListeners() { 683 this.rootElement.addEventListener('mouseenter', () => { 684 Dropdown.hoverSupport && this.open(); 685 }); 686 this.rootElement.addEventListener('mouseleave', () => { 687 Dropdown.hoverSupport && this.close(); 688 }); 689 this.hoverListeners = true; 690 } 691 692 static evaluateHoverSupport() { 693 return ( 694 Dropdown.hoverableMQ.matches && 695 (window.matchMedia('(any-hover: hover)').matches || !('ontouchstart' in window || navigator.maxTouchPoints > 0)) 696 ); 697 } 698 699 bindEvents() { 700 this.isHoverable && this.addHoverListeners(); 701 702 if (Dropdown.globalClickListener) return; 703 704 document.addEventListener('click', (e) => { 705 if (e.target.closest(this.selectors.button)) { 706 const dropdown = e.target.closest(dropdownSelector)._instance; 707 if (dropdown.isHoverable && !dropdown.isToggler) return; 708 e.preventDefault(); 709 dropdown.toggle(); 710 return; 711 } 712 if (Dropdown.openDropdowns.length && !e.target.closest(this.selectors.container)) { 713 Dropdown.closeAll(); 714 } 715 }); 716 717 Dropdown.globalClickListener = true; 718 } 719} 720 721class initDropdown { 722 constructor() { 723 this.init(); 724 } 725 726 init() { 727 const dropdowns = document.querySelectorAll(dropdownSelector); 728 dropdowns.forEach((dropdown) => new Dropdown(dropdown)); 729 730 const handleResize = (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.debounce)(() => { 731 const currentHoverSupport = Dropdown.evaluateHoverSupport(); 732 733 if (Dropdown.hoverSupport !== currentHoverSupport) { 734 Dropdown.hoverSupport = currentHoverSupport; 735 736 dropdowns.forEach((dropdown) => { 737 const instance = dropdown._instance; 738 if (instance.isHoverable && !instance.hoverListeners) { 739 instance.addHoverListeners(); 740 } 741 }); 742 } 743 }, 300); 744 745 window.addEventListener('resize', () => { 746 handleResize(); 747 Dropdown.openDropdowns.length && Dropdown.closeAll(); 748 }); 749 } 750} 751 752 753/***/ }), 754
755/***/ "./js/components/Sticky.js": 756/*!*************************************!*\ 757 !*** ./js/components/Sticky.js ***! 758 \*************************************/ 759/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 760 761__webpack_require__.r(__webpack_exports__); 762/* harmony export */ __webpack_require__.d(__webpack_exports__, { 763/* harmony export */ "default": () => (/* binding */ initSticky) 764/* harmony export */ }); 765/* harmony import */ var _BaseComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BaseComponent */ "./js/components/BaseComponent.js"); 766/* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/utils */ "./js/utils/utils.js"); 767 768 769 770const stickySelector = '[data-sticky]'; 771 772class Sticky extends _BaseComponent__WEBPACK_IMPORTED_MODULE_0__["default"] { 773 selectors = { 774 containerElement: 'data-sticky-container', 775 offsetElement: '[data-sticky-offset]', 776 stickyHeaderElement: 'header', 777 }; 778 779 stateClasses = { 780 isSticky: 'is-sticky', 781 isLocked: 'is-locked', 782 }; 783 784 stateAttributes = { 785 isOnTop: 'data-sticky-on-top', 786 }; 787 788 constructor(rootElement) { 789 super(); 790 this.rootElement = rootElement; 791 this.rootElement.sticky = this; 792 this.isHeader = this.rootElement.classList.contains(this.selectors.stickyHeaderElement); 793 this.isOnTop = this.rootElement.hasAttribute(this.stateAttributes.isOnTop); 794 this.stickyElementName = this.rootElement.dataset.sticky; 795 this.containerElement = this.getContainerElement(); 796 this.rootElementOffset = this.rootElement.offsetTop; 797 this.offsetElements = this.rootElement.querySelectorAll(this.selectors.offsetElement); 798 this.innerOffset = null; 799 this.state = this.getProxyState({ isSticky: false }); 800 801 this.applyInitialSettings(); 802 this.bindEvents(); 803 } 804 805 updateUI() { 806 const { isSticky } = this.state; 807 const container = this.containerElement; 808 const stickyHeight = `${this.rootElement.offsetHeight - this.innerOffset}px`; 809 const stickyHeightVar = `--sticky-${this.stickyElementName}-height`; 810 const stickyOffsetVar = `--sticky-${this.stickyElementName}-offset`; 811 812 if (isSticky) { 813 this.rootElement.classList.add(this.stateClasses.isSticky); 814 if (!this.innerOffset) return; 815 container.style.setProperty(stickyHeightVar, stickyHeight); 816 container.style.setProperty(stickyOffsetVar, `${this.innerOffset}px`); 817 } else { 818 if (this.isHeader && document.body.classList.contains(this.stateClasses.isLocked)) return; 819 820 this.rootElement.classList.remove(this.stateClasses.isSticky); 821 if (!this.innerOffset) return; 822 container.style.removeProperty(stickyHeightVar); 823 container.style.removeProperty(stickyOffsetVar); 824 } 825 } 826 827 setInnerOffset() { 828 this.innerOffset = 0; 829 this.offsetElements.forEach((offsetElement) => { 830 this.innerOffset += offsetElement.offsetHeight; 831 }); 832 } 833 834 setOuterOffset(boolean) { 835 if (!boolean) return; 836 this.rootElementOffset > 0 && 837 this.rootElement.style.setProperty(`--${this.stickyElementName}-offset`, `${this.rootElementOffset}px`); 838 } 839 840 setHeightVariable(boolean) { 841 boolean && 842 this.containerElement.style.setProperty( 843 `--${this.stickyElementName}-height`, 844 `${this.rootElement.offsetHeight}px`, 845 ); 846 } 847 848 applyOuterOffset() { 849 this.setOuterOffset(this.isOnTop); 850 this.setHeightVariable(this.isOnTop); 851 } 852 853 recalculateOffset() { 854 this.rootElementOffset = this.rootElement.offsetTop; 855 this.applyOuterOffset(); 856 this.setInnerOffset(); 857 this.updateUI(); 858 } 859 860 getContainerElement() { 861 return this.isHeader 862 ? document.body 863 : this.rootElement.closest(`[${this.selectors.containerElement}=${this.stickyElementName}]`); 864 } 865 866 toggleStickState() { 867 const mqMedium = window.matchMedia('(min-width: 829.98px)'); 868 if (mqMedium.matches) return; 869 870 window.scrollY > this.rootElementOffset + this.innerOffset 871 ? (this.state.isSticky = true) 872 : (this.state.isSticky = false); 873 } 874 875 applyInitialSettings() { 876 this.applyOuterOffset(); 877 this.setInnerOffset(); 878 this.toggleStickState(); 879 } 880 881 handleScroll() { 882 requestAnimationFrame(() => { 883 this.toggleStickState(); 884 }); 885 } 886 887 bindEvents() { 888 window.addEventListener('scroll', this.handleScroll.bind(this)); 889 window.addEventListener( 890 'resize', 891 (0,_utils_utils__WEBPACK_IMPORTED_MODULE_1__.debounce)(() => { 892 this.recalculateOffset(); 893 }, 300), 894 ); 895 } 896} 897 898class initSticky { 899 constructor() { 900 this.init(); 901 } 902 903 init() { 904 const stickyElements = document.querySelectorAll(stickySelector);
905 stickyElements.forEach((element) => new Sticky(element)); 906 } 907} 908 909 910/***/ }), 911
912/***/ "./js/components/categories-nav.js": 913/*!*********************************************!*\ 914 !*** ./js/components/categories-nav.js ***! 915 \*********************************************/ 916/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 917 918__webpack_require__.r(__webpack_exports__); 919/* harmony export */ __webpack_require__.d(__webpack_exports__, { 920/* harmony export */ initCategoriesNav: () => (/* binding */ initCategoriesNav) 921/* harmony export */ }); 922function initCategoriesNav() { 923 const catNavOpeners = document.querySelectorAll('[data-cat-nav-opener]'); 924 const catNav = document.querySelector('.cat-nav'); 925 const catNavLinks = catNav.querySelectorAll('.sublist-toggler'); 926 const catSubLists = catNav.querySelectorAll('.nav__sublist'); 927 const catCloseButtons = catNav.querySelectorAll('[data-cat-nav-close]'); 928 const catOverlay = document.querySelector('.cat-overlay'); 929 const mqSmall = window.matchMedia('(max-width: 829.98px)'); 930 931 // Open categories menu 932 catNavOpeners.forEach((button) => { 933 button.addEventListener('click', (e) => { 934 e.preventDefault(); 935 catNav.classList.toggle('is-visible'); 936 catOverlay.classList.toggle('is-visible'); 937 catNavOpeners.forEach((button) => { 938 button.classList.toggle('is-active'); 939 }); 940 document.body.classList.toggle('is-fixed', catNav.classList.contains('is-visible')); 941 document.body.classList.toggle( 942 'has-scroll', 943 document.body.scrollHeight > window.innerHeight && catNav.classList.contains('is-visible'), 944 ); 945 }); 946 }); 947 948 // Toggle the categories menu state 949 function toggleCatMenu() { 950 if (mqSmall.matches) { 951 catNavLinks.forEach((link) => { 952 link.removeEventListener('mouseover', mouseoverCatlinksHandler); 953 link.addEventListener('click', clickCatLinksHandler); 954 }); 955 catSubLists.forEach((sublist) => sublist.classList.remove('is-visible')); 956 return; 957 } 958 catNavLinks.forEach((link) => { 959 link.removeEventListener('click', clickCatLinksHandler); 960 link.addEventListener('mouseover', mouseoverCatlinksHandler); 961 }); 962 catSubLists[0].classList.add('is-visible'); 963 } 964 965 // Mobile categories menu 966 function clickCatLinksHandler(e) { 967 e.preventDefault(); 968 const link = e.currentTarget; 969 catSubLists.forEach((sublist) => { 970 if (sublist.dataset.sublistIndex === link.dataset.sublistIndex) { 971 return sublist.classList.add('is-visible'); 972 } 973 sublist.classList.remove('is-visible'); 974 }); 975 } 976 977 // Desktop categories menu 978 function mouseoverCatlinksHandler() { 979 const link = this; 980 link.classList.add('is-active'); 981 catNavLinks.forEach((otherLink) => { 982 if (otherLink !== link) { 983 otherLink.classList.remove('is-active'); 984 } 985 }); 986 catSubLists.forEach((sublist) => { 987 if (sublist.dataset.sublistIndex === link.dataset.sublistIndex) { 988 return sublist.classList.add('is-visible'); 989 } 990 sublist.classList.remove('is-visible'); 991 }); 992 } 993 994 mqSmall.addEventListener('change', toggleCatMenu); 995 996 toggleCatMenu(); 997 998 // Mobile categories menu return buttons 999 const mobileNavReturnButtons = document.querySelectorAll('.nav__mobile-return'); 1000 mobileNavReturnButtons.forEach((button) => { 1001 button.addEventListener('click', () => { 1002 const submenu = button.closest('.nav__sublist'); 1003 submenu.classList.remove('is-visible'); 1004 }); 1005 }); 1006 1007 catOverlay.addEventListener('click', () => { 1008 closeCatNav(); 1009 }); 1010 1011 catCloseButtons.forEach((button) => { 1012 button.addEventListener('click', () => { 1013 closeCatNav(); 1014 }); 1015 }); 1016 1017 window.addEventListener('click', (e) => { 1018 if (!catNav.contains(e.target) && e.target.closest('[data-cat-nav-opener]') === null) { 1019 closeCatNav(); 1020 } 1021 }); 1022 1023 function closeCatNav() { 1024 catNav.classList.remove('is-visible'); 1025 catOverlay.classList.remove('is-visible'); 1026 catNavOpeners.forEach((button) => { 1027 button.classList.remove('is-active'); 1028 }); 1029 document.body.classList.remove('is-fixed'); 1030 document.body.classList.remove('has-scroll'); 1031 } 1032} 1033 1034 1035/***/ }), 1036
1037/***/ "./js/components/drag-nav.js": 1038/*!***************************************!*\ 1039 !*** ./js/components/drag-nav.js ***! 1040 \***************************************/ 1041/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1042 1043__webpack_require__.r(__webpack_exports__); 1044/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1045/* harmony export */ "default": () => (/* binding */ DragNav) 1046/* harmony export */ }); 1047/* Draggable navigation component */ 1048class DragNav { 1049 constructor(selector, options) { 1050 if (typeof selector === 'string') { 1051 const elements = document.querySelectorAll(selector); 1052 if (elements.length > 1) { 1053 elements.forEach((element) => new DragNav(element, options)); 1054 return; 1055 } 1056 selector = elements[0]; 1057 } 1058 1059 if (!selector) { 1060 return console.error('Drag Nav: Selector not found.'); 1061 } 1062 1063 this.container = selector.querySelector('.drag-nav-container'); 1064 this.items = selector.querySelectorAll('a, div'); 1065 1066 this.startX = null; 1067 this.scrollLeft = null; 1068 1069 this.mouseMoveHandler = this.mouseMoveHandler.bind(this); 1070 this.mouseUpHandler = this.mouseUpHandler.bind(this); 1071 1072 this.events(); 1073 } 1074 1075 events() { 1076 this.container.addEventListener('mousedown', (e) => { 1077 this.startX = e.pageX - this.container.offsetLeft; 1078 this.scrollLeft = this.container.scrollLeft; 1079 1080 document.addEventListener('mousemove', this.mouseMoveHandler); 1081 document.addEventListener('mouseup', this.mouseUpHandler); 1082 }); 1083 } 1084 1085 mouseMoveHandler(e) { 1086 // Disable default browser drag & drop behavior 1087 this.items.forEach((link) => { 1088 link.addEventListener('dragstart', (e) => { 1089 e.preventDefault(); 1090 }); 1091 }); 1092 const x = e.pageX - this.container.offsetLeft; 1093 const distance = (x - this.startX) * 1; 1094 // Disable pointer events on links while dragging 1095 if (Math.abs(distance) > 10) { 1096 this.container.classList.add('is-dragging'); 1097 } 1098 this.container.scrollLeft = this.scrollLeft - distance; 1099 } 1100 1101 mouseUpHandler() { 1102 this.container.classList.remove('is-dragging'); 1103 document.removeEventListener('mousemove', this.mouseMoveHandler); 1104 document.removeEventListener('mouseup', this.mouseUpHandler); 1105 } 1106} 1107 1108 1109/***/ }), 1110
1111/***/ "./js/components/greedy-nav.js": 1112/*!*****************************************!*\ 1113 !*** ./js/components/greedy-nav.js ***! 1114 \*****************************************/ 1115/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1116 1117__webpack_require__.r(__webpack_exports__); 1118/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1119/* harmony export */ "default": () => (/* binding */ GreedyNav) 1120/* harmony export */ }); 1121/* Greedy navigation component */ 1122/* Dinamically calculates total width of items and shows/hides them when needed */ 1123class GreedyNav { 1124 constructor(selector, options) { 1125 if (typeof selector === 'string') { 1126 const elements = document.querySelectorAll(selector); 1127 if (elements.length > 1) { 1128 elements.forEach((element) => new GreedyNav(element, options)); 1129 return; 1130 } 1131 selector = elements[0]; 1132 } 1133 1134 if (!selector) { 1135 return console.error('Greedy Nav: Selector not found.'); 1136 } 1137 1138 const defaultOptions = { 1139 visibleItemsContainer: '.greedy-items', 1140 item: '.greedy-item', 1141 button: '.greedy-button', 1142 buttonPlaceholder: 18, 1143 hiddenItemsContainer: '.greedy-hidden-items', 1144 minWidth: 830, 1145 }; 1146 1147 this.options = Object.assign(defaultOptions, options); 1148 this.container = selector; 1149 1150 this.visibleItemsContainer = this.container.querySelector(this.options.visibleItemsContainer); 1151 this.items = this.container.querySelectorAll(this.options.item); 1152 this.button = this.container.querySelector(this.options.button); 1153 this.hiddenItemsContainer = this.container.querySelector(this.options.hiddenItemsContainer); 1154 1155 this.availableSpace = 0; 1156 this.totalItems = 0; 1157 this.totalOccupiedSpace = 0; 1158 this.itemBreakpoints = []; 1159 this.placeholderNeeded = false; 1160 1161 this.init(); 1162 this.events(); 1163 } 1164 1165 init() { 1166 this.items.forEach((item) => { 1167 this.totalItems += 1; 1168 this.totalOccupiedSpace += item.offsetWidth; 1169 this.itemBreakpoints.push(this.totalOccupiedSpace); 1170 }); 1171 1172 this.checkAvailableSpace(); 1173 } 1174 1175 checkAvailableSpace() { 1176 const viewportWidth = window.innerWidth; 1177 1178 if (viewportWidth < this.options.minWidth) { 1179 // Restore all hidden items to the visible container 1180 while (this.hiddenItemsContainer.children.length > 0) { 1181 this.visibleItemsContainer.appendChild(this.hiddenItemsContainer.firstElementChild); 1182 } 1183 this.placeholderNeeded = false; 1184 this.updateButtonState(); 1185 return; 1186 } 1187 1188 // Proceed with greedy logic 1189 while (true) { 1190 this.availableSpace = this.container.offsetWidth - (this.placeholderNeeded ? this.options.buttonPlaceholder : 0); 1191 this.sumOfVisibleItems = this.visibleItemsContainer.children.length; 1192 this.requiredSpace = this.itemBreakpoints[this.sumOfVisibleItems - 1]; 1193 1194 if (this.requiredSpace > this.availableSpace && this.sumOfVisibleItems > 0) { 1195 this.hiddenItemsContainer.prepend(this.visibleItemsContainer.lastElementChild); 1196 this.sumOfVisibleItems -= 1; 1197 this.placeholderNeeded = true; 1198 } else if ( 1199 this.itemBreakpoints[this.totalItems - 1] <= this.container.offsetWidth && 1200 this.hiddenItemsContainer.children.length > 0 1201 ) { 1202 this.visibleItemsContainer.appendChild(this.hiddenItemsContainer.firstElementChild); 1203 this.sumOfVisibleItems += 1; 1204 this.placeholderNeeded = false; 1205 } else if ( 1206 this.availableSpace >= this.itemBreakpoints[this.sumOfVisibleItems] && 1207 this.hiddenItemsContainer.children.length > 0 1208 ) { 1209 this.visibleItemsContainer.appendChild(this.hiddenItemsContainer.firstElementChild); 1210 this.sumOfVisibleItems += 1; 1211 } else { 1212 break; 1213 } 1214 } 1215 1216 this.updateButtonState(); 1217 } 1218 1219 updateButtonState() { 1220 this.button.setAttribute('data-counter', this.totalItems - this.visibleItemsContainer.children.length); 1221 if (this.visibleItemsContainer.children.length === this.totalItems) { 1222 this.button.classList.remove('is-visible'); 1223 } else { 1224 this.button.classList.add('is-visible'); 1225 } 1226 } 1227 1228 events() { 1229 window.addEventListener('resize', this.debounce(this.checkAvailableSpace.bind(this), 500)); 1230 } 1231 1232 debounce(fn, time) { 1233 let timeout; 1234 return function () { 1235 clearTimeout(timeout); 1236 const context = this; 1237 const args = arguments; 1238 timeout = setTimeout(() =>
1238 fn.apply(context, args), time); 1239 }; 1240 } 1241} 1242 1243 1244/***/ }), 1245
1246/***/ "./js/libs/custom-select-settings.js": 1247/*!***********************************************!*\ 1248 !*** ./js/libs/custom-select-settings.js ***! 1249 \***********************************************/ 1250/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1251 1252__webpack_require__.r(__webpack_exports__); 1253/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1254/* harmony export */ initCustomSelect: () => (/* binding */ initCustomSelect) 1255/* harmony export */ }); 1256/* Custom select settings */ 1257function initCustomSelect() { 1258 if (typeof customSelect === 'undefined') return; 1259 1260 const headerSelects = customSelect('.header-select'); 1261 const selects = customSelect('.select'); 1262 1263 // Improve custom select aria 1264 const selectOpeners = document.querySelectorAll('.custom-select-opener'); 1265 1266 selectOpeners.forEach((opener) => { 1267 opener.setAttribute('aria-label', 'Select category'); 1268 }); 1269 1270 // Inject data-flag into custom options 1271 document.querySelectorAll('.header-lang').forEach((customSelect) => { 1272 const instance = customSelect.querySelector('.header-select').customSelect; 1273 const select = instance.select; 1274 const options = customSelect.querySelectorAll('option'); 1275 const customItems = customSelect.parentNode.querySelectorAll('.custom-select-option'); 1276 1277 options.forEach((option, index) => { 1278 const flag = option.dataset.flag; 1279 if (flag && customItems[index]) { 1280 customItems[index].setAttribute('data-flag', flag); 1281 } 1282 }); 1283 1284 // Set initial flag on opener 1285 const selectedOption = select.options[select.selectedIndex]; 1286 const initialFlag = selectedOption.dataset.flag; 1287 if (initialFlag) { 1288 instance.opener.setAttribute('data-flag', initialFlag); 1289 } 1290 1291 select.addEventListener('change', () => { 1292 const selectedOption = select.options[select.selectedIndex]; 1293 const flag = selectedOption.dataset.flag; 1294 1295 if (flag) { 1296 instance.opener.setAttribute('data-flag', flag); 1297 } 1298 }); 1299 }); 1300} 1301 1302 1303/***/ }), 1304
1305/***/ "./js/libs/intl-tel-settings.js": 1306/*!******************************************!*\ 1307 !*** ./js/libs/intl-tel-settings.js ***! 1308 \******************************************/ 1309/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1310 1311__webpack_require__.r(__webpack_exports__); 1312/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1313/* harmony export */ initIntlTel: () => (/* binding */ initIntlTel) 1314/* harmony export */ }); 1315/* International Telephone Input settings */ 1316function initIntlTel() { 1317 if (typeof intlTelInput === 'undefined') return; 1318 1319 const callbackBtn = document.querySelector('.link[data-dialog-open="call"]'); 1320 const callbackDialog = document.querySelector('.dialog-container[data-dialog="call"]'); 1321 1322 callbackBtn && 1323 callbackBtn.addEventListener('click', initIntlTelInput, { 1324 once: true, 1325 }); 1326 1327 function initIntlTelInput() { 1328 const callbackIntlTelInput = callbackDialog.querySelector('.intl-phone'); 1329 intlTelInput(callbackIntlTelInput, { 1330 utilsScript: './vendor/intl-tel/js/utils.js', 1331 useFullscreenPopup: false, 1332 showSelectedDialCode: true, 1333 initialCountry: $('#initial_country').val(), 1334 onlyCountries: JSON.parse($('#country_iso').val()), 1335 }); 1336 } 1337 1338 const intlInputs = document.querySelectorAll('form:not(.callback-form) .intl-phone'); 1339 1340 intlInputs.forEach((input) => { 1341 intlTelInput(input, { 1342 utilsScript: './vendor/intl-tel/js/utils.js', 1343 useFullscreenPopup: false, 1344 showSelectedDialCode: true, 1345 initialCountry: $('#initial_country').val(), 1346 onlyCountries: JSON.parse($('#country_iso').val()), 1347 }); 1348 }); 1349} 1350 1351 1352/***/ }), 1353
1354/***/ "./js/libs/just-validate-settings.js": 1355/*!***********************************************!*\ 1356 !*** ./js/libs/just-validate-settings.js ***! 1357 \***********************************************/ 1358/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1359 1360__webpack_require__.r(__webpack_exports__); 1361/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1362/* harmony export */ initJustValidate: () => (/* binding */ initJustValidate) 1363/* harmony export */ }); 1364/* Just validate settings */ 1365function initJustValidate() { 1366 if (typeof JustValidate === 'undefined') return; 1367 1368 const forms = document.querySelectorAll('.form:not(.form--native-submit)'); 1369 1370 forms.forEach((form) => { 1371 const validator = new JustValidate(form, { 1372 errorLabelStyle: { 1373 // Remove default inline styling 1374 color: false, 1375 }, 1376 errorFieldCssClass: 'error-field', 1377 errorLabelCssClass: 'error-label', 1378 }); 1379 1380 /* Text fields */ 1381 const textFields = form.querySelectorAll('.input-text[required]'); 1382 const strictTextFieldValidation = true; 1383 const strictTextFieldValidationRuleset = { 1384 rule: 'customRegexp', 1385 errorMessage: 'Incorrect input format', 1386 // Latin & russian letters, numbers, space, hyphens 1387 value: /^[A-Za-z0-9Ð-Яа-ÑÐÑ\s\-ââ]+$/gi, 1388 }; 1389 1390 textFields.forEach((field) => { 1391 validator.addField(field, [ 1392 { 1393 rule: 'required', 1394 errorMessage: 'Required field', 1395 }, 1396 ...(strictTextFieldValidation ? [strictTextFieldValidationRuleset] : []), 1397 { 1398 rule: 'minLength', 1399 errorMessage: 'Minimum 2 characters', 1400 value: 2, 1401 }, 1402 { 1403 rule: 'maxLength', 1404 errorMessage: 'Maximum 30 characters', 1405 value: 30, 1406 }, 1407 ]); 1408 }); 1409 1410 /* Textarea fields */ 1411 const textareaFields = form.querySelectorAll('.input-textarea[required]'); 1412 const strictTextareaValidation = false; 1413 const strictTextareaValidationRuleset = { 1414 rule: 'customRegexp', 1415 errorMessage: 'Incorrect input format', 1416 // Latin & russian letters, numbers, space, 1417 // general punctuation marks, quotes 1418 value: /^[A-Za-z0-9Ð-Яа-ÑÐÑ\s\-_.,!?;:'"«»ââââ()]+$/gi, 1419 }; 1420 1421 textareaFields.forEach((field) => { 1422 validator.addField(field, [ 1423 { 1424 rule: 'required', 1425 errorMessage: 'Required field', 1426 }, 1427 ...(strictTextareaValidation ? [strictTextareaValidationRuleset] : []), 1428 { 1429 rule: 'minLength', 1430 errorMessage: 'Minimum 5 characters', 1431 value: 5, 1432 }, 1433 { 1434 rule: 'maxLength', 1435 errorMessage: 'Maximum 2000 characters', 1436 value: 2000, 1437 }, 1438 ]); 1439 }); 1440 1441 /* Email fields */ 1442 const emailFields = form.querySelectorAll('.input-email[required]'); 1443 1444 emailFields.forEach((field) => { 1445 validator.addField(field, [ 1446 { 1447 rule: 'required', 1448 errorMessage: 'Required field', 1449 }, 1450 { 1451 rule: 'email', 1452 errorMessage: 'Invalid email format', 1453 }, 1454 ]); 1455 }); 1456 1457 /* Phone number fields */ 1458 const phoneFields = form.querySelectorAll('.input-tel[required]'); 1459 // 11 digit + iMask symbols format 1460 const iMaskPhoneRegex = /^(?:\+7|(?:\D*\d){11}\D*)$/gi; 1461 // Russian phone number formats + simple 7 digit format 1462 const russianPhoneRegex = 1463 /^(?:\+?7|8)?[\s-]?(?:\(?(?:9\d{2}|4[6-9]\d|5\d{2}|3[0-8]\d|82\d|8[1-9][0-9])\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}|\d{3}[\s-]?\d{2}[\s-]?\d{2})$/gi; 1464 // Simplified phone number format 1465 const simplifiedPhoneRegex = /^(?:\D*\d){7,10}\D*$/gi; 1466 1467 phoneFields.forEach((field) => { 1468 validator.addField(field, [ 1469 { 1470 rule: 'required', 1471 errorMessage: 'Required field', 1472 }, 1473 { 1474 rule: 'customRegexp', 1475 errorMessage: 'Invalid phone number', 1476 value: simplifiedPhoneRegex, 1477 }, 1478 ]); 1479 }); 1480 1481 /* Password fields */ 1482 const passwordFields = form.querySelectorAll('.input-password[required]'); 1483 1484 passwordFields.forEach((field) => { 1485 validator.addField(field, [ 1486 { 1487 rule: 'required', 1488 errorMessage: 'Required field', 1489 }, 1490 { 1491 rule: 'minLength', 1492 errorMessage: 'Minimum 8 characters', 1493 value: 8, 1494 }, 1495 ]); 1496 }); 1497 1498 /* Checkboxes */ 1499 const checkboxes = form.querySelectorAll('.input-checkbox[required]'); 1500
1501 checkboxes.forEach((checkbox) => { 1502 validator.addField(checkbox, [ 1503 { 1504 rule: 'required', 1505 errorMessage: 'Required field', 1506 }, 1507 ]); 1508 }); 1509 1510 /* Radio groups */ 1511 const radioGroups = form.querySelectorAll('.radio-fieldset--required'); 1512 1513 radioGroups.forEach((radioGroup) => { 1514 validator.addRequiredGroup(radioGroup, 'Select an option'); 1515 }); 1516 1517 /* Selects */ 1518 const selects = form.querySelectorAll('select[required]'); 1519 1520 selects.forEach((select) => { 1521 validator.addField(select, [ 1522 { 1523 rule: 'required', 1524 errorMessage: 'Select an option', 1525 }, 1526 ]); 1527 }); 1528 1529 validator.onFail((e) => { 1530 // Error logic goes here 1531 }); 1532 1533 validator.onSuccess((e) => { 1534 closeDialog(form); 1535 // Success logic goes here 1536 1537 /* 1538 Example: redirect to success page upon form submission 1539 */ 1540 // const rootLocation = 'https://domen.name'; 1541 // const successPage = 'success.html'; 1542 // window.location.href = rootLocation + successPage; 1543 /* 1544 Example: show status dialog upon form submission 1545 */ 1546 // const xhr = new XMLHttpRequest(); 1547 // const formData = new FormData(form); 1548 // xhr.open('POST', form.getAttribute('action')); 1549 // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 1550 // xhr.send(formData); 1551 // xhr.onreadystatechange = function () { 1552 // const statusDialogSuccess = document.querySelector('dialog[data-dialog="success"]'); 1553 // // Form send successfully 1554 // if (this.readyState === 4 && this.status === 200) { 1555 // statusDialogSuccess.showModal(); 1556 // } 1557 // }; 1558 // xhr.onerror = function () { 1559 // const statusDialogError = document.querySelector('dialog[data-dialog="error"]'); 1560 // statusDialogError.showModal(); 1561 // }; 1562 }); 1563 }); 1564} 1565 1566function closeDialog(form) { 1567 if (form.method !== 'dialog') return; 1568 const dialog = form.closest('dialog')._instance; 1569 dialog.close(); 1570} 1571 1572 1573/***/ }), 1574
1575/***/ "./js/utils/utils.js": 1576/*!*******************************!*\ 1577 !*** ./js/utils/utils.js ***! 1578 \*******************************/ 1579/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1580 1581__webpack_require__.r(__webpack_exports__); 1582/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1583/* harmony export */ applyInitialSettings: () => (/* binding */ applyInitialSettings), 1584/* harmony export */ checkDialog: () => (/* binding */ checkDialog), 1585/* harmony export */ createOverlay: () => (/* binding */ createOverlay), 1586/* harmony export */ debounce: () => (/* binding */ debounce), 1587/* harmony export */ getCssValue: () => (/* binding */ getCssValue), 1588/* harmony export */ initBrowserChecks: () => (/* binding */ initBrowserChecks), 1589/* harmony export */ lockScrolling: () => (/* binding */ lockScrolling) 1590/* harmony export */ }); 1591/** 1592 Browser utilities 1593================= **/ 1594 1595/* Check if JavaScript is enabled */ 1596function checkJS() { 1597 document.documentElement.classList.remove('no-js'); 1598} 1599 1600/* Detect browser */ 1601function detectBrowser() { 1602 const test = (regexp) => regexp.test(navigator.userAgent); 1603 const browser = (() => { 1604 switch (true) { 1605 case test(/edg/i): 1606 return 'edge'; 1607 case test(/trident/i): 1608 return 'ie'; 1609 case test(/firefox|fxios/i): 1610 return 'firefox'; 1611 case test(/opr\//i): 1612 return 'opera'; 1613 case test(/chrome|chromium|crios/i): 1614 return 'chrome'; 1615 case test(/safari|AppleWebKit/i): 1616 return 'safari'; 1617 default: 1618 return 'unknown'; 1619 } 1620 })(); 1621 1622 document.documentElement.classList.add(`ua-${browser}`); 1623} 1624 1625/* Check if browser supports .webp-images */ 1626function checkWebp() { 1627 const webp = new Image(); 1628 webp.src = 1629 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA'; 1630 1631 webp.onload = webp.onerror = () => { 1632 const isWebpSupported = webp.height === 2; 1633 if (!isWebpSupported) document.documentElement.classList.remove('webp'); 1634 }; 1635} 1636 1637/* Check if browser supports dialog element */ 1638function checkDialog() { 1639 document.addEventListener('DOMContentLoaded', function () { 1640 const dialog = document.querySelector('dialog'); 1641 try { 1642 dialog && dialog.close(); 1643 } catch (e) { 1644 const head = document.getElementsByTagName('HEAD')[0]; 1645 const link = document.createElement('link'); 1646 const script = document.createElement('script'); 1647 const dialogs = document.querySelectorAll('dialog'); 1648 link.rel = 'stylesheet'; 1649 link.type = 'text/css'; 1650 link.href = 'vendor/dialog-polyfill/dialog-polyfill.min.css'; 1651 script.src = 'vendor/dialog-polyfill/dialog-polyfill.min.js'; 1652 head.append(link, script); 1653 script.addEventListener('load', () => {
1654 dialogs.forEach((dialog) => { 1655 dialogPolyfill.registerDialog(dialog); 1656 }); 1657 }); 1658 } 1659 }); 1660} 1661 1662function initBrowserChecks() { 1663 checkJS(); 1664 detectBrowser(); 1665 checkWebp(); 1666 checkDialog(); 1667} 1668 1669/** 1670 DOM utilities 1671============= **/ 1672 1673/* Disable a[href="#!"] links */ 1674function disableEmptyLinks() { 1675 document.addEventListener('click', (e) => { 1676 if (e.target.matches('a[href="#!"]')) { 1677 e.preventDefault(); 1678 } 1679 }); 1680} 1681 1682/* Disable CSS transitions during window resize */ 1683function disableTransitionsOnResize(className = 'no-transitions', delay = 300) { 1684 const handleResize = debounce(() => { 1685 document.body.classList.remove(className); 1686 }, delay); 1687 1688 window.addEventListener('resize', () => { 1689 document.body.classList.add(className); 1690 handleResize(); 1691 }); 1692} 1693 1694function applyInitialSettings() { 1695 disableEmptyLinks(); 1696 disableTransitionsOnResize(); 1697} 1698 1699/** 1700 General utilities 1701================= **/ 1702 1703/* Debounce function to limit its execution rate */ 1704function debounce(func, delay) { 1705 let timeout; 1706 return function (...args) { 1707 clearTimeout(timeout); 1708 timeout = setTimeout(() => func.apply(this, args), delay); 1709 }; 1710} 1711 1712/* Get CSS property value */ 1713function getCssValue(element, property) { 1714 return window.getComputedStyle(element).getPropertyValue(property); 1715} 1716 1717/** 1718 UI utilities 1719============ **/ 1720 1721/* Create overlay element */ 1722function createOverlay(className, parent = document.body, filter = false) { 1723 const overlay = document.createElement('div'); 1724 if (filter) { 1725 const filter = document.createElement('div'); 1726 filter.classList.add('overlay__filter'); 1727 overlay.appendChild(filter); 1728 } 1729 overlay.classList.add(className, 'overlay'); 1730 parent.appendChild(overlay); 1731 return overlay; 1732} 1733 1734/* Lock scrolling */ 1735function lockScrolling(enableLock, source, delay = 0) { 1736 const { documentElement: html, body } = document; 1737 const currentSource = body.dataset.lockSource; 1738 const hasSameSource = currentSource === source; 1739 const scrollY = window.scrollY; 1740 1741 // Toggle lock state and document scrollbar 1742 if (!currentSource || hasSameSource) { 1743 body.classList.toggle('is-locked', enableLock); 1744 html.classList.toggle('has-scrollbar', body.scrollHeight > window.innerHeight && enableLock); 1745 } 1746 1747 // Toggle lock source 1748 if (enableLock && !currentSource) { 1749 body.dataset.lockSource = source; 1750 } else if (!enableLock && hasSameSource) { 1751 body.removeAttribute('data-lock-source'); 1752 } 1753 1754 // Manage scroll offset 1755 if (enableLock && scrollY > 0) { 1756 body.style.setProperty('--top-body-offset', `-${scrollY}px`); 1757 } else if (hasSameSource) { 1758 const offset = Math.abs(parseInt(getCssValue(body, '--top-body-offset') || '0', 10)); 1759 setTimeout(() => { 1760 body.style.removeProperty('--top-body-offset'); 1761 }, delay); 1762 requestAnimationFrame(() => { 1763 window.scrollTo({ top: offset, left: 0, behavior: 'instant' }); 1764 }); 1765 } 1766} 1767 1768 1769/***/ })
1769 1770 1771/******/ }); 1772/************************************************************************/ 1773/******/ // The module cache 1774/******/ var __webpack_module_cache__ = {}; 1775/******/ 1776/******/ // The require function 1777/******/ function __webpack_require__(moduleId) { 1778/******/ // Check if module is in cache 1779/******/ var cachedModule = __webpack_module_cache__[moduleId]; 1780/******/ if (cachedModule !== undefined) { 1781/******/ return cachedModule.exports; 1782/******/ } 1783/******/ // Create a new module (and put it into the cache) 1784/******/ var module = __webpack_module_cache__[moduleId] = { 1785/******/ // no module.id needed 1786/******/ // no module.loaded needed 1787/******/ exports: {} 1788/******/ }; 1789/******/ 1790/******/ // Execute the module function 1791/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 1792/******/ 1793/******/ // Return the exports of the module 1794/******/ return module.exports; 1795/******/ } 1796/******/ 1797/************************************************************************/ 1798/******/ /* webpack/runtime/define property getters */ 1799/******/ (() => { 1800/******/ // define getter functions for harmony exports 1801/******/ __webpack_require__.d = (exports, definition) => { 1802/******/ for(var key in definition) { 1803/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 1804/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 1805/******/ } 1806/******/ } 1807/******/ }; 1808/******/ })(); 1809/******/ 1810/******/ /* webpack/runtime/hasOwnProperty shorthand */ 1811/******/ (() => { 1812/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 1813/******/ })(); 1814/******/ 1815/******/ /* webpack/runtime/make namespace object */ 1816/******/ (() => { 1817/******/ // define __esModule on exports 1818/******/ __webpack_require__.r = (exports) => { 1819/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 1820/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 1821/******/ } 1822/******/ Object.defineProperty(exports, '__esModule', { value: true }); 1823/******/ }; 1824/******/ })(); 1825/******/ 1826/************************************************************************/ 1827var __webpack_exports__ = {}; 1828// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 1829(() => { 1830/*!************************!*\ 1831 !*** ./js/main.js ***! 1832 \************************/ 1833__webpack_require__.r(__webpack_exports__); 1834/* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/utils */ "./js/utils/utils.js"); 1835/* harmony import */ var _components_Sticky__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/Sticky */ "./js/components/Sticky.js"); 1836/* harmony import */ var _components_Accordion__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/Accordion */ "./js/components/Accordion.js"); 1837/* harmony import */ var _components_Dialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/Dialog */ "./js/components/Dialog.js"); 1838/* harmony import */ var _components_Drawer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/Drawer */ "./js/components/Drawer.js"); 1839/* harmony import */ var _components_Dropdown__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/Dropdown */ "./js/components/Dropdown.js"); 1840/* harmony import */ var _components_greedy_nav__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./components/greedy-nav */ "./js/components/greedy-nav.js"); 1841/* harmony import */ var _components_drag_nav__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./components/drag-nav */ "./js/components/drag-nav.js"); 1842/* harmony import */ var _libs_just_validate_settings__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./libs/just-validate-settings */ "./js/libs/just-validate-settings.js"); 1843/* harmony import */ var _libs_custom_select_settings__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./libs/custom-select-settings */ "./js/libs/custom-select-settings.js"); 1844/* harmony import */ var _libs_intl_tel_settings__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./libs/intl-tel-settings */ "./js/libs/intl-tel-settings.js"); 1845/* harmony import */ var _components_categories_nav__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./components/categories-nav */ "./js/components/categories-nav.js"); 1846/** 1847 Inintial checks & settings 1848========================== **/ 1849 1850 1851(0,_utils_utils__WEBPACK_IMPORTED_MODULE_0__.initBrowserChecks)(); 1852(0,_utils_utils__WEBPACK_IMPORTED_MODULE_0__.applyInitialSettings)(); 1853 1854/** 1855 Components 1856========== **/ 1857 1858 1859 1860 1861 1862 1863 1864 1865new _components_Sticky__WEBPACK_IMPORTED_MODULE_1__["default"](); 1866new _components_Accordion__WEBPACK_IMPORTED_MODULE_2__["default"](); 1867new _components_Dialog__WEBPACK_IMPORTED_MODULE_3__["default"](); 1868new _components_Drawer__WEBPACK_IMPORTED_MODULE_4__["default"](); 1869new _components_Dropdown__WEBPACK_IMPORTED_MODULE_5__["default"](); 1870const greedyNav = new _components_greedy_nav__WEBPACK_IMPORTED_MODULE_6__["default"]('.greedy-nav'); 1871const dragNav = new _components_drag_nav__WEBPACK_IMPORTED_MODULE_7__["default"]('.drag-nav'); 1872 1873/** 1874 Form elements UX 1875================ **/ 1876 1877/* Autoresizible textarea */ 1878 1879const textareaFields = document.querySelectorAll('.textarea-field'); 1880 1881textareaFields.forEach((field) => { 1882 const textarea = field.querySelector('.input-textarea'); 1883 const initialHeight = textarea.offsetHeight; 1884 const initialOffset = initialHeight - textarea.clientHeight; 1885 1886 textarea.addEventListener('input', () => { 1887 textarea.style.height = initialHeight + 'px'; 1888 textarea.style.height = textarea.closest('.dialog') 1889 ? textarea.scrollHeight + initialOffset + 2 + 'px' 1890 : textarea.scrollHeight + initialOffset + 'px'; 1891 // Fix clumsy textarea scroll 1892 textarea.scrollTo(0, textarea.scrollHeight); 1893 }); 1894}); 1895 1896/* Quantity inputs */ 1897 1898document.querySelectorAll('.qty-input__plus').forEach((button) => { 1899 button.addEventListener('click', () => { 1900 const input = button.parentNode.querySelector('.qty-input__qty-field'); 1901 let val = parseInt(input.value); 1902 isNaN(val) && (val = 0); 1903 input.value = val + 1; 1904 }); 1905}); 1906 1907document.querySelectorAll('.qty-input__minus').forEach((button) => { 1908 button.addEventListener('click', () => { 1909 const input = button.parentNode.querySelector('.qty-input__qty-field'); 1910 const val = parseInt(input.value); 1911 if (val > 1) { 1912 input.value = val - 1; 1913 } 1914 }); 1915}); 1916 1917/* Show uploaded file name */ 1918 1919const fileUploadFields = document.querySelectorAll('.file-field'); 1920 1921fileUploadFields.forEach((field) => { 1922 const input = field.querySelector('.input-file'); 1923 const label = field.querySelector('.label-file'); 1924 const labelText = label.textContent; 1925 1926 input.addEventListener('change', (e) => { 1927 label.textContent = e.target.files.length > 0 ? e.target.files[0].name : labelText; 1928 }); 1929}); 1930 1931/* Password visibility toggler */ 1932 1933const passwordTogglers = document.querySelectorAll('.password-toggler'); 1934
1935passwordTogglers.forEach((toggler) => { 1936 toggler.addEventListener('click', () => { 1937 const input = toggler.nextSibling; 1938 toggler.classList.toggle('is-active'); 1939 input.type = input.type === 'password' ? 'text' : 'password'; 1940 }); 1941}); 1942 1943/** 1944 Vendor libs settings 1945==================== **/ 1946 1947 1948 1949 1950(0,_libs_just_validate_settings__WEBPACK_IMPORTED_MODULE_8__.initJustValidate)(); 1951(0,_libs_custom_select_settings__WEBPACK_IMPORTED_MODULE_9__.initCustomSelect)(); 1952(0,_libs_intl_tel_settings__WEBPACK_IMPORTED_MODULE_10__.initIntlTel)(); 1953 1954/** 1955 Categories nav 1956=============- **/ 1957 1958 1959(0,_components_categories_nav__WEBPACK_IMPORTED_MODULE_11__.initCategoriesNav)(); 1960 1961/** 1962 Payment method's switcher 1963======================= **/ 1964 1965const paymentMethod = document.querySelector('.payment-select')?.customSelect; 1966 1967paymentMethod && 1968 paymentMethod.select.addEventListener('change', (e) => { 1969 if (e.target.value === 'crypto') 1970 return swithPaymentMethod( 1971 '.payment-information__card-field', 1972 '.payment-information__crypto-field', 1973 'I have paid', 1974 ); 1975 if (e.target.value === 'card') 1976 return swithPaymentMethod( 1977 '.payment-information__crypto-field', 1978 '.payment-information__card-field', 1979 'Place the order', 1980 ); 1981 }); 1982 1983// Set crypto method first 1984paymentMethod && 1985 swithPaymentMethod('.payment-information__card-field', '.payment-information__crypto-field', 'I have paid'); 1986 1987function swithPaymentMethod(hiddenClasses, shownClasses, submitText) { 1988 const parent = paymentMethod.select.closest('.payment-information'); 1989 parent.querySelectorAll(hiddenClasses).forEach((field) => { 1990 field.classList.add('hidden-field'); 1991 field.querySelectorAll('input').forEach((input) => input.setAttribute('disabled', '')); 1992 });
1993 parent.querySelectorAll(shownClasses).forEach((field) => { 1994 field.classList.remove('hidden-field'); 1995 field.querySelectorAll('input').forEach((input) => input.removeAttribute('disabled')); 1996 }); 1997 parent.closest('.order-form').querySelector('.submit-button').textContent = submitText; 1998 parent.closest('.order-form').querySelector('.submit-button').value = submitText; 1999} 2000 2001/** 2002 Copy text from copy fields 2003========================-= **/ 2004 2005const copyFields = document.querySelectorAll('.copy-field'); 2006 2007copyFields.forEach((field) => { 2008 const text = field.querySelector('.copy-text').textContent; 2009 const button = field.querySelector('.copy-button'); 2010 2011 button.addEventListener('click', () => { 2012 if (navigator && navigator.clipboard && navigator.clipboard.writeText) { 2013 button.querySelector('.button-text').classList.add('is-visible'); 2014 setTimeout(() => { 2015 button.querySelector('.button-text').classList.remove('is-visible'); 2016 }, 1500); 2017 return navigator.clipboard.writeText(text); 2018 } 2019 }); 2020}); 2021 2022})(); 2023 2024/******/ })() 2025;
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.