1/******/ (() => { // webpackBootstrap 2/******/ var __webpack_modules__ = ({ 3 4/***/ 5907: 5/***/ (() => { 6 7( function () { 8 'use strict'; 9 10 function onReady( fn ) { 11 if ( document.readyState === 'loading' ) { 12 document.addEventListener( 'DOMContentLoaded', fn ); 13 } else { 14 fn(); 15 } 16 } 17 18 onReady( function () { 19 document.querySelectorAll( '.pattern-allied-collapsed-text__view-more a' ).forEach( function ( link ) { 20 link.addEventListener( 'click', function ( event ) { 21 event.preventDefault(); 22 23 link.classList.toggle( 'active' ); 24 25 const parent = link.closest( '.wp-block-columns' ); 26 const content = parent && parent.querySelector( '.pattern-allied-collapsed-text__right' ); 27 28 if ( content ) { 29 content.classList.toggle( 'active' ); 30 } 31 } ); 32 } ); 33 } ); 34}() ); 35 36 37/***/ }), 38 39/***/ 9873: 40/***/ (() => { 41 42( function () { 43 'use strict'; 44 45 function onReady( fn ) { 46 if ( document.readyState === 'loading' ) { 47 document.addEventListener( 'DOMContentLoaded', fn ); 48 } else { 49 fn(); 50 } 51 } 52 53 onReady( function () { 54 document.querySelectorAll( '.pattern-allied-steps__text .arrow-icon a' ).forEach( function ( link ) { 55 link.addEventListener( 'click', function ( event ) { 56 event.preventDefault(); 57 58 link.classList.toggle( 'active' ); 59 60 const arrowIcon = link.closest( '.arrow-icon' ); 61 const description = arrowIcon && arrowIcon.nextElementSibling; 62 63 if ( description && description.classList.contains( 'description' ) ) { 64 description.classList.toggle( 'active' ); 65 } 66 } ); 67 } ); 68 } ); 69}() ); 70 71 72/***/ }), 73 74/***/ 304: 75/***/ (() => { 76 77document.addEventListener( "DOMContentLoaded", function () { 78 let colibriTabs = document.querySelectorAll( '.pattern-allied-tabs .wp-block-colibri-tabs__tab[role="tab"]' ); 79 let filterTabs = document.querySelectorAll( '.pattern-allied-tabs .wp-block-colibri-tabs__tab[href^="#filter-"]' ); 80 let availableFilters = document.querySelectorAll( '.filter-list .custom-control-input' ); 81 let tabContent = document.querySelector( '.pattern-allied-tabs .wp-block-colibri-tabs__tabs-content' ); 82 let resetFilters = document.querySelector( '.js-reset-filters' ); 83 let availableFiltersList = []; 84 const hash = window.location.hash || ''; 85 86 if ( (!filterTabs.length || !availableFilters.length) && !tabContent ) return false; 87 88 // set active tab and apply filters on page load 89 if ( hash.startsWith( '#filter-' ) ) { 90 // add a small timeout to prevent potential issues during rendering 91 setTimeout( function () { 92 document.querySelector( 'a[href="' + hash + '"]' )?.click(); 93 }, 500 ); 94 } 95 96 // get all available filters 97 availableFilters.forEach( filter => { 98 availableFiltersList.push( filter.getAttribute( 'value' ) ); 99 } ); 100 101 // make tabs with href="#filter-" unavailable if they are not in available filters list 102 filterTabs.forEach( tab => { 103 let href = tab.getAttribute( 'href' ); 104 105 if ( href ) { 106 let filterBy = href.split( '-' )[1]; 107 108 if ( !availableFiltersList.includes( filterBy ) && filterBy !== 'reset' ) { 109 tab.classList.add( 'disabled' ); 110 } 111 } 112 } ); 113 114 // handle click on tabs with href="#filter-" 115 filterTabs.forEach( tab => { 116 tab.addEventListener( 'click', function () { 117 let href = this.getAttribute( 'href' ); 118 119 if ( href ) { 120 let filterBy = href.split( '-' )[1]; 121 let currentFilterItem = document.querySelector( '.filter-list .custom-control-input[value="' + filterBy + '"]' ); 122 123 if ( filterBy === 'reset' ) { 124 resetFilters.click(); 125 } else if ( currentFilterItem ) { 126 // uncheck all filters before check the clicked one 127 availableFilters.forEach( item => { 128 item.checked = false; 129 } ); 130 // select clicked filter 131 currentFilterItem.click(); 132 } else { 133 return false; 134 } 135 136 window.scrollTo( { 137 top: document.getElementById( 'listing' ).offsetTop, 138 behavior: 'smooth' 139 } ); 140 } else { 141 return false; 142 } 143 } ); 144 } ); 145 146 // Update URL hash when a tab is clicked ( uses panel data-anchor ) ---
147 colibriTabs.forEach( tab => { 148 tab.addEventListener( 'click', function () { 149 let panelId = this.getAttribute( 'aria-controls' ); 150 if ( !panelId ) return; 151 152 let panel = document.getElementById( panelId ); 153 if ( !panel ) return; 154 155 let anchor = panel.getAttribute( 'data-anchor' ); 156 if ( !anchor ) return; 157 158 if ( window.location.hash !== '#' + anchor ) { 159 history.replaceState( null, '', '#' + anchor ); 160 } 161 } ); 162 } ); 163 164 if ( tabContent ) tabContent.style.scrollBehavior = 'smooth'; 165 166 let isDown = false; 167 let startX; 168 let scrollLeft; 169 170 if ( tabContent ) { 171 tabContent.addEventListener( 'mousedown', ( e ) => { 172 isDown = true; 173 tabContent.classList.add( 'active' ); 174 startX = e.pageX - tabContent.offsetLeft; 175 scrollLeft = tabContent.scrollLeft; 176 } ); 177 178 tabContent.addEventListener( 'mouseleave', () => { 179 isDown = false; 180 tabContent.classList.remove( 'active' ); 181 } ); 182 183 tabContent.addEventListener( 'mouseup', () => { 184 isDown = false; 185 tabContent.classList.remove( 'active' ); 186 } ); 187 188 tabContent.addEventListener( 'mousemove', ( e ) => { 189 if ( !isDown ) return; 190 e.preventDefault(); 191 const x = e.pageX - tabContent.offsetLeft; 192 const walk = (x - startX) * 2; 193 tabContent.scrollLeft = scrollLeft - walk; 194 } ); 195 } 196} ); 197 198 199/***/ }), 200 201/***/ 1637: 202/***/ (() => { 203 204( function () { 205 'use strict'; 206 207 function onReady( fn ) { 208 if ( document.readyState === 'loading' ) { 209 document.addEventListener( 'DOMContentLoaded', fn ); 210 } else { 211 fn(); 212 } 213 } 214 215 function animateScrollTop( to, duration ) { 216 const start = window.pageYOffset; 217 const change = to - start; 218 const startTime = performance.now(); 219 220 function easeInOutQuad( t ) { 221 return t < 0.5 ? 2 * t * t : 1 - Math.pow( -2 * t + 2, 2 ) / 2; 222 } 223 224 function step( now ) { 225 const elapsed = now - startTime; 226 const t = Math.min( elapsed / duration, 1 ); 227 window.scrollTo( 0, start + change * easeInOutQuad( t ) ); 228 if ( t < 1 ) { 229 requestAnimationFrame( step ); 230 } 231 } 232 233 requestAnimationFrame( step ); 234 } 235 236 onReady( function () { 237 const anchors = document.querySelectorAll( '.pattern-anchor-bar a' ); 238 239 anchors.forEach( function ( item ) { 240 item.addEventListener( 'click', function ( event ) { 241 const href = item.getAttribute( 'href' ); 242 if ( ! href || href.charAt( 0 ) !== '#' ) { 243 return; 244 } 245 246 const blockId = href.slice( 1 ); 247 if ( ! blockId ) { 248 return; 249 } 250 251 const targetEl = document.getElementById( blockId ); 252 if ( ! targetEl ) { 253 return; 254 } 255 256 event.preventDefault(); 257 258 const y = targetEl.getBoundingClientRect().top + window.scrollY; 259 animateScrollTop( y, 500 ); 260 261 anchors.forEach( function ( el ) { 262 el.classList.remove( 'active' ); 263 } ); 264 item.classList.add( 'active' ); 265 } ); 266 } ); 267 } ); 268}() ); 269 270 271/***/ }), 272 273/***/ 5818: 274/***/ (() => { 275 276/** 277 * File navigation.js. 278 * 279 * Handles toggling the navigation menu for small screens and enables TAB key 280 * navigation support for dropdown menus. 281 * FET-2230: Enables state toggle sub-menu items. 282 * FET-2642: Enables touch interactivity with desktop navigation. 283 * FET-2548: Enables sorting of state-layout navigation items. 284 */ 285 286function onReady( fn ) { 287 if ( document.readyState === 'loading' ) { 288 document.addEventListener( 'DOMContentLoaded', fn ); 289 } else { 290 fn(); 291 } 292} 293 294let mmToggli, mmToggla, mmBody, mmDrawer, mmBacker, mmClones, mmTargets, mmHoverItems, primaryNavItems, 295 mastHead, header, stateLayoutMenus; 296 297onReady( function () { 298 let collection, container, menu, links, i, len; 299 300 // Collect the menus for mobile 301 collection = document.querySelectorAll( '#masthead .main-navigation, #masthead .menu-secondary-menu-container, #masthead .menu-utility-menu-container' ); 302 303 // Get the toggle menu item (li) 304 mmToggli = document.getElementById( 'menu-item-menu-toggle' ); 305 306 // Define base selector 307 const mmBaseSelector = '.menu-container:not(#menu-clones) #primary-menu>li.menu-item-has-children'; 308 309 // Get all menu items with submenus 310 mmHoverItems = [ 311 ...document.querySelectorAll( `${ mmBaseSelector }:not(.mm-condensed)>ul.sub-menu>li.menu-item-has-children` ), 312 ...document.querySelectorAll( `${ mmBaseSelector }>ul.sub-menu>li.state-layout-list>ul.state-layout-menu>li.menu-item-has-children` ), 313 ...document.querySelectorAll( `${ mmBaseSelector }.mm-expanded>ul.sub-menu>li.menu-item-has-children>ul.sub-menu>li.menu-item-has-children` ) 314 ]; 315 316 // Iterate over each menu item with a submenu
317 mmHoverItems.forEach( parentElement => { 318 // Find submenu and anchor toggle 319 const subMenu = parentElement.querySelector( 'ul.sub-menu' ); 320 const anchorToggle = parentElement.querySelector( 'a' ); 321 // Create caret element 322 const spanElement = document.createElement( 'span' ); 323 spanElement.classList.add( 'sub-menu-caret' ); 324 anchorToggle.appendChild( spanElement ); 325 326 // Function to toggle submenu visibility 327 const toggleSubMenu = event => { 328 event.stopPropagation(); 329 event.preventDefault(); 330 if ( !parentElement.classList.contains( 'has-been-toggled' ) ) { 331 parentElement.classList.add( 'has-been-toggled' ); 332 } 333 subMenu.classList.toggle( 'menu-item-visible' ); 334 parentElement.classList.toggle( 'sub-menu-toggled' ); 335 }; 336 337 // Attach click and touchstart event listeners to toggle submenu 338 [ 'click', 'touchstart' ].forEach( eventType => { 339 parentElement.addEventListener( eventType, toggleSubMenu ); 340 } ); 341 342 // Add click event listeners to anchor links within the submenu 343 subMenu.querySelectorAll( 'a' ).forEach( anchor => { 344 anchor.addEventListener( 'click', e => { 345 e.stopPropagation(); 346 forceHashChangeReload(); 347 } ); 348 } ); 349 } ); 350 351 // Select all primary navigation items with children 352 primaryNavItems = document.querySelectorAll( mmBaseSelector ); 353 354 // Function to close all submenus that were previously toggled and remove classes 355 function closeAllSubNavs() { 356 primaryNavItems.forEach( navItem => { 357 navItem.classList.remove( 'focus' ); 358 navItem.querySelectorAll( 'li.has-been-toggled' ).forEach( subMenu => { 359 const focusedAnchor = subMenu.querySelector( 'a:focus' ); 360 if ( focusedAnchor ) { 361 focusedAnchor.blur(); 362 } 363 const childMenuVisible = subMenu.querySelector( 'ul.menu-item-visible' ); 364 if ( childMenuVisible ) { 365 childMenuVisible.classList.remove( 'menu-item-visible' ); 366 } 367 subMenu.classList.remove( 'has-been-toggled' ); 368 subMenu.classList.remove( 'sub-menu-toggled' ); 369 } ); 370 } ); 371 } 372 373 // Function to toggle submenu visibility 374 function toggleSubNav( navItem ) { 375 navItem.classList.toggle( 'focus' ); 376 } 377 378 // Variable to store the last touched navigation item 379 let lastTouchedNavItem = null; 380 381 // Handle touchstart event on navigation item 382 function handleNavTouchStart( event, navItem ) { 383 event.preventDefault(); 384 event.stopPropagation(); 385 // Check if this is the first touch on the navigation item 386 if ( lastTouchedNavItem !== navItem ) { 387 lastTouchedNavItem = navItem; 388 // Close all submenus and toggle current submenu 389 closeAllSubNavs(); 390 toggleSubNav( navItem ); 391 } else { 392 // Follow the link on the second touch 393 window.location.href = navItem.querySelector( 'a' ).href; 394 } 395 } 396 397 // Handle mouseenter event on navigation item 398 function handleNavMouseEnter( navItem ) { 399 // Close all submenus and toggle current submenu 400 closeAllSubNavs(); 401 toggleSubNav( navItem ); 402 } 403 404 // Handle mouseleave event on navigation item 405 function handleNavMouseLeave() { 406 // Close all submenus 407 closeAllSubNavs(); 408 } 409 410 // Handle focusin event on navigation item 411 function handleNavFocusIn( event, navItem ) { 412 if ( !navItem.classList.contains( 'focus' ) ) { 413 // Close all submenus and toggle current submenu 414 closeAllSubNavs(); 415 toggleSubNav( navItem ); 416 } 417 } 418 419 // Add event listeners to each navigation item 420 primaryNavItems.forEach( navItem => { 421 navItem.addEventListener( 'touchstart', event => handleNavTouchStart( event, navItem ) ); 422 navItem.addEventListener( 'mouseenter', () => handleNavMouseEnter( navItem ) ); 423 navItem.addEventListener( 'mouseleave', handleNavMouseLeave ); 424 navItem.addEventListener( 'focusin', event => handleNavFocusIn( event, navItem ) ); 425 // Prevent touch events propagation for links within submenu 426 const subMenuLinks = navItem.querySelectorAll( '.sub-menu a' ); 427 subMenuLinks.forEach( link => { 428 link.addEventListener( 'touchstart', event => { 429 event.stopPropagation(); 430 } ); 431 } ); 432 } ); 433 434 // Close submenu when tapping outside the nav 435 document.addEventListener( 'touchstart', event => { 436 const isOutsideNav = !event.target.closest( mmBaseSelector ); 437 if ( isOutsideNav ) { 438 closeAllSubNavs(); // Close all submenus if tapping outside the nav 439 lastTouchedNavItem = null; // Reset last touched nav item 440 } 441 } ); 442 443 // Close submenu when tabbing out of the mmBaseSelector 444 document.addEventListener( 'focusin', event => { 445 const isOutsideNav = !event.target.closest( mmBaseSelector ); 446 if ( isOutsideNav ) { 447 closeAllSubNavs(); // Close all submenus if tabbing out of the nav 448 lastTouchedNavItem = null; // Reset last touched nav item 449 } 450 } ); 451 452 // Query the masthead 453 mastHead = document.querySelector( '#masthead' ); 454 455 // Query state layout menus 456 stateLayoutMenus = document.querySelectorAll( '.menu-container:not(#menu-clones) #primary-menu>li.sl-row' ); 457 458 // Iterate over each state layout menu
459 stateLayoutMenus.forEach( menu => { 460 // Find the submenu within each menu 461 const subMenu = menu.querySelector( ':scope > ul.sub-menu' ); 462 463 // Check if submenu exists and if the menu has children 464 if ( subMenu && menu.classList.contains( 'menu-item-has-children' ) ) { 465 // Get all first level list items excluding those with class "menu-header" 466 const topLevelItems = Array.from( subMenu.children ).filter( item => item.tagName === 'LI' && !item.classList.contains( 'menu-header' ) ); 467 468 // Determine the number of columns based on mm-col-x class if present, else default to 4 columns 469 let numColumns = 4; 470 const mmColClass = menu.className.match( /mm-col-(\d+)/ ); 471 if ( mmColClass ) { 472 numColumns = parseInt( mmColClass[1] ); 473 } 474 475 // Calculate the number of items to distribute evenly into the columns 476 const numItemsPerColumn = Math.ceil( topLevelItems.length / numColumns ); 477 478 // Create the columns (unordered lists) and distribute items 479 for ( let i = 0; i < numColumns; i++ ) { 480 // Create the new list 481 const newList = document.createElement( 'ul' ); 482 newList.classList.add( 'state-layout-menu' ); // Add the class 'state-layout-menu' 483 484 // Create the containing <li> with a span describing the list 485 const listItem = document.createElement( 'li' ); 486 listItem.classList.add( 'state-layout-list' ); // Add the class 'state-layout-list' 487 const listText = document.createTextNode( `State selection list ${ i + 1 }` ); 488 const listSpan = document.createElement( 'span' ); 489 listSpan.appendChild( listText ); 490 listItem.appendChild( listSpan ); 491 492 // Append the <ul> inside the <li> 493 listItem.appendChild( newList ); 494 495 // Append the <li> to the submenu 496 subMenu.appendChild( listItem ); 497 498 // Distribute items evenly into each column 499 const itemsForThisColumn = topLevelItems.splice( 0, numItemsPerColumn ); 500 itemsForThisColumn.forEach( item => { 501 newList.appendChild( item ); 502 } ); 503 } 504 } 505 } ); 506 507 // Function to activate menu 508 function activateMenu( item ) { 509 let subMenu = item.querySelector( ':scope > ul.sub-menu' ); 510 let resizeObserver; 511 512 function handleMouseLeave() { 513 // Disconnect ResizeObserver if present 514 if ( resizeObserver ) { 515 resizeObserver.disconnect(); 516 } 517 // Remove 'zen-background' class from mastHead 518 mastHead.classList.remove( 'zen-background' ); 519 } 520 521 if ( item.classList.contains( 'mm-full-width' ) && !item.classList.contains( 'mm-centered' ) ) { 522 // Set initial submenu height 523 updateSubMenuHeight( subMenu ); 524 // Add 'zen-background' class to mastHead 525 mastHead.classList.add( 'zen-background' ); 526 // Dynamically update submenu height using ResizeObserver 527 resizeObserver = new ResizeObserver( () => { 528 updateSubMenuHeight( subMenu ); 529 } ); 530 resizeObserver.observe( subMenu ); 531 } else { 532 // If the item doesn't have 'mm-full-width' class, handle mouse leave directly 533 handleMouseLeave(); 534 } 535 536 // Add mouseleave event listener 537 item.addEventListener( 'mouseleave', handleMouseLeave ); 538 } 539 540 // Event handlers for mouseenter and touchstart 541 function handleMouseEnter( item ) { 542 activateMenu( item ); 543 } 544 545 function handleTouchStart( item ) { 546 activateMenu( item ); 547 } 548 549 // Add event listeners to menu items 550 document.querySelectorAll( mmBaseSelector ).forEach( item => { 551 item.addEventListener( 'mouseenter', () => handleMouseEnter( item ) ); 552 item.addEventListener( 'touchstart', () => handleTouchStart( item ) ); 553 } ); 554 555 // Function to update submenu height 556 function updateSubMenuHeight( subMenu ) { 557 // Set CSS variable '--sub-menu-height' to submenu client height 558 document.body.style.setProperty( '--sub-menu-height', subMenu.clientHeight + 'px' ); 559 } 560 561 // Global touch event listener to close menu if touched outside 562 document.addEventListener( 'touchstart', event => { 563 // Check if touch event target is outside the menu area 564 if ( !event.target.closest( '.menu-item-has-children' ) ) { 565 // Remove 'zen-background' class from mastHead 566 mastHead.classList.remove( 'zen-background' ); 567 // Additional code to close menu or perform other actions as
567needed 568 } 569 } ); 570 571 // Exit early if collection is empty or the toggle button is undefined 572 if ( 0 === collection.length || null === mmToggli || 'undefined' === typeof mmToggli ) { 573 console.log( 'Mobile navigation skipped' ); 574 return; 575 } 576 577 // WordPress assigns ids and classes to the `li` element in menus, but we also need the anchor 578 mmToggla = mmToggli.getElementsByTagName( 'a' )[0]; 579 mmToggla.innerHTML = ""; 580 // And the anchor needs a proper void() because WordPress strips it 581 mmToggla.href = 'javascript:void(0);'; 582 mmToggla.setAttribute( 'aria-label', 'Toggle mobile menu' ); 583 584 // The top-most element containing content (i.e. body) 585 mmBody = document.getElementsByTagName( 'body' )[0]; 586 587 mmDrawer = document.getElementById( 'menu-drawer' ); 588 mmBacker = document.getElementById( 'menu-backer' ); 589 mmClones = document.getElementById( 'menu-clones' ); 590 591 mmTargets = []; // array of expanded menus in the menu drawer 592 593 // Reverse the collection so .main-navigation renders first in the .menu-drawer 594 // Updated 2023-04-04 by @Pav-Osmolski: Disabled reverse for CRE since we require the Utility Nav to display last. 595 collection = Array.prototype.slice.call( collection ); 596 //collection.reverse(); 597 598 mmToggla.onclick = function ( e ) { 599 if ( !mmDrawer.classList.contains( 'toggled' ) ) { 600 openMenuDrawer(); 601 } else { 602 closeMenuDrawer(); 603 } 604 }; 605 606 collection.forEach( container => { 607 menu = container.querySelector( 'ul' ); 608 609 // Put a clone of the menu in the menu clones container 610 let clone = menu.cloneNode( true ); 611 mmClones.appendChild( clone ); 612 613 if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { 614 menu.className += ' nav-menu'; 615 } 616 } ); 617 618 mmClones.querySelectorAll( '*' ).forEach( item => { 619 if ( item.id ) { 620 item.id = item.id + "-drawer"; 621 } 622 } ); 623 624 mmDrawer.querySelectorAll( '.menu-item-has-children' ).forEach( item => { 625 item.addEventListener( 'click', function ( event ) { 626 // Stop propagation of child elements 627 item.querySelectorAll( 'li:not(.menu-item-has-children)' ).forEach( child => { 628 child.addEventListener( 'click', function ( event ) { 629 event.stopPropagation(); 630 } ); 631 } ); 632 633 item.classList.add( 'expanded' ); 634 mmTargets.push( this ); 635 slideMenus( this ); 636 scrollMenuToTop(); 637 event.stopPropagation(); 638 event.preventDefault(); 639 forceHashChangeReload(); 640 preventScroll( mmTargets ) 641 } ); 642 } ); 643 644 mmBacker.addEventListener( 'click', function ( event ) { 645 if ( mmTargets.length > 0 ) { 646 let collapse = mmTargets.pop(); 647 collapse.classList.remove( 'expanded' ); 648 slideMenus( this ); 649 scrollMenuToTop(); 650 preventScroll( mmTargets ) 651 } else { 652 closeMenuDrawer(); 653 } 654 655 event.preventDefault(); 656 } ); 657} ); 658 659function openMenuDrawer() { 660 backerContext(); 661 mmDrawer.querySelectorAll( 'a' ).forEach( item => { 662 item.tabIndex = 0; 663 } ); 664 mmBody.classList.add( 'menu-drawer-toggled' ); 665 mmDrawer.classList.add( 'toggled' ); 666 mmToggli.classList.remove( 'menu-opener' ); 667 mmToggli.classList.add( 'menu-closer' ); 668 mmToggli.focus(); 669} 670 671function closeMenuDrawer() { 672 // Clear the expanded targets array 673 mmTargets = []; 674 675 // Remove the expanded class from all menu items 676 mmDrawer.querySelectorAll( '.expanded' ).forEach( item => { 677 item.classList.remove( 'expanded' ); 678 } ); 679
680 // Remove collapsed items from the tabindex 681 mmDrawer.querySelectorAll( 'a' ).forEach( item => { 682 item.tabIndex = -1; 683 } ); 684 685 // Remove clone transformations 686 mmClones.style.transform = "none"; 687 688 mmBody.classList.remove( 'menu-drawer-toggled' ); 689 mmDrawer.classList.remove( 'toggled' ); 690 mmToggli.classList.remove( 'menu-closer' ); 691 mmToggli.classList.add( 'menu-opener' ); 692 mmToggli.focus(); 693} 694 695function slideMenus( target ) { 696 backerContext(); 697 target.classList.add( 'expanded' ); 698 mmClones.style.transform = "translateX( -" + (100 * mmTargets.length) + "% )"; 699} 700 701function backerContext() { 702 if ( mmTargets.length > 0 ) { 703 mmBacker.innerHTML = 'Back'; 704 mmBacker.style.setProperty( "--check-display", "none" ); 705 mmBacker.style.setProperty( "--check-display-before", "inline-block" ); 706 mmBacker.style.setProperty( "padding-left", "3rem" ); 707 } else { 708 mmBacker.innerHTML = 'Menu'; 709 mmBacker.style.setProperty( "--check-display", "inline-block" ); 710 mmBacker.style.setProperty( "--check-display-before", "none" ); 711 } 712} 713 714/** 715 * Scrolls the menu drawer to top. 716 */ 717function scrollMenuToTop() { 718 mmDrawer.scrollTo( 0, 0 ); 719} 720 721/** 722 * Sets or removes .focus class on an element. 723 */ 724function toggleFocus( menuItem ) { 725 mmDrawer.querySelectorAll( '.focus' ).forEach( focusItem => { 726 focusItem.classList.remove( 'focus' ); 727 } ); 728 const li = menuItem.closest( 'li' ); 729 if ( li ) { 730 li.classList.add( 'focus' ); 731 } 732} 733 734/** 735 * Forces a page refresh when clicking links with same-page anchors in the nav. 736 */ 737function forceHashChangeReload() { 738 window.onhashchange = function () { 739 window.location.reload(); 740 } 741} 742 743/** 744 * Prevent scroll if there are not enough menu items in the drawer. 745 */ 746function preventScroll( target ) { 747 // Get the last expanded menu item from array 748 let menuItemDrawer = Object.values( target ).slice( -1 ); 749 let deviceHeight = window.innerHeight; 750 751 // Iterate over expanded menu item to get sub menu items list 752 menuItemDrawer.forEach( item => { 753 let subMenuList = item.querySelector( ':scope > ul.sub-menu' ); 754 755 // Check if the height of the submenu list is less than or equal to the space of device height minus the space above 756 // And disable/enable overflow scrolling on the drawer element 757 if ( subMenuList.clientHeight <= deviceHeight - subMenuList.getBoundingClientRect().top ) { 758 mmDrawer.style.overflow = 'hidden'; 759 } else { 760 mmDrawer.style.overflow = 'auto'; 761 } 762 } ) 763} 764 765 766/***/ }), 767 768/***/ 695: 769/***/ (() => { 770 771// Scripts from the original FHEA site which are required for certain custom elements to function correctly 772// Would be nice to remove this and replace it with a VC module or Gutenberg block equivalent 773( function () { 774 'use strict'; 775 776 function onReady( fn ) { 777 if ( document.readyState === 'loading' ) { 778 document.addEventListener( 'DOMContentLoaded', fn ); 779 } else { 780 fn(); 781 } 782 } 783 784 function isSlideVisible( el ) { 785 return el && window.getComputedStyle( el ).display !== 'none'; 786 } 787 788 function slideDown( el ) { 789 if ( ! el ) { 790 return; 791 } 792 el.style.removeProperty( 'display' ); 793 if ( window.getComputedStyle( el ).display === 'none' ) { 794 el.style.display = 'block'; 795 } 796 const targetHeight = el.scrollHeight; 797 el.style.overflow = 'hidden'; 798 el.style.height = '0px'; 799 el.offsetHeight; 800 el.style.transition = 'height 0.3s ease'; 801 el.style.height = targetHeight + 'px'; 802 803 function cleanup() { 804 el.removeEventListener( 'transitionend', cleanup ); 805 el.style.removeProperty( 'height' ); 806 el.style.removeProperty( 'overflow' ); 807 el.style.removeProperty( 'transition' ); 808 } 809 810 el.addEventListener( 'transitionend', cleanup ); 811 } 812 813 function slideUp( el ) { 814 if ( ! el ) { 815 return; 816 } 817 el.style.overflow = 'hidden'; 818 el.style.height = el.scrollHeight + 'px'; 819 el.offsetHeight; 820 el.style.transition = 'height 0.3s ease'; 821 el.style.height = '0px'; 822 823 function cleanup() { 824 el.removeEventListener( 'transitionend', cleanup ); 825 el.style.display = 'none'; 826 el.style.removeProperty( 'height' ); 827 el.style.removeProperty( 'overflow' ); 828 el.style.removeProperty( 'transition' ); 829 } 830 831 el.addEventListener( 'transitionend', cleanup ); 832 } 833 834 function slideToggle( el ) { 835 if ( ! el ) { 836 return; 837 } 838 if ( isSlideVisible( el ) ) { 839 slideUp( el ); 840 } else { 841 slideDown( el ); 842 } 843 } 844 845 function animateScrollTop( to, duration ) { 846 const start = window.pageYOffset; 847 const change = to - start; 848 const startTime = performance.now(); 849 850 function easeInOutQuad( t ) { 851 return t < 0.5 ? 2 * t * t : 1 - Math.pow( -2 * t + 2, 2 ) / 2; 852 } 853 854 function step( now ) {
855 const elapsed = now - startTime; 856 const t = Math.min( elapsed / duration, 1 ); 857 window.scrollTo( 0, start + change * easeInOutQuad( t ) ); 858 if ( t < 1 ) { 859 requestAnimationFrame( step ); 860 } 861 } 862 863 requestAnimationFrame( step ); 864 } 865 866 onReady( function () { 867 document.querySelectorAll( '.link-more' ).forEach( function ( btn ) { 868 btn.addEventListener( 'click', function ( e ) { 869 e.preventDefault(); 870 btn.classList.toggle( 'arrow-up-link' ); 871 btn.textContent = btn.classList.contains( 'arrow-up-link' ) ? 'View Less' : 'View More'; 872 const section = btn.parentElement && btn.parentElement.parentElement && btn.parentElement.parentElement.parentElement; 873 if ( ! section ) { 874 return; 875 } 876 section.querySelectorAll( '.detail-bottom .left-side .course-more-details' ).forEach( slideToggle ); 877 section.querySelectorAll( '.detail-bottom .right-side' ).forEach( function ( el ) { 878 el.classList.toggle( 'details-opened' ); 879 } ); 880 } ); 881 } ); 882 883 document.querySelectorAll( '.btn-filter' ).forEach( function ( btn ) { 884 btn.addEventListener( 'click', function () { 885 btn.classList.toggle( 'filter-toggle-expanded' ); 886 document.querySelectorAll( '.filters-form' ).forEach( function ( form ) { 887 form.classList.toggle( 'mobile-hidden' ); 888 } ); 889 } ); 890 } ); 891 892 document.querySelectorAll( '.read-toggle' ).forEach( function ( toggle ) { 893 toggle.addEventListener( 'click', function () { 894 const caption = toggle.textContent.trim(); 895 const row = toggle.parentElement && toggle.parentElement.parentElement; 896 if ( ! row ) { 897 return; 898 } 899 const bios = row.querySelectorAll( '.bio-detail' ); 900 if ( caption === 'Read Less' ) { 901 toggle.textContent = 'Read More'; 902 bios.forEach( slideUp ); 903 } else { 904 toggle.textContent = 'Read Less'; 905 bios.forEach( slideDown ); 906 } 907 } ); 908 } ); 909 910 document.querySelectorAll( '.dropdown-toggler' ).forEach( function ( toggler ) { 911 toggler.addEventListener( 'click', function () { 912 toggler.classList.toggle( 'active' ); 913 const parent = toggler.parentElement; 914 if ( parent ) { 915 parent.classList.toggle( 'active-cta-dropdown' ); 916 } 917 const grandParent = parent && parent.parentElement; 918 if ( grandParent ) { 919 grandParent.querySelectorAll( '.cta-button-sub-drop-down' ).forEach( slideToggle ); 920 } 921 } ); 922 } ); 923 924 document.addEventListener( 'click', function ( e ) { 925 const activeContainers = document.querySelectorAll( '.active-cta-dropdown' ); 926 if ( ! activeContainers.length ) { 927 return; 928 } 929 930 let inside = false;
931 activeContainers.forEach( function ( container ) { 932 if ( container === e.target || container.contains( e.target ) ) { 933 inside = true; 934 } 935 } ); 936 if ( inside ) { 937 return; 938 } 939 940 activeContainers.forEach( function ( container ) { 941 container.querySelectorAll( '.dropdown-toggler.active' ).forEach( function ( t ) { 942 t.classList.toggle( 'active' ); 943 } ); 944 container.classList.toggle( 'active-cta-dropdown' ); 945 const parentEl = container.parentElement; 946 if ( parentEl ) { 947 parentEl.querySelectorAll( '.cta-button-sub-drop-down' ).forEach( slideToggle ); 948 } 949 } ); 950 } ); 951 952 document.querySelectorAll( '.pricing-option-box' ).forEach( function ( box ) { 953 box.addEventListener( 'click', function ( e ) { 954 e.preventDefault(); 955 document.querySelectorAll( '.pricing-option-box' ).forEach( function ( b ) { 956 b.classList.remove( 'pricing-option-box-active' ); 957 } ); 958 box.classList.add( 'pricing-option-box-active' ); 959 } ); 960 } ); 961 962 document.addEventListener( 'click', function ( e ) { 963 const link = e.target.closest && e.target.closest( '.back-to-top-wrap a[href^="#masthead"]' ); 964 if ( ! link ) { 965 return; 966 } 967 e.preventDefault(); 968 animateScrollTop( 0, 2000 ); 969 } ); 970 971 document.addEventListener( 'click', function ( e ) { 972 const link = e.target.closest && e.target.closest( '.anchor-link' ); 973 if ( ! link ) { 974 return; 975 } 976 e.preventDefault(); 977 978 const anchor = link.getAttribute( 'href' ); 979 if ( ! anchor || anchor.charAt( 0 ) !== '#' ) { 980 return; 981 } 982 983 const rootSelector = window.innerWidth > 1199 ? '.product-details-desktop' : '.product-details-mobile'; 984 const root = document.querySelector( rootSelector ); 985 if ( ! root ) { 986 return; 987 } 988 989 let target; 990 try { 991 target = root.querySelector( anchor ); 992 } catch ( err ) { 993 return; 994 } 995 if ( ! target ) { 996 return; 997 } 998 999 const y = target.getBoundingClientRect().top + window.scrollY; 1000 animateScrollTop( y, 1000 ); 1001 } ); 1002 1003 const stickySearch = document.querySelector( '.custom-sticky-form #search' ); 1004 const searchFormContainer = document.querySelector( '.custom-sticky-form__container' ); 1005 if ( stickySearch && searchFormContainer && window.innerWidth > 1024 ) { 1006 const formOffsetTop = searchFormContainer.getBoundingClientRect().top + window.scrollY; 1007 1008 function onScroll() { 1009 if ( window.pageYOffset > formOffsetTop ) { 1010 searchFormContainer.classList.add( 'sticky' ); 1011 } else { 1012 searchFormContainer.classList.remove( 'sticky' ); 1013 } 1014 } 1015 1016 window.addEventListener( 'scroll', onScroll, { passive: true } ); 1017 } 1018 } ); 1019}() ); 1020 1021 1022/***/ }), 1023 1024/***/ 7422: 1025/***/ (() => { 1026 1027( function () { 1028 'use strict'; 1029 1030 function onReady( fn ) { 1031 if ( document.readyState === 'loading' ) { 1032 document.addEventListener( 'DOMContentLoaded', fn, { once: true } ); 1033 } else { 1034 fn(); 1035 } 1036 } 1037 1038 function initPatternFaq() { 1039 const questions = document.querySelectorAll( '.schema-faq-question' ); 1040 1041 if ( ! questions.length ) { 1042 return; 1043 } 1044 1045 questions.forEach( function ( question ) { 1046 const section = question.parentElement; 1047 const answer = question.nextElementSibling; 1048 1049 question.addEventListener( 'click', function () { 1050 section.classList.toggle( 'expanded' ); 1051 1052 questions.forEach( function ( collapse ) { 1053 if ( question !== collapse ) { 1054 const otherSection = collapse.parentElement; 1055 if ( otherSection ) { 1056 otherSection.classList.remove( 'expanded' ); 1057 } 1058 const otherAnswer = collapse.nextElementSibling; 1059 if ( otherAnswer ) { 1060 otherAnswer.style.maxHeight = '0'; 1061 } 1062 } 1063 } ); 1064 1065 if ( answer ) { 1066 answer.style.maxHeight = section.classList.contains( 'expanded' ) 1067 ? answer.scrollHeight + 'px' 1068 : '0'; 1069 } 1070 } ); 1071 } ); 1072 } 1073 1074 onReady( initPatternFaq ); 1075}() ); 1076 1077 1078/***/ }), 1079 1080/***/ 8115: 1081/***/ (() => { 1082 1083( function () { 1084 'use strict'; 1085 1086 function onReady( fn ) { 1087 if ( document.readyState === 'loading' ) { 1088 document.addEventListener( 'DOMContentLoaded', fn, { once: true } ); 1089 } else { 1090 fn(); 1091 } 1092 } 1093 1094 function setProperty( obj, path, value ) { 1095 const pList = path.split( '.' ); 1096 const len = pList.length; 1097 let schema = obj; 1098 for ( let i = 0; i < len - 1; i++ ) { 1099 const elem = pList[ i ]; 1100 if ( ! schema[ elem ] ) {
1101 schema[ elem ] = {}; 1102 } 1103 schema = schema[ elem ]; 1104 } 1105 schema[ pList[ len - 1 ] ] = value; 1106 } 1107 1108 function loadScript( src, onLoad ) { 1109 const s = document.createElement( 'script' ); 1110 s.src = src; 1111 s.onload = onLoad; 1112 s.onerror = function () { 1113 console.error( 'Failed to load Swiper:', src ); 1114 }; 1115 document.head.appendChild( s ); 1116 } 1117 1118 onReady( function () { 1119 const sliders = document.querySelectorAll( '.js-blog-slider' ); 1120 if ( ! sliders.length ) { 1121 return; 1122 } 1123 1124 const defaultSettings = { 1125 pagination: false, 1126 loop: false, 1127 rewind: false, 1128 simulateTouch: true, 1129 slidesPerView: 2, 1130 }; 1131 1132 const breakpoints = { 1133 md: 768, 1134 lg: 1024, 1135 }; 1136 1137 const dataItems = { sm: 1.2, md: 1.6, lg: 3 }; 1138 const dataGutter = { sm: 16, md: 16, lg: 32 }; 1139 1140 function initSliders() { 1141 if ( typeof Swiper === 'undefined' ) { 1142 return; 1143 } 1144 1145 sliders.forEach( function ( el, i ) { 1146 const swiperSlider = el.querySelector( '.wp-block-query' ); 1147 if ( ! swiperSlider ) { 1148 console.log( 'found no slider cards' ); 1149 return; 1150 } 1151 1152 swiperSlider.classList.add( 'swiper', 'swiper-' + i ); 1153 1154 const postTemplate = el.querySelector( '.wp-block-post-template' ); 1155 if ( postTemplate ) { 1156 postTemplate.classList.add( 'swiper-wrapper', 'block-slider__items' ); 1157 } 1158 el.querySelectorAll( '.wp-block-post-template > .wp-block-post' ).forEach( function ( post ) { 1159 post.classList.add( 'swiper-slide' ); 1160 } ); 1161 1162 swiperSlider.querySelectorAll( '.swiper-slide' ).forEach( function ( slide ) { 1163 const catItems = slide.querySelectorAll( '.taxonomy-category a' ); 1164 if ( catItems.length ) { 1165 const pick = catItems[ Math.floor( Math.random() * catItems.length ) ]; 1166 pick.classList.add( 'show' ); 1167 } 1168 } ); 1169 1170 const customSettings = {}; 1171 1172 if ( dataItems.sm ) { 1173 setProperty( customSettings, 'slidesPerView', dataItems.sm ); 1174 } 1175 if ( dataItems.md ) { 1176 setProperty( customSettings, 'breakpoints.' + breakpoints.md + '.slidesPerView', dataItems.md ); 1177 } 1178 if ( dataItems.lg ) { 1179 setProperty( customSettings, 'breakpoints.' + breakpoints.lg + '.slidesPerView', dataItems.lg ); 1180 } 1181 1182 if ( dataGutter.sm ) { 1183 setProperty( customSettings, 'spaceBetween', dataGutter.sm ); 1184 } 1185 if ( dataGutter.md ) { 1186 setProperty( customSettings, 'breakpoints.' + breakpoints.md + '.spaceBetween', dataGutter.md ); 1187 } 1188 if ( dataGutter.lg ) { 1189 setProperty( customSettings, 'breakpoints.' + breakpoints.lg + '.spaceBetween', dataGutter.lg ); 1190 } 1191 1192 new Swiper( '.swiper-' + i, Object.assign( {}, defaultSettings, customSettings ) ); 1193 } ); 1194 } 1195 1196 if ( typeof Swiper !== 'undefined' ) { 1197 initSliders(); 1198 return; 1199 } 1200 1201 const styleUrl = 'https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css'; 1202 const scriptUrl = 'https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js'; 1203 1204 const link = document.createElement( 'link' ); 1205 link.rel = 'stylesheet'; 1206 link.href = styleUrl; 1207 document.head.appendChild( link ); 1208 1209 loadScript( scriptUrl, initSliders ); 1210 } ); 1211}() ); 1212 1213 1214/***/ }), 1215 1216/***/ 1563: 1217/***/ (() => { 1218 1219/** 1220 * Pattern Lowest Package Price — sync hero prices from `.product-comparison-view` DOM. 1221 */ 1222( function () { 1223 'use strict'; 1224 1225 /** 1226 * Format price — show cents only if greater than .00 (no $; CSS adds currency). 1227 * 1228 * @param {number} price The price to format. 1229 * @returns {string} Formatted price without dollar sign. 1230 */ 1231 function formatPrice( price ) { 1232 const cents = Math.round( ( price % 1 ) * 100 ); 1233 if ( cents > 0 ) { 1234 return price.toFixed( 2 ); 1235 } 1236 return price.toFixed( 0 ); 1237 } 1238 1239 /** 1240 * Parse currency value from text snippets rendered by Affirm. 1241 * 1242 * @param {string} text The text to parse. 1243 * @returns {number|null} Parsed amount or null. 1244 */ 1245 function parseCurrencyFromText( text ) { 1246 if ( ! text ) { 1247 return null; 1248 } 1249 1250 const dollarMatch = text.match( /\$\s*([0-9][0-9,]*(?:\.[0-9]{1,2})?)/ ); 1251 if ( dollarMatch ) { 1252 const value = parseFloat( dollarMatch[ 1 ].replace( /,/g, '' ) ); 1253 return Number.isNaN( value ) ? null : value; 1254 } 1255 1256 const monthlyMatch = text.match( 1257 /([0-9][0-9,]*(?:\.[0-9]{1,2})?)\s*\/\s*mo/i 1258 ); 1259 if ( monthlyMatch ) { 1260 const value = parseFloat( monthlyMatch[ 1 ].replace( /,/g, '' ) ); 1261 return Number.isNaN( value ) ? null : value; 1262 } 1263 1264 return null; 1265 } 1266 1267 /** 1268 * Ensure monthly price node exists on legacy pages where pattern markup may be older. 1269 * 1270 * @param {Element}
1270 patternRoot The root element of the lowest package price pattern. 1271 * @param {Element|null} pricesBlock The price group element. 1272 * @returns {{wrapper: Element|null, value: Element|null}} Monthly price wrapper and value span. 1273 */ 1274 function ensureMonthlyPriceNode( patternRoot, pricesBlock ) { 1275 let wrapper = patternRoot.querySelector( 1276 '.pattern-lowest-package-price__monthly-price' 1277 ); 1278 1279 if ( ! wrapper && pricesBlock ) { 1280 wrapper = document.createElement( 'p' ); 1281 wrapper.className = 'pattern-lowest-package-price__monthly-price'; 1282 wrapper.innerHTML = '<span class="price-value"></span>/mo*'; 1283 pricesBlock.appendChild( wrapper ); 1284 } 1285 1286 const value = wrapper 1287 ? wrapper.querySelector( '.price-value' ) 1288 : null; 1289 1290 return { wrapper, value }; 1291 } 1292 1293 function onReady( fn ) { 1294 if ( document.readyState === 'loading' ) { 1295 document.addEventListener( 'DOMContentLoaded', fn, { once: true } ); 1296 } else { 1297 fn(); 1298 } 1299 } 1300 1301 /** 1302 * Grace period to wait for Affirm's widget to render its monthly price 1303 * before showing the regular/sale price as a placeholder. This is NOT a 1304 * final decision — Affirm's price is the required display, so checking 1305 * keeps going indefinitely afterward (via the MutationObserver below) 1306 * and will still switch over the moment Affirm's price appears, however 1307 * late that is. 1308 */ 1309 const AFFIRM_REVEAL_WINDOW_MS = 5000; 1310 1311 let affirmWindowStartedAt = null; 1312 let affirmFallbackTimer = null; 1313 1314 window.syncHeroPrice = function syncHeroPrice() { 1315 const patternRoot = document.querySelector( '.pattern-lowest-package-price' ); 1316 1317 if ( ! patternRoot ) { 1318 return; 1319 } 1320 1321 const enableAffirmMonthlyPrice = 1322 String( 1323 patternRoot.getAttribute( 'data-enable-affirm-monthly-price' ) ?? 'true' 1324 ).toLowerCase() !== 'false'; 1325 1326 function hidePattern() { 1327 const pricesBlockEarly = patternRoot.querySelector( 1328 '.pattern-lowest-package-price__prices' 1329 ); 1330 if ( pricesBlockEarly ) { 1331 pricesBlockEarly.classList.remove( 'is-loaded' ); 1332 pricesBlockEarly.style.display = ''; 1333 } 1334 patternRoot.style.display = 'none'; 1335 } 1336 1337 const comparisonView = document.querySelector( '.product-comparison-view' ); 1338 1339 if ( ! comparisonView ) { 1340 hidePattern(); 1341 return; 1342 } 1343 1344 const pricingCards = comparisonView.querySelectorAll( 1345 '.pricing-inner[id^="pricing-"]' 1346 ); 1347 1348 if ( ! pricingCards.length ) { 1349 hidePattern(); 1350 return; 1351 } 1352 1353 patternRoot.style.display = ''; 1354 1355 let lowestRegular = null; 1356 let lowestDiscounted = null; 1357 let lowestCard = null; 1358 let lowestAffirm = null; 1359 1360 pricingCards.forEach( function ( card ) { 1361 if ( enableAffirmMonthlyPrice ) { 1362 // Affirm's monthly price is tracked independently of package price — 1363 // the lowest Affirm price is not always on the lowest-priced card. 1364 const affirmEl = card.querySelector( '.affirm-as-low-as' ); 1365 const affirmPriceText = 1366 affirmEl?.querySelector( '.affirm-as-low-as-price' )?.textContent || 1367 affirmEl?.textContent || 1368 ''; 1369 const affirmPrice = parseCurrencyFromText( affirmPriceText ); 1370 1371 if ( 1372 affirmPrice !== null && 1373 ! Number.isNaN( affirmPrice ) && 1374 ( lowestAffirm === null || affirmPrice < lowestAffirm ) 1375 ) { 1376 lowestAffirm = affirmPrice; 1377 } 1378 } 1379 1380 const priceEl = card.querySelector( '.price' ); 1381 const regularEl = card.querySelector( '.regular-price .price-value' ); 1382 const hasDiscount = priceEl?.classList.contains( 'has-discount' ); 1383 const discountedEl = hasDiscount 1384 ? card.querySelector( '.discounted-price .price-value' ) 1385 : null; 1386 1387 if ( ! regularEl ) { 1388 return; 1389 } 1390 1391 if ( hasDiscount && ! discountedEl ) { 1392 return; 1393 } 1394 1395 let regularPrice = parseFloat( 1396 regularEl.textContent.replace( /[^0-9.]/g, '' ) 1397 ); 1398 let discountedPrice = discountedEl 1399 ? parseFloat( 1400 discountedEl.textContent.replace( /[^0-9.]/g, '' ) 1401 ) 1402 : null; 1403 1404 if ( Number.isNaN( regularPrice ) ) { 1405 return; 1406 } 1407 1408 if ( discountedPrice !== null && Number.isNaN( discountedPrice ) ) { 1409 discountedPrice = null; 1410 } 1411 1412 const comparePrice = 1413 discountedPrice !== null ? discountedPrice : regularPrice;
1414 const currentLowest = 1415 lowestDiscounted !== null ? lowestDiscounted : lowestRegular; 1416 1417 if ( 1418 lowestCard === null || 1419 currentLowest === null || 1420 comparePrice < currentLowest 1421 ) { 1422 lowestRegular = regularPrice; 1423 lowestDiscounted = discountedPrice; 1424 lowestCard = card; 1425 } 1426 } ); 1427 1428 if ( ! lowestCard ) { 1429 return; 1430 } 1431 1432 const pricesBlock = patternRoot.querySelector( 1433 '.pattern-lowest-package-price__prices' 1434 ); 1435 const regularPriceWrapper = patternRoot.querySelector( 1436 '.pattern-lowest-package-price__regular-price' 1437 ); 1438 const regularPriceValue = patternRoot.querySelector( 1439 '.pattern-lowest-package-price__regular-price .price-value' 1440 ); 1441 const salePriceWrapper = patternRoot.querySelector( 1442 '.pattern-lowest-package-price__sale-price' 1443 ); 1444 const salePriceValue = patternRoot.querySelector( 1445 '.pattern-lowest-package-price__sale-price .price-value' 1446 ); 1447 const monthlyNodes = ensureMonthlyPriceNode( patternRoot, pricesBlock ); 1448 const monthlyPriceWrapper = monthlyNodes.wrapper; 1449 const monthlyPriceValue = monthlyNodes.value; 1450 1451 if ( enableAffirmMonthlyPrice && affirmWindowStartedAt === null ) { 1452 affirmWindowStartedAt = Date.now(); 1453 } 1454 1455 const affirmWindowElapsed = 1456 enableAffirmMonthlyPrice && 1457 Date.now() - affirmWindowStartedAt >= AFFIRM_REVEAL_WINDOW_MS; 1458 1459 const canShowAffirmMonthly = 1460 enableAffirmMonthlyPrice && 1461 lowestAffirm !== null && 1462 monthlyPriceWrapper && 1463 monthlyPriceValue; 1464 1465 if ( canShowAffirmMonthly ) { 1466 // Affirm's price is the required display — show/refresh it whenever 1467 // it's available, no matter how long it took to render, and keep 1468 // refining if a lower Affirm price shows up later (e.g. another 1469 // modality becomes visible). 1470 if ( monthlyPriceValue ) { 1471 monthlyPriceValue.textContent = formatPrice( lowestAffirm ); 1472 } 1473 1474 if ( monthlyPriceWrapper ) { 1475 monthlyPriceWrapper.style.display = ''; 1476 } 1477 1478 if ( pricesBlock ) { 1479 pricesBlock.classList.add( 'has-affirm', 'is-loaded' ); 1480 pricesBlock.style.display = ''; 1481 } 1482 1483 if ( affirmFallbackTimer ) { 1484 clearTimeout( affirmFallbackTimer ); 1485 affirmFallbackTimer = null; 1486 } 1487 } else if ( enableAffirmMonthlyPrice && affirmWindowElapsed ) { 1488 // No Affirm price yet after the grace period — show the package 1489 // price so the hero isn't blank. Keep watching regardless: the 1490 // MutationObserver below will re-run this and switch to Affirm's 1491 // price the moment it renders. 1492 if ( monthlyPriceWrapper ) { 1493 monthlyPriceWrapper.style.display = 'none'; 1494 } 1495 1496 if ( pricesBlock ) { 1497 pricesBlock.classList.remove( 'has-affirm' ); 1498 pricesBlock.classList.add( 'is-loaded' ); 1499 pricesBlock.style.display = ''; 1500 } 1501 } else if ( enableAffirmMonthlyPrice && ! affirmFallbackTimer ) { 1502 affirmFallbackTimer = setTimeout( function () { 1503 affirmFallbackTimer = null; 1504 window.syncHeroPrice(); 1505 }, AFFIRM_REVEAL_WINDOW_MS - ( Date.now() - affirmWindowStartedAt ) + 50 ); 1506 } else { 1507 if ( monthlyPriceWrapper ) { 1508 monthlyPriceWrapper.style.display = 'none'; 1509 } 1510 1511 if ( pricesBlock ) { 1512 pricesBlock.classList.remove( 'has-affirm' ); 1513 pricesBlock.classList.add( 'is-loaded' ); 1514 pricesBlock.style.display = ''; 1515 } 1516 1517 if ( affirmFallbackTimer ) { 1518 clearTimeout( affirmFallbackTimer ); 1519 affirmFallbackTimer = null; 1520 } 1521 } 1522 1523 if ( lowestDiscounted !== null ) { 1524 if ( regularPriceValue ) { 1525 regularPriceValue.textContent = formatPrice( lowestRegular ); 1526 } 1527 1528 if ( regularPriceWrapper ) { 1529 regularPriceWrapper.style.display = ''; 1530 } 1531 1532 if ( salePriceValue ) { 1533 salePriceValue.textContent = formatPrice( lowestDiscounted ); 1534 } 1535 1536 if ( salePriceWrapper ) { 1537 salePriceWrapper.style.display = ''; 1538 } 1539 1540 if ( pricesBlock ) { 1541 pricesBlock.classList.add( 'has-discount' ); 1542 } 1543 } else { 1544 if ( salePriceValue ) { 1545 salePriceValue.textContent = formatPrice( lowestRegular ); 1546 } 1547 1548 if ( regularPriceWrapper ) { 1549 regularPriceWrapper.style.display = 'none'; 1550 } 1551 1552 if ( salePriceWrapper ) { 1553 salePriceWrapper.style.display = ''; 1554 } 1555 1556 if ( pricesBlock ) { 1557 pricesBlock.classList.remove( 'has-discount' ); 1558 } 1559 } 1560 }; 1561 1562 function initLowestPackagePricePattern() { 1563 let attempts = 0; 1564 1565 const initInterval = setInterval( function () { 1566 attempts++; 1567 1568 const firstPrice = document.querySelector( 1569 '.product-comparison-view .pricing-inner .regular-price .price-value' 1570 ); 1571 1572 if ( firstPrice ) { 1573 const parsedPrice = parseFloat( 1574 firstPrice.textContent.replace( /[^0-9.]/g, '' ) 1575 ); 1576 1577 if ( ! Number.isNaN( parsedPrice ) && parsedPrice > 0 ) { 1578 window.syncHeroPrice(); 1579 clearInterval( initInterval ); 1580 } 1581 } 1582 1583 if ( attempts >= 10 ) { 1584 clearInterval( initInterval ); 1585 } 1586 }, 500 ); 1587 1588 const observer = new MutationObserver( function ( mutations ) { 1589 let relevant = false;
1590 1591 for ( let i = 0; i < mutations.length; i++ ) { 1592 const target = mutations[ i ].target; 1593 1594 const targetElement = 1595 target.nodeType === 1 ? target : target.parentElement; 1596 1597 if ( targetElement && typeof targetElement.closest === 'function' ) { 1598 if ( targetElement.closest( '.product-comparison-view' ) ) { 1599 relevant = true; 1600 break; 1601 } 1602 } 1603 } 1604 1605 if ( relevant ) { 1606 window.syncHeroPrice(); 1607 } 1608 } ); 1609 1610 const pricingContainer = 1611 document.querySelector( '.product-comparison-view' ) || 1612 document.querySelector( 'main' ) || 1613 document.body; 1614 1615 if ( pricingContainer ) { 1616 observer.observe( pricingContainer, { 1617 childList: true, 1618 subtree: true, 1619 characterData: true, 1620 } ); 1621 } 1622 } 1623 1624 onReady( initLowestPackagePricePattern ); 1625}() ); 1626 1627 1628/***/ }), 1629 1630/***/ 4300: 1631/***/ (() => { 1632 1633/** 1634 * Replace the <body> class "no-js" if javascript is enabled. 1635 */ 1636document.body.className = document.body.className.replace( 'no-js', 'js' ); 1637 1638/***/ }), 1639 1640/***/ 4551: 1641/***/ (() => { 1642 1643/** 1644 * This code involves the management of cookies to keep track of the last two pages visited by the user, 1645 * and it uses this information to set the 'Continue Shopping' link on a breadcrumb navigation. 1646 */ 1647( function() { 1648function initLastSearchCookie() { 1649 1650 // Get the current page's pathname 1651 var pathname = window.location.pathname; 1652 1653 // Get the cookies 'LastSearch' and 'LastSearchFallback' 1654 var LastSearch = getCookie( 'LastSearch' ); 1655 var LastSearchFallback = getCookie( 'LastSearchFallback' ); 1656 1657 // set the 'Continue Shopping' link 1658 setContinueShoppingLink(); 1659 1660 // Set a new 'LastSearch' cookie with the current pathname 1661 setLastSearchCookie( pathname, "LastSearch" ); 1662 1663 // Function to set the 'Continue Shopping' link based on the cookies retrieved 1664 function setContinueShoppingLink() { 1665 var anchor; 1666 if ( LastSearch && pathname !== LastSearch ) { 1667 // If 'LastSearch' cookie exists and is different from the current pathname, set the href attribute 1668 anchor = document.querySelector( ".breadcrumbs-continue-shopping a" ); 1669 if ( anchor ) { 1670 anchor.setAttribute( "href", LastSearch ); 1671 } 1672 1673 // Set the 'LastSearchFallback' cookie as the previous 'LastSearch' cookie 1674 setLastSearchCookie( LastSearch, "LastSearchFallback" ); 1675 } else { 1676 if ( LastSearchFallback !== null ) { 1677 anchor = document.querySelector( ".breadcrumbs-continue-shopping a" ); 1678 if ( anchor ) { 1679 // If 'LastSearchFallback' cookie exists, set the href attribute with its value 1680 anchor.setAttribute( "href", LastSearchFallback ); 1681 } 1682 } 1683 } 1684 } 1685 1686 // Function to retrieve a cookie by its name 1687 function getCookie( cname ) { 1688 var name = cname + "="; 1689 var decodedCookie = decodeURIComponent( document.cookie ); 1690 var ca = decodedCookie.split( ';' ); 1691 for ( var i = 0; i < ca.length; i ++ ) { 1692 var c = ca[ i ]; 1693 while ( c.charAt( 0 ) == ' ' ) { 1694 c = c.substring( 1 ); 1695 } 1696 if ( c.indexOf( name ) == 0 ) { 1697 return c.substring( name.length, c.length ); 1698 } 1699 } 1700 return null; 1701 } 1702 1703 // Function to set a cookie with a specific name, value, and expiration date 1704 function setLastSearchCookie( path, cname ) { 1705 var d = new Date(); 1706 1707 // Set the expiration date to 7 days from the current date 1708 d.setTime( d.getTime() + ( 7 * 24 * 60 * 60 * 1000 ) ); 1709 var expires = "expires=" + d.toUTCString(); 1710 var temp = location.host.split( '.' ).reverse(); 1711 var root_domain = '.' + temp[ 1 ] + '.' + temp[ 0 ]; 1712 1713 // Define the cookie string and set it 1714 var cookie = cname + "=" + path + ";" + expires + ";path=/;" + "domain=" + root_domain; 1715 document.cookie = cookie; 1716 } 1717} 1718 1719if ( document.readyState === 'loading' ) { 1720 document.addEventListener( 'DOMContentLoaded', initLastSearchCookie, { once: true } ); 1721} else { 1722 initLastSearchCookie(); 1723} 1724} )(); 1725 1726 1727/***/ }), 1728 1729/***/ 1498: 1730/***/ (() => { 1731 1732( function() { 1733 const handleOptinMonsterError = function( evt, data, obj ) { 1734 window.console.log( [ evt, data, obj ] ); 1735 }; 1736 1737 const initOptinMonsterOverrides = function() { 1738 document.addEventListener( 'OptinMonsterOnError', function( evt ) { 1739 handleOptinMonsterError( evt, evt?.detail ); 1740 } ); 1741 }; 1742 1743 if ( document.readyState === 'loading' ) { 1744 document.addEventListener( 'DOMContentLoaded', initOptinMonsterOverrides, { once: true } ); 1745 } else { 1746 initOptinMonsterOverrides(); 1747 } 1748} )(); 1749 1750/***/ }), 1751 1752/***/ 1870: 1753/***/ (() => { 1754 1755/** 1756 * Helps with accessibility for keyboard only users. 1757 */ 1758( function() { 1759 var is_webkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1, 1760 is_opera = navigator.userAgent.toLowerCase().indexOf( 'opera' ) > -1, 1761 is_ie = navigator.userAgent.toLowerCase().indexOf( 'msie' ) > -1; 1762 1763 if ( ( is_webkit || is_opera || is_ie ) && document.getElementById && window.addEventListener ) { 1764 window.addEventListener( 'hashchange', function() { 1765 var id = location.hash.substring( 1 ), 1766 element; 1767 1768 if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) { 1769 return; 1770 } 1771 1772 element = document.getElementById( id ); 1773 1774 if ( element ) { 1775 if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) { 1776 element.tabIndex = -1; 1777 } 1778 1779 element.focus(); 1780 } 1781 }, false ); 1782 } 1783})(); 1784 1785/***/ }), 1786 1787/***/ 6905: 1788/***/ (() => { 1789 1790/** 1791 * After everything has loaded, add a "ready" class to <body>. 1792 */ 1793( function( window ) { 1794 const context = { 1795 window: window, 1796 body: document.body, 1797 }; 1798 1799 const addBodyClass = function() { 1800 context.body.classList.add( 'ready' ); 1801 }; 1802 1803 const bindEvents = function() { 1804 if ( document.readyState === 'complete' ) {
1805 addBodyClass(); 1806 } else { 1807 context.window.addEventListener( 'load', addBodyClass, { once: true } ); 1808 } 1809 }; 1810 1811 const initWindowReady = function() { 1812 bindEvents(); 1813 }; 1814 1815 if ( document.readyState === 'loading' ) { 1816 document.addEventListener( 'DOMContentLoaded', initWindowReady, { once: true } ); 1817 } else { 1818 initWindowReady(); 1819 } 1820} )( window ); 1821 1822 1823/***/ }) 1824 1825/******/ }); 1826/************************************************************************/ 1827/******/ // The module cache 1828/******/ var __webpack_module_cache__ = {}; 1829/******/ 1830/******/ // The require function 1831/******/ function __webpack_require__(moduleId) { 1832/******/ // Check if module is in cache 1833/******/ var cachedModule = __webpack_module_cache__[moduleId]; 1834/******/ if (cachedModule !== undefined) { 1835/******/ return cachedModule.exports; 1836/******/ } 1837/******/ // Create a new module (and put it into the cache) 1838/******/ var module = __webpack_module_cache__[moduleId] = { 1839/******/ // no module.id needed 1840/******/ // no module.loaded needed 1841/******/ exports: {} 1842/******/ }; 1843/******/ 1844/******/ // Execute the module function 1845/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 1846/******/ 1847/******/ // Return the exports of the module 1848/******/ return module.exports; 1849/******/ } 1850/******/ 1851/************************************************************************/ 1852// This entry needs to be wrapped in an IIFE because it needs to be in strict mode. 1853(() => { 1854"use strict"; 1855 1856// EXTERNAL MODULE: ./scripts/theme.js 1857var theme = __webpack_require__(695); 1858// EXTERNAL MODULE: ./scripts/navigation.js 1859var navigation = __webpack_require__(5818); 1860// EXTERNAL MODULE: ../wp-common-theme/scripts/rex/concat/js-enabled.js 1861var js_enabled = __webpack_require__(4300); 1862// EXTERNAL MODULE: ../wp-common-theme/scripts/rex/concat/optin-monster-overrides.js 1863var optin_monster_overrides = __webpack_require__(1498); 1864// EXTERNAL MODULE: ../wp-common-theme/scripts/rex/concat/skip-link-focus-fix.js 1865var skip_link_focus_fix = __webpack_require__(1870); 1866// EXTERNAL MODULE: ../wp-common-theme/scripts/rex/concat/window-ready.js 1867var window_ready = __webpack_require__(6905); 1868;// ../wp-common-theme/scripts/cl2/utils.js 1869/** 1870 * Queue-based proxy for window.colibri.utils. 1871 * 1872 * The authoritative implementations live in wp-common-plugin's utils.js, 1873 * which registers them on window.colibri.utils at load time. 1874 * 1875 * Theme scripts may execute before the plugin bundle, so every call either 1876 * delegates immediately (if the real function exists) or enqueues the 1877 * invocation for the plugin bundle to flush once it finishes loading. 1878 */ 1879 1880( function () { 1881 window.colibri = window.colibri || {}; 1882 window.colibri.utils = window.colibri.utils || {}; 1883 if ( !window.colibri.utils._queue ) { 1884 window.colibri.utils._queue = []; 1885 } 1886} )(); 1887 1888function _callOrQueue( fnName, args ) { 1889 if ( typeof window.colibri.utils[ fnName ] === 'function' ) { 1890 return window.colibri.utils[ fnName ].apply( null, args ); 1891 } 1892 window.colibri.utils._queue.push( { fn: fnName, args: Array.prototype.slice.call( args ) } ); 1893} 1894 1895function pushAnalyticsData() { 1896 return _callOrQueue( 'pushAnalyticsData', arguments ); 1897} 1898 1899function formatTime() { 1900 return _callOrQueue( 'formatTime', arguments ); 1901} 1902 1903function maybeUpdateTimezones() { 1904 return _callOrQueue( 'maybeUpdateTimezones', arguments ); 1905} 1906 1907function maybeInitMapLinks() { 1908 return _callOrQueue( 'maybeInitMapLinks', arguments ); 1909} 1910 1911function extractStateCode( productCode ) { 1912 if ( !productCode || typeof productCode !== 'string' ) { 1913 return ''; 1914 } 1915 const parts = productCode.split( '-' ); 1916 return parts[ 2 ] ? String( parts[ 2 ] ).toUpperCase() : ''; 1917} 1918 1919function extractProductId( productCode ) { 1920 return + productCode.split( '-' )[ 0 ]; 1921} 1922 1923function extractDeliveryMethodId( productCode ) { 1924 return + productCode.split( '-' )[ 1 ]; 1925} 1926 1927function simpleSlugify( text ) { 1928 return text.replace( /\s+/g, '-' ).toLowerCase(); 1929} 1930 1931;// ../wp-common-theme/scripts/rex/concat/cds-modal.js 1932 1933 1934function init() { 1935 const body = document.body; 1936 const modalState = new WeakMap(); 1937 let originalBodyPaddingRight = null; 1938 1939 function getScrollbarWidth() { 1940 const viewportDelta = window.innerWidth - document.documentElement.clientWidth; 1941 if ( viewportDelta > 0 ) { 1942 return viewportDelta; 1943 } 1944 1945 const scrollDiv = document.createElement( 'div' ); 1946 scrollDiv.style.position = 'absolute'; 1947 scrollDiv.style.top = '-9999px'; 1948 scrollDiv.style.width = '50px'; 1949 scrollDiv.style.height = '50px'; 1950 scrollDiv.style.overflow = 'scroll'; 1951 body.appendChild( scrollDiv ); 1952 const width = scrollDiv.offsetWidth - scrollDiv.clientWidth; 1953 body.removeChild( scrollDiv ); 1954 return width; 1955 } 1956 1957 function adjustBodyPaddingForModal() {
1958 if ( body.classList.contains( 'modal-open' ) ) { 1959 return; 1960 } 1961 1962 const scrollbarWidth = getScrollbarWidth(); 1963 if ( scrollbarWidth <= 0 ) { 1964 return; 1965 } 1966 1967 originalBodyPaddingRight = body.style.paddingRight; 1968 const computedPadding = parseFloat( window.getComputedStyle( body ).paddingRight ) || 0; 1969 body.style.paddingRight = `${computedPadding + scrollbarWidth}px`; 1970 } 1971 1972 function resetBodyPaddingAfterModal() { 1973 if ( originalBodyPaddingRight === null ) { 1974 return; 1975 } 1976 body.style.paddingRight = originalBodyPaddingRight; 1977 originalBodyPaddingRight = null; 1978 } 1979 1980 function triggerColibriEvent( target, eventName, detail, jQueryPayload ) { 1981 target.dispatchEvent( new CustomEvent( eventName, { detail } ) ); 1982 if ( typeof window.jQuery === 'function' ) { 1983 window.jQuery( target ).trigger( eventName, [ jQueryPayload ?? detail ] ); 1984 } 1985 } 1986 1987 function releaseModalFocus( modal, restoreTo ) { 1988 const active = document.activeElement; 1989 if ( ! ( active instanceof Element ) || ( active !== modal && ! modal.contains( active ) ) ) { 1990 return; 1991 } 1992 if ( restoreTo instanceof Element && document.contains( restoreTo ) && typeof restoreTo.focus === 'function' ) { 1993 restoreTo.focus(); 1994 return; 1995 } 1996 active.blur(); 1997 } 1998 1999 function concealModal( modal ) { 2000 modal.inert = true; 2001 modal.setAttribute( 'aria-hidden', 'true' ); 2002 } 2003 2004 function revealModal( modal ) { 2005 modal.inert = false; 2006 modal.removeAttribute( 'inert' ); 2007 modal.removeAttribute( 'aria-hidden' ); 2008 } 2009 2010 function showModal( modal, opener ) { 2011 let state = modalState.get( modal ); 2012 if ( state ) { 2013 modal.removeEventListener( 'click', state.onBackdropClick ); 2014 modal.removeEventListener( 'click', state.onDismissClick ); 2015 document.removeEventListener( 'keydown', state.onKeydown ); 2016 } 2017 state = state || {}; 2018 2019 if ( state.backdrop ) { 2020 state.backdrop.remove(); 2021 } 2022 2023 const backdrop = document.createElement( 'div' ); 2024 backdrop.className = 'modal-backdrop fade show'; 2025 document.body.appendChild( backdrop ); 2026 2027 const closeModal = function() { 2028 hideModal( modal ); 2029 }; 2030 2031 const onBackdropClick = function( event ) { 2032 if ( event.target === modal ) { 2033 closeModal(); 2034 } 2035 }; 2036 const onDismissClick = function( event ) { 2037 const dismissButton = event.target.closest( '[data-dismiss="modal"]' ); 2038 if ( dismissButton && modal.contains( dismissButton ) ) { 2039 event.preventDefault(); 2040 closeModal(); 2041 } 2042 }; 2043 const onKeydown = function( event ) { 2044 if ( event.key === 'Escape' || event.keyCode === 27 ) { 2045 closeModal(); 2046 } 2047 }; 2048 2049 modal.addEventListener( 'click', onBackdropClick ); 2050 modal.addEventListener( 'click', onDismissClick ); 2051 document.addEventListener( 'keydown', onKeydown ); 2052 2053 state = { 2054 ...state, 2055 backdrop, 2056 onBackdropClick, 2057 onDismissClick, 2058 onKeydown, 2059 opener: opener || state.opener || null, 2060 }; 2061 modalState.set( modal, state ); 2062 2063 adjustBodyPaddingForModal();
2064 document.body.classList.add( 'modal-open' ); 2065 modal.style.display = 'block'; 2066 modal.classList.add( 'show' ); 2067 revealModal( modal ); 2068 modal.setAttribute( 'aria-modal', 'true' ); 2069 modal.focus(); 2070 } 2071 2072 function hideModal( modal ) { 2073 const state = modalState.get( modal ); 2074 if ( state ) { 2075 modal.removeEventListener( 'click', state.onBackdropClick ); 2076 modal.removeEventListener( 'click', state.onDismissClick ); 2077 document.removeEventListener( 'keydown', state.onKeydown ); 2078 state.backdrop?.remove(); 2079 } 2080 2081 releaseModalFocus( modal, state?.opener ); 2082 modal.classList.remove( 'show' ); 2083 modal.style.display = 'none'; 2084 concealModal( modal ); 2085 modal.removeAttribute( 'aria-modal' ); 2086 if ( !document.querySelector( '.modal.show' ) ) { 2087 document.body.classList.remove( 'modal-open' ); 2088 resetBodyPaddingAfterModal(); 2089 } 2090 2091 const hiddenEvent = new CustomEvent( 'hidden.bs.modal', { bubbles: true } ); 2092 modal.dispatchEvent( hiddenEvent ); 2093 if ( typeof window.jQuery === 'function' ) { 2094 window.jQuery( modal ).trigger( 'hidden.bs.modal' ); 2095 } 2096 modalState.delete( modal ); 2097 } 2098 2099 function showModalContent( modal, content, opener ) { 2100 const modalBody = modal.querySelector( '.modal-body' ); 2101 if ( !modalBody ) { 2102 return; 2103 } 2104 modalBody.innerHTML = content; 2105 2106 setTimeout( () => { 2107 const jqContainer = typeof window.jQuery === 'function' ? window.jQuery( modalBody ) : modalBody; 2108 triggerColibriEvent( 2109 body, 2110 'ColibriEvent:AFTER_MODAL_UPDATED', 2111 { container: modalBody, $container: modalBody }, 2112 { $container: jqContainer } 2113 ); 2114 }, 0 ); 2115 2116 const addToCartContainer = modalBody.querySelector( '.product-actions .add-to-cart-container, .product-modal .add-to-cart-container' ); 2117 setTimeout( () => { 2118 maybeUpdateTimezones( modalBody ); 2119 if ( addToCartContainer ) { 2120 const jqContainer = typeof window.jQuery === 'function' ? window.jQuery( addToCartContainer ) : addToCartContainer; 2121 triggerColibriEvent( 2122 window, 2123 'ColibriEvent:INIT_ADD_TO_CART', 2124 { container: addToCartContainer, $container: addToCartContainer }, 2125 jqContainer 2126 ); 2127 } 2128 window.affirm?.ui?.ready?.( () => window.affirm.ui.refresh?.() ); 2129 }, 1 ); 2130 2131 showModal( modal, opener ); 2132 2133 if ( !modal.dataset.iframeCleanupBound ) { 2134 modal.addEventListener( 'hidden.bs.modal', function() { 2135 modal.querySelectorAll( 'iframe' ).forEach( iframe => { 2136 iframe.removeAttribute( 'src' ); 2137 } ); 2138 } ); 2139 modal.dataset.iframeCleanupBound = '1'; 2140 } 2141 } 2142 2143 function getModal( modalClass ) { 2144 const selector = 'body > .modal' + ( modalClass ? '.' + modalClass : '' ) + ':not(.modal--map)'; 2145 let modal = document.querySelector( selector ); 2146 if ( !modal ) { 2147 modal = document.createElement( 'div' ); 2148 modal.className = 'modal' + ( modalClass ? ' ' + modalClass : '' ); 2149 modal.tabIndex = -1; 2150 modal.setAttribute( 'role', 'dialog' ); 2151 concealModal( modal ); 2152 modal.innerHTML = 2153 '<div class="modal-dialog modal-dialog-centered" role="document">' + 2154 '<div class="modal-content">' + 2155 '<div class="modal-header">' + 2156 '<h5 class="modal-title"></h5>' + 2157 '<button type="button" class="close" data-dismiss="modal" aria-label="Close" title="Close" tabindex="0">' + 2158 '<span aria-hidden="true">×</span>' + 2159 '</button>' + 2160 '</div>' + 2161 '<div class="modal-body"></div>' + 2162 '</div>' + 2163 '</div>'; 2164 document.body.appendChild( modal ); 2165 } 2166 return modal; 2167 } 2168 2169 body.addEventListener( 'click', function( e ) { 2170 const link = e.target.closest( '[rel="modal"]' ); 2171 if ( !link ) { 2172 return; 2173 } 2174 handleModalLink( e, link ); 2175 } ); 2176 2177 body.addEventListener( 'keydown', function( e ) { 2178 const link = e.target.closest( '[rel="modal"]' ); 2179 if ( !link ) { 2180 return; 2181 } 2182 handleModalLink( e, link ); 2183 } ); 2184 2185 /** 2186 * Legacy overlays with cg-ajax product URLs but no data-product-preview (cached HTML). 2187 * 2188 * @param {Element} link 2189 * @return {boolean} 2190 */ 2191 function isMarketingAjaxOverlayWithoutPreviewAttr( link ) { 2192 if ( link.matches( '[data-product-preview]' ) ) { 2193 return false; 2194 }
2195 const code = String( link?.dataset?.productCode || '' ).trim(); 2196 const ajax = String( link?.dataset?.ajaxSource || '' ); 2197 return ( 2198 code !== '' && 2199 ( ajax.includes( 'cg-ajax-content=product-modal' ) || 2200 ajax.includes( 'cg-ajax-content=quick-view-modal' ) ) 2201 ); 2202 } 2203 2204 function handleModalLink( e, link ) { 2205 // colibri-product-preview (fetch + renderTemplate + cache); do not cds-fetch these. 2206 if ( link.matches( '[data-product-preview]' ) ) { 2207 return; 2208 } 2209 if ( isMarketingAjaxOverlayWithoutPreviewAttr( link ) ) { 2210 return; 2211 } 2212 2213 if ( e.key !== 'Tab' && e.keyCode !== 9 ) { 2214 e.preventDefault(); 2215 } 2216 2217 if ( !( e.type === 'click' || ( e.type === 'keydown' && ( e.key === 'Enter' || e.keyCode === 13 ) ) ) ) { 2218 return; 2219 } 2220 2221 const modalClass = link.dataset.modalClass || 'modal--default'; 2222 const modal = getModal( modalClass ); 2223 const sourceSelector = link.dataset.modalSource || link.getAttribute( 'href' ); 2224 const ajaxSource = link.dataset.ajaxSource; 2225 2226 const title = 2227 link.dataset.modalTitle || 2228 link.dataset.title || 2229 link.getAttribute( 'title' ) || 2230 link.innerHTML.trim(); 2231 const modalTitle = modal.querySelector( '.modal-title' ); 2232 if ( modalTitle ) { 2233 modalTitle.innerHTML = title; 2234 } 2235 2236 if ( link.matches( '.custom-pm' ) ) { 2237 showModalContent( modal, link.dataset.content || '', link ); 2238 } else if ( link.matches( '.child-link' ) ) { 2239 showModalContent( modal, link.nextElementSibling?.innerHTML || '', link ); 2240 } else if ( ajaxSource ) { 2241 const modalBody = modal.querySelector( '.modal-body' ); 2242 if ( modalBody ) { 2243 modalBody.innerHTML = '<p>Loading...</p>'; 2244 } 2245 fetch( ajaxSource, { method: 'GET' } ) 2246 .then( response => response.ok ? response.text() : Promise.reject() ) 2247 .then( response => { 2248 showModalContent( modal, response, link ); 2249 } ) 2250 .catch( () => { 2251 if ( modalBody ) { 2252 modalBody.innerHTML = '<p style="color:#ff0000">Could not load data.</p>'; 2253 } 2254 } ); 2255 } else if ( sourceSelector && ( sourceSelector.startsWith( 'http' ) || sourceSelector.startsWith( '/' ) ) ) { 2256 showModalContent( modal, link.nextElementSibling?.innerHTML || '', link ); 2257 } else if ( sourceSelector ) { 2258 const source = document.querySelector( sourceSelector ); 2259 if ( source ) { 2260 showModalContent( modal, source.innerHTML, link ); 2261 if ( sourceSelector.startsWith( '#' ) && source.dataset.wpBlock === 'modal' ) { 2262 if ( source.className ) { 2263 modal.classList.add( ...source.className.split( /\s+/ ).filter( Boolean ) ); 2264 } 2265 if ( source.dataset.modalSize ) { 2266 modal.setAttribute( 'data-modal-size', source.dataset.modalSize ); 2267 } 2268 } 2269 } 2270 } 2271 2272 return false; 2273 } 2274} 2275 2276if ( document.readyState === 'loading' ) { 2277 document.addEventListener( 'DOMContentLoaded', init ); 2278} else { 2279 init(); 2280} 2281// EXTERNAL MODULE: ../wp-common-theme/scripts/rex/concat/last-search-cookie.js 2282var last_search_cookie = __webpack_require__(4551); 2283// EXTERNAL MODULE: ../wp-common-theme/patterns/pattern-faq.js 2284var pattern_faq = __webpack_require__(7422); 2285// EXTERNAL MODULE: ../wp-common-theme/patterns/pattern-featured-blog.js 2286var pattern_featured_blog = __webpack_require__(8115); 2287// EXTERNAL MODULE: ../wp-common-theme/patterns/pattern-lowest-package-price.js 2288var pattern_lowest_package_price = __webpack_require__(1563); 2289// EXTERNAL MODULE: ./patterns/pattern-anchor-bar.js 2290var pattern_anchor_bar = __webpack_require__(1637); 2291// EXTERNAL MODULE: ./patterns/pattern-allied-steps.js 2292var pattern_allied_steps = __webpack_require__(9873); 2293// EXTERNAL MODULE: ./patterns/pattern-allied-collapsed-text.js 2294var pattern_allied_collapsed_text = __webpack_require__(5907); 2295// EXTERNAL MODULE: ./patterns/pattern-allied-tabs.js 2296var pattern_allied_tabs = __webpack_require__(304); 2297;// ./theme-lean.js 2298/*-------------------------------------------------------------- 2299# Common - Webpack Theme Entrypoint 2300--------------------------------------------------------------*/ 2301 2302// JS 2303 2304 2305 2306//import '../wp-common-theme/scripts/rex/concat/navigation'; 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323})(); 2324 2325// This entry needs to be wrapped in an IIFE because it needs to be in strict mode. 2326(() => { 2327"use strict";
2328// extracted by mini-css-extract-plugin 2329 2330})(); 2331 2332/******/ })() 2333;
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.