1// Shared front-end Commerce7 UI enhancements. 2 3const foleyC7UiGa4Analytics = (() => { 4 const CHECKOUT_LINK_SELECTOR = '[data-testid="proceed-to-checkout-link"]'; 5 const CHECKOUT_MARKER_MAX_AGE = 24 * 60 * 60 * 1000; 6 const DATA_LAYER_WRAPPER_MARKER = '__foleyC7UiGa4Normalizer'; 7 const LIST_ITEM_SELECTOR = '.foley-c7-ui-wine-card[data-ga4-item-id][data-ga4-item-name]'; 8 const PDP_SELECTOR = '[data-ga4-product-context="pdp"][data-ga4-item-id][data-ga4-item-name]'; 9 const STORAGE_NAMESPACE = 'foley-c7-ui:ga4'; 10 const REMOVE_FROM_CART_MAX_AGE = 15000; 11 const RECEIPT_SEGMENT = 'order-receipt'; 12 const settings = window.foleyC7UiGa4Settings || {}; 13 const suppressedDataLayerEntry = Symbol('suppressedDataLayerEntry'); 14 const listImpressionStates = new WeakMap(); 15 const pdpViewFingerprints = new Set(); 16 17 let checkoutListenerRegistered = false; 18 let lastCheckoutActivation = 0; 19 let pendingRemovals = []; 20 let pluginPushDepth = 0; 21 let removeListenerRegistered = false; 22 23 function getDataLayer() { 24 window.dataLayer = window.dataLayer || []; 25 return 'function' === typeof window.dataLayer.push ? window.dataLayer : null; 26 } 27 28 function getDataLayerLength() { 29 const dataLayer = getDataLayer(); 30 return dataLayer && 'number' === typeof dataLayer.length ? dataLayer.length : 0; 31 } 32 33 function pushEcommerceEvent(eventName, ecommerce) { 34 const dataLayer = getDataLayer(); 35 const requiresCheckoutMarker = 'begin_checkout' === eventName; 36 37 if (!dataLayer || !eventName || !ecommerce) { 38 return false; 39 } 40 41 if (requiresCheckoutMarker && !setCheckoutMarker()) { 42 return false; 43 } 44 45 try { 46 pluginPushDepth++; 47 48 try { 49 dataLayer.push({ ecommerce: null }); 50 dataLayer.push({ 51 event: eventName, 52 ecommerce 53 }); 54 } finally { 55 pluginPushDepth--; 56 } 57 58 return true; 59 } catch (error) { 60 if (requiresCheckoutMarker) { 61 clearCheckoutMarker(); 62 } 63 64 return false; 65 } 66 } 67 68 function cloneEcommerce(ecommerce) { 69 if (!ecommerce || 'object' !== typeof ecommerce || Array.isArray(ecommerce)) { 70 return null; 71 } 72 73 try { 74 return stripUndefinedProperties(ecommerce); 75 } catch (error) { 76 return null; 77 } 78 } 79 80 function parseCents(value) { 81 if (null === value || '' === value || 'boolean' === typeof value) { 82 return null; 83 } 84 85 const cents = Number(value); 86 return Number.isFinite(cents) && cents >= 0 ? cents : null; 87 } 88 89 function centsToCurrency(value) { 90 const cents = parseCents(value); 91 return null === cents ? null : cents / 100; 92 } 93 94 function isNumericValue(value) { 95 return ( 96 null !== value && 97 '' !== value && 98 'boolean' !== typeof value && 99 Number.isFinite(Number(value)) 100 ); 101 } 102 103 function getCurrency() { 104 return String(settings.currency || '').trim().toUpperCase(); 105 } 106 107 function isValidCurrency(value) { 108 return 'string' === typeof value && '' !== value.trim(); 109 } 110 111 function normalizeComparableText(value) { 112 return String(value || '').trim().replace(/\s+/g, ' ').toLowerCase(); 113 } 114 115 function stripUndefinedProperties(value) { 116 if (Array.isArray(value)) { 117 return value 118 .filter(item => undefined !== item) 119 .map(stripUndefinedProperties); 120 } 121 122 if (value && 'object' === typeof value) { 123 const clonedValue = {}; 124 125 Object.keys(value).forEach(key => { 126 if (undefined !== value[key]) { 127 clonedValue[key] = stripUndefinedProperties(value[key]); 128 } 129 }); 130 131 return clonedValue; 132 } 133 134 return value; 135 } 136 137 function ensureEventCurrency(ecommerce) { 138 if (!ecommerce || 'object' !== typeof ecommerce || Array.isArray(ecommerce)) { 139 return; 140 } 141 142 if (!isValidCurrency(ecommerce.currency)) { 143 const currency = getCurrency(); 144 145 if (currency) { 146 ecommerce.currency = currency; 147 } else { 148 delete ecommerce.currency; 149 } 150 } 151 } 152 153 function cleanItemCurrencies(items) { 154 if (!Array.isArray(items)) { 155 return; 156 } 157
158 items.forEach(item => { 159 if ( 160 item && 161 'object' === typeof item && 162 !Array.isArray(item) && 163 Object.prototype.hasOwnProperty.call(item, 'currency') && 164 !isValidCurrency(item.currency) 165 ) { 166 delete item.currency; 167 } 168 }); 169 } 170 171 function cloneDataLayerEntry(entry) { 172 if (!entry || 'object' !== typeof entry || Array.isArray(entry)) { 173 return null; 174 } 175 176 try { 177 return stripUndefinedProperties(entry); 178 } catch (error) { 179 return null; 180 } 181 } 182 183 function analyticsElementMatchesItem(element, item) { 184 const eventItemId = String(item?.item_id || '').trim(); 185 const eventItemName = normalizeComparableText(item?.item_name); 186 const candidateIds = [ 187 element.dataset.ga4ItemId, 188 element.dataset.ga4Commerce7Id 189 ].map(value => String(value || '').trim()).filter(Boolean); 190 const candidateName = normalizeComparableText(element.dataset.ga4ItemName); 191 192 return Boolean( 193 (eventItemId && candidateIds.includes(eventItemId)) || 194 (eventItemName && candidateName && eventItemName === candidateName) 195 ); 196 } 197 198 function shouldSuppressCustomContextViewItem(entry) { 199 const items = entry?.ecommerce?.items; 200 201 if (!Array.isArray(items) || 1 !== items.length || !items[0]) { 202 return false; 203 } 204 205 const candidates = [ 206 ...document.querySelectorAll(PDP_SELECTOR), 207 ...document.querySelectorAll(`.js-foley-c7-ui-wine-listing ${LIST_ITEM_SELECTOR}`), 208 ...document.querySelectorAll(`.foley-c7-ui-wine-detail ${L
208IST_ITEM_SELECTOR}`) 209 ]; 210 211 return candidates.some(element => analyticsElementMatchesItem(element, items[0])); 212 } 213 214 function normalizeViewCartEntry(entry) { 215 const normalizedEntry = cloneDataLayerEntry(entry); 216 217 if (!normalizedEntry?.ecommerce) { 218 return entry; 219 } 220 221 ensureEventCurrency(normalizedEntry.ecommerce); 222 cleanItemCurrencies(normalizedEntry.ecommerce.items); 223 return normalizedEntry; 224 } 225 226 function normalizeViewItemEntry(entry) { 227 if (shouldSuppressCustomContextViewItem(entry)) { 228 return suppressedDataLayerEntry; 229 } 230 231 const normalizedEntry = cloneDataLayerEntry(entry); 232 const ecommerce = normalizedEntry?.ecommerce; 233 234 if (!ecommerce) { 235 return entry; 236 } 237 238 ensureEventCurrency(ecommerce); 239 cleanItemCurrencies(ecommerce.items); 240 241 if (Array.isArray(ecommerce.items) && 1 === ecommerce.items.length) { 242 const item = ecommerce.items[0]; 243 244 if (item && 'object' === typeof item && !Array.isArray(item)) { 245 if (!isNumericValue(item.quantity) || Number(item.quantity) < 1) { 246 item.quantity = 1; 247 } 248 249 if (!isNumericValue(ecommerce.value) && isNumericValue(item.price)) { 250 ecommerce.value = Math.round( 251 Number(item.price) * Number(item.quantity) * 100 252 ) / 100; 253 } 254 } 255 } 256 257 return normalizedEntry; 258 } 259 260 function cleanupPendingRemovals(now = Date.now()) { 261 pendingRemovals = pendingRemovals.filter(removal => ( 262 now - removal.timestamp >= 0 && 263 now - removal.timestamp <= REMOVE_FROM_CART_MAX_AGE 264 )); 265 } 266 267 function pendingRemovalMatchesItem(removal, item) { 268 const itemName = normalizeComparableText(item?.item_name); 269 const itemVariant = normalizeComparableText(item?.item_variant); 270 271 if (!itemName || itemName !== removal.itemName) { 272 return false; 273 } 274 275 return !removal.itemVariant || !itemVariant || itemVariant === removal.itemVariant; 276 } 277 278 function consumePendingRemoval(item) { 279 cleanupPendingRemovals(); 280 281 for (let index = pendingRemovals.length - 1; index >= 0; index--) { 282 if (pendingRemovalMatchesItem(pendingRemovals[index], item)) { 283 return pendingRemovals.splice(index, 1)[0]; 284 } 285 } 286 287 return null; 288 } 289 290 function normalizeRemoveFromCartEntry(entry) { 291 const normalizedEntry = cloneDataLayerEntry(entry); 292 const ecommerce = normalizedEntry?.ecommerce; 293 294 if (!ecommerce) { 295 return entry; 296 } 297 298 ensureEventCurrency(ecommerce); 299 cleanItemCurrencies(ecommerce.items); 300 301 if (!Array.isArray(ecommerce.items) || !ecommerce.items.length) { 302 return normalizedEntry; 303 } 304 305 let value = 0; 306 let hasCompleteValue = true; 307 308 ecommerce.items.forEach(item => { 309 if (!item || 'object' !== typeof item || Array.isArray(item)) { 310 hasCompleteValue = false; 311 return; 312 } 313 314 const pendingRemoval = consumePendingRemoval(item); 315 316 if (!isNumericValue(item.quantity) || Number(item.quantity) < 1) { 317 if (pendingRemoval) { 318 item.quantity = pendingRemoval.quantity; 319 } 320 } 321 322 if ( 323 !Number.isInteger(Number(item.quantity)) || 324 Number(item.quantity) < 1 || 325 !isNumericValue(item.price) 326 ) { 327 hasCompleteValue = false; 328 return; 329 } 330 331 value += Number(item.price) * Number(item.quantity); 332 }); 333 334 if (hasCompleteValue) { 335 ecommerce.value = Math.round(value * 100) / 100; 336 } 337 338 return normalizedEntry; 339 } 340 341 function normalizePurchaseEntry(entry) { 342 const normalizedEntry = cloneDataLayerEntry(entry); 343 const transactionId = normalizedEntry?.ecommerce?.transaction_id; 344 345 if (!normalizedEntry?.ecommerce || null === transactionId || undefined === transactionId) { 346 return normalizedEntry || entry; 347 } 348 349 normalizedEntry.ecommerce.transaction_id = String(transactionId); 350 return normalizedEntry; 351 } 352 353 function normalizeDataLayerEntry(entry) { 354 if (!entry || 'object' !== typeof entry || Array.isArray(entry)) { 355 return entry; 356 } 357 358 switch (entry.event) { 359 case 'begin_checkout': 360 if (isValidBeginCheckoutEcommerce(entry.ecommerce)) { 361 setCheckoutMarker(); 362 } 363 364 return entry; 365 case 'view_cart': 366 return normalizeViewCartEntry(entry); 367 case 'view_item': 368 return normalizeViewItemEntry(entry); 369 case 'remove_from_cart': 370 return normalizeRemoveFromCartEntry(entry); 371 case 'purchase': 372 return normalizePurchaseEntry(entry); 373 default: 374 return entry; 375 } 376 } 377 378 function installDataLayerNormalizer() { 379 const dataLayer = getDataLayer(); 380 const originalPush = dataLayer?.push; 381 382 if (!dataLayer || 'function' !== typeof originalPush || originalPush[DATA_LAYER_WRAPPER_MARKER]) { 383 return false;
384 } 385 386 function normalizedPush(...entries) { 387 if (pluginPushDepth > 0) { 388 return originalPush.apply(this, entries); 389 } 390 391 try { 392 const normalizedEntries = entries 393 .map(entry => { 394 try { 395 return normalizeDataLayerEntry(entry); 396 } catch (error) { 397 return entry; 398 } 399 }) 400 .filter(entry => suppressedDataLayerEntry !== entry); 401 402 return originalPush.apply(this, normalizedEntries); 403 } catch (error) { 404 return originalPush.apply(this, entries); 405 } 406 } 407 408 try { 409 Object.defineProperty(normalizedPush, DATA_LAYER_WRAPPER_MARKER, { 410 configurable: false, 411 enumerable: false, 412 value: true, 413 writable: false 414 }); 415 dataLayer.push = normalizedPush; 416 return dataLayer.push === normalizedPush; 417 } catch (error) { 418 return false; 419 } 420 } 421 422 function registerRemoveFromCartCapture() { 423 if (removeListenerRegistered) { 424 return; 425 } 426 427 removeListenerRegistered = true; 428 429 document.addEventListener('click', event => { 430 const target = event.target instanceof Element ? event.target : null; 431 const removeButton = target?.closest('.c7-order-item button.c7-link'); 432 433 if ( 434 !removeButton || 435 'remove' !== normalizeComparableText(removeButton.textContent) || 436 removeButton.matches('[disabled], [aria-disabled="true"]') 437 ) { 438 return; 439 } 440 441 const orderItem = removeButton.closest('.c7-order-item'); 442 const quantityControl = orderItem?.querySelector( 443 'select[name="c7-quantityOther"], input[name="quantity"]' 444 ); 445 const quantity = Number(quantityControl?.value); 446 const itemName = normalizeComparableText( 447 orderItem?.querySelector('.c7-order-item__title')?.textContent 448 ); 449 const itemVariant = normalizeComparableText( 450 orderItem?.querySelector('.c7-order-item__description')?.textContent 451 ); 452 453 if (!Number.isInteger(quantity) || quantity < 1 || !itemName) { 454 return; 455 } 456 457 cleanupPendingRemovals(); 458 pendingRemovals.push({ 459 itemName, 460 itemVariant, 461 quantity, 462 timestamp: Date.now() 463 }); 464 }, true); 465 } 466 467 function getTenant() { 468 return String( 469 window.foleyC7?.tenantId || 470 window.c7wp_settings?.tenant || 471 document.getElementById('c7-javascript')?.getAttribute('data-tenant') || 472 '' 473 ).trim(); 474 } 475 476 function getStorageScope() { 477 const tenant = getTenant(); 478 479 if (tenant) { 480 return tenant; 481 } 482 483 const siteId = String(settings.siteId || '').trim(); 484 return siteId ? `site-${siteId}` : ''; 485 } 486 487 function getStorageKey(eventName, identifier = '') { 488 const scope = getStorageScope(); 489 490 if (!scope || !eventName) { 491 return ''; 492 } 493 494 return [ 495 STORAGE_NAMESPACE, 496 eventName, 497 encodeURIComponent(scope), 498 identifier ? encodeURIComponent(String(identifier)) : '' 499 ].filter(Boolean).join(':'); 500 } 501 502 function storageGet(storage, key) { 503 if (!storage || !key) { 504 return { success: false, value: null }; 505 } 506 507 try { 508 return { success: true, value: storage.getItem(key) }; 509 } catch (error) { 510 return { success: false, value: null }; 511 } 512 } 513 514 function getBrowserStorage(storageName) { 515 try { 516 return window[storageName] || null; 517 } catch (error) { 518 return null; 519 } 520 } 521 522 function storageSet(storage, key, value) { 523 if (!storage || !key) { 524 return false; 525 } 526 527 try { 528 storage.setItem(key, value); 529 return true; 530 } catch (error) { 531 return false; 532 } 533 } 534 535 function storageRemove(storage, key) { 536 if (!storage || !key) { 537 return false; 538 } 539 540 try { 541 storage.removeItem(key); 542 return true; 543 } catch (error) { 544 return false; 545 } 546 } 547 548 function getCheckoutMarkerKey() { 549 return getStorageKey('checkout-origin'); 550 } 551 552 function setCheckoutMarker() { 553 return storageSet( 554 getBrowserStorage('sessionStorage'), 555 getCheckoutMarkerKey(), 556 JSON.stringify({ timestamp: Date.now() }) 557 ); 558 } 559 560 function clearCheckoutMarker() { 561 storageRemove(getBrowserStorage('sessionStorage'), getCheckoutMarkerKey()); 562 } 563 564 function hasValidCheckoutMarker() {
565 const key = getCheckoutMarkerKey(); 566 const stored = storageGet(getBrowserStorage('sessionStorage'), key); 567 568 if (!stored.success || !stored.value) { 569 return false; 570 } 571 572 try { 573 const marker = JSON.parse(stored.value); 574 const timestamp = Number(marker?.timestamp); 575 const age = Date.now() - timestamp; 576 577 if (!Number.isFinite(timestamp) || age < 0 || age > CHECKOUT_MARKER_MAX_AGE) { 578 storageRemove(getBrowserStorage('sessionStorage'), key); 579 return false; 580 } 581 582 return true; 583 } catch (error) { 584 storageRemove(getBrowserStorage('sessionStorage'), key); 585 return false; 586 } 587 } 588 589 function getMostRecentDataLayerEvent(eventName) { 590 const dataLayer = getDataLayer(); 591 592 if (!dataLayer || !eventName) { 593 return null; 594 } 595 596 for (let index = dataLayer.length - 1; index >= 0; index--) { 597 if (dataLayer[index]?.event === eventName) { 598 return dataLayer[index]; 599 } 600 } 601 602 return null; 603 } 604 605 function isValidViewCartEcommerce(ecommerce) { 606 return Boolean( 607 ecommerce && 608 'object' === typeof ecommerce && 609 !Array.isArray(ecommerce) && 610 'string' === typeof ecommerce.currency && 611 ecommerce.currency.trim() && 612 isNumericValue(ecommerce.value) && 613 isNumericValue(ecommerce.tax) && 614 isNumericValue(ecommerce.shipping) && 615 'string' === typeof ecommerce.payment_type && 616 ecommerce.payment_type.trim() && 617 Array.isArray(ecommerce.items) && 618 ecommerce.items.length 619 ); 620 } 621 622 function isValidBeginCheckoutEcommerce(ecommerce) { 623 return Boolean( 624 ecommerce && 625 'object' === typeof ecommerce && 626 !Array.isArray(ecommerce) && 627 isValidCurrency(ecommerce.currency) && 628 isNumericValue(ecommerce.value) && 629 Array.isArray(ecommerce.items) && 630 ecommerce.items.length && 631 ecommerce.items.every(item => ( 632 item && 633 'object' === typeof item && 634 !Array.isArray(item) && 635 ( 636 String(item.item_id || '').trim() || 637 String(item.item_name || '').trim() 638 ) 639 )) 640 ); 641 } 642 643 function registerBeginCheckout() { 644 if (checkoutListenerRegistered) { 645 return; 646 } 647 648 checkoutListenerRegistered = true; 649 650 document.addEventListener('click', function (event) { 651 if (!event.isTrusted || (Number.isFinite(event.button) && 0 !== event.button)) { 652 return; 653 } 654 655 const target = event.target instanceof Element ? event.target : null; 656 const checkoutLink = target?.closest(CHECKOUT_LINK_SELECTOR); 657 658 if ( 659 !checkoutLink || 660 checkoutLink.matches('[disabled], [aria-disabled="true"]') 661 ) { 662 return; 663 } 664 665 const now = Date.now(); 666 667 if (now - lastCheckoutActivation < 1000) { 668 return; 669 } 670 671 const viewCart = getMostRecentDataLayerEvent('view_cart'); 672 673 if (!isValidViewCartEcommerce(viewCart?.ecommerce)) { 674 return; 675 } 676 677 const ecommerce = cloneEcommerce(viewCart.ecommerce); 678 679 if (!ecommerce) { 680 return; 681 } 682 683 if (!pushEcommerceEvent('begin_checkout', ecommerce)) { 684 return; 685 } 686 687 lastCheckoutActivation = now; 688 }); 689 } 690 691 function hasMatchingAddToCartEvent(startIndex, sku) { 692 const dataLayer = getDataLayer(); 693 694 if (!dataLayer) { 695 return false; 696 } 697 698 for (let index = Math.max(0, startIndex); index < dataLayer.length; index++) { 699 const entry = dataLayer[index]; 700 701 if ( 702 entry?.event === 'add_to_cart' && 703 Array.isArray(entry.ecommerce?.items) && 704 entry.ecommerce.items.some(item => String(item?.item_id || '') === sku) 705 ) { 706 return true; 707 } 708 } 709 710 return false; 711 } 712 713 function mapAddedItem(item, requestedQuantity) { 714 const sku = String(item?.sku || '').trim(); 715 const productTitle = String(item?.productTitle || '').trim(); 716 const price = centsToCurrency(item?.price); 717 const quantity = Number(requestedQuantity); 718 719 if ( 720 !sku || 721 !productTitle || 722 null === price || 723 !Number.isInteger(quantity) || 724 quantity < 1 725 ) { 726 return null; 727 } 728 729 const mappedItem = { 730 item_id: sku, 731 item_name: productTitle, 732 price, 733 quantity 734 }; 735 const variant = String(item?.productVariantTitle || '').trim(); 736 737 if (variant) { 738 mappedItem.item_variant = variant; 739 } 740 741 return mappedItem; 742 } 743 744 function emitAddToCart(cart, submittedSku, requestedQuantity, dataLayerStartIndex = 0) { 745 try { 746 const sku = String(submittedSku || '').trim(); 747 const matchingItems = Array.isArray(cart?.items) 748 ? cart.items.filter(item => String(item?.sku || '').trim() === sku) 749 : []; 750 751 if (!sku || 1 !== matchingItems.length) { 752 return false;
753 } 754 755 const item = mapAddedItem(matchingItems[0], requestedQuantity); 756 const priceCents = parseCents(matchingItems[0]?.price); 757 const value = null === priceCents 758 ? null 759 : centsToCurrency(priceCents * Number(requestedQuantity)); 760 761 if (!item || null === value || hasMatchingAddToCartEvent(dataLayerStartIndex, item.item_id)) { 762 return false; 763 } 764 765 return pushEcommerceEvent('add_to_cart', { 766 currency: getCurrency(), 767 value, 768 items: [item] 769 }); 770 } catch (error) { 771 return false; 772 } 773 } 774 775 function getDatasetPrice(element) { 776 const rawPrice = String(element?.dataset?.ga4ItemPrice || '').trim(); 777 778 if (!rawPrice || !isNumericValue(rawPrice) || Number(rawPrice) < 0) { 779 return null; 780 } 781 782 return Number(rawPrice); 783 } 784 785 function mapAnalyticsElement(element, index = null, includeCurrency = false) { 786 const itemId = String(element?.dataset?.ga4ItemId || '').trim(); 787 const itemName = String(element?.dataset?.ga4ItemName || '').trim(); 788 789 if (!itemId || !itemName) { 790 return null; 791 } 792 793 const item = { 794 item_id: itemId, 795 item_name: itemName 796 }; 797 const currency = getCurrency(); 798 const price = getDatasetPrice(element); 799 const variant = String(element.dataset.ga4ItemVariant || '').trim(); 800 801 if (includeCurrency && currency) { 802 item.currency = currency; 803 } 804 805 if (null !== price) { 806 item.price = price; 807 } 808 809 if (variant) { 810 item.item_variant = variant; 811 } 812 813 if (Number.isInteger(index) && index >= 0) { 814 item.index = index; 815 } 816 817 return item; 818 } 819 820 function getListImpressionState(listElement) { 821 if (!listImpressionStates.has(listElement)) { 822 listImpressionStates.set(listElement, { 823 currentFingerprint: '', 824 seenItemIds: new Set() 825 }); 826 } 827 828 return listImpressionStates.get(listElement); 829 } 830 831 function emitViewItemList(listElement, options = {}) { 832 try { 833 if (!(listElement instanceof Element)) { 834 return false; 835 } 836 837 const listId = String(listElement.dataset.ga4ItemListId || '').trim(); 838 const listName = String(listElement.dataset.ga4ItemListName || '').trim(); 839 const currency = getCurrency(); 840 const productElements = Array.from(listElement.querySelectorAll(LIST_ITEM_SELECTOR)); 841 const requestedStartIndex = Number(options.startIndex); 842 const startIndex = Number.isInteger(requestedStartIndex) && requestedStartIndex >= 0 843 ? requestedStartIndex 844 : 0; 845 const reset = true === options.reset || 0 === startIndex; 846 const state = getListImpressionState(listElement); 847 848 if (!listId || !listName || !currency || !productElements.length) { 849 return false; 850 } 851 852 const allItems = productElements 853 .map((element, index) => mapAnalyticsElement(element, index, true)) 854 .filter(Boolean); 855 const currentFingerprint = JSON.stringify({ listId, listName, items: allItems }); 856 let items = allItems.filter(item => item.index >= startIndex); 857 let nextSeenItemIds; 858 859 if (reset) { 860 if (!items.length || currentFingerprint === state.currentFingerprint) { 861 return false; 862 } 863 864 nextSeenItemIds = new Set(items.map(item => item.item_id)); 865 } else { 866 items = items.filter(item => !state.seenItemIds.has(item.item_id)); 867 868 if (!items.length) { 869 return false; 870 } 871 872 nextSeenItemIds = new Set(state.seenItemIds); 873 items.forEach(item => nextSeenItemIds.add(item.item_id)); 874 } 875 876 if (!pushEcommerceEvent('view_item_list', { 877 item_list_id: listId, 878 item_list_name: listName, 879 items 880 })) { 881 return false; 882 } 883 884 state.currentFingerprint = currentFingerprint; 885 state.seenItemIds = nextSeenItemIds; 886 return true; 887 } catch (error) { 888 return false; 889 } 890 } 891 892 function emitCustomPdpViewItem() { 893 try { 894 const productElement = document.querySelector(PDP_SELECTOR); 895 const currency = getCurrency(); 896 const item = mapAnalyticsElement(productElement); 897 898 if (!productElement || !currency || !item) { 899 return false;
900 } 901 902 item.quantity = 1; 903 904 const fingerprint = JSON.stringify({ 905 path: location.pathname, 906 item 907 }); 908 909 if (pdpViewFingerprints.has(fingerprint)) { 910 return false; 911 } 912 913 const ecommerce = { 914 currency, 915 items: [item] 916 }; 917 918 if (isNumericValue(item.price)) { 919 ecommerce.value = Number(item.price); 920 } 921 922 if (!pushEcommerceEvent('view_item', ecommerce)) { 923 return false; 924 } 925 926 pdpViewFingerprints.add(fingerprint); 927 return true; 928 } catch (error) { 929 return false; 930 } 931 } 932 933 function sumMonetaryLines(lines) { 934 if (!Array.isArray(lines)) { 935 return 0; 936 } 937 938 let total = 0; 939 940 for (const line of lines) { 941 const price = parseCents(line?.price); 942 943 if (null === price) { 944 return null; 945 } 946 947 total += price; 948 } 949 950 return centsToCurrency(total); 951 } 952 953 function mapOrderItem(item) { 954 const sku = String(item?.sku || '').trim(); 955 const productTitle = String(item?.productTitle || '').trim(); 956 const price = centsToCurrency(item?.price); 957 const quantity = Number(item?.quantity); 958 959 if ( 960 !sku || 961 !productTitle || 962 null === price || 963 !Number.isInteger(quantity) || 964 quantity < 1 965 ) { 966 return null; 967 } 968 969 const mappedItem = { 970 item_id: sku, 971 item_name: productTitle, 972 price, 973 quantity 974 }; 975 const brand = String(item?.vendor || '').trim(); 976 const variant = String(item?.productVariantTitle || '').trim(); 977 978 if (brand) { 979 mappedItem.item_brand = brand; 980 } 981 982 if (variant) { 983 mappedItem.item_variant = variant; 984 } 985 986 return mappedItem; 987 } 988 989 function hasDataLayerPurchase(transactionId) { 990 const dataLayer = getDataLayer(); 991 992 return Boolean(dataLayer?.some(entry => ( 993 entry?.event === 'purchase' && 994 String(entry.ecommerce?.transaction_id || '') === transactionId 995 ))); 996 } 997 998 function emitPurchase(order) { 999 try { 1000 if (!hasValidCheckoutMarker()) { 1001 return false; 1002 } 1003 1004 const transactionId = String(order?.orderNumber ?? '').trim(); 1005 const value = centsToCurrency(order?.total); 1006 const tax = sumMonetaryLines(order?.taxes); 1007 const shipping = sumMonetaryLines(order?.shipping); 1008 const sourceItems = Array.isArray(order?.items) ? order.items : []; 1009 const items = sourceItems.map(mapOrderItem); 1010 1011 if ( 1012 !transactionId || 1013 null === value || 1014 null === tax || 1015 null === shipping || 1016 !sourceItems.length || 1017 items.some(item => null === item) 1018 ) { 1019 return false; 1020 } 1021 1022 const purchaseKey = getStorageKey('purchase', transactionId); 1023 const localStorage = getBrowserStorage('localStorage'); 1024 const purchaseMarker = storageGet(localStorage, purchaseKey); 1025 1026 if (!purchaseKey || !purchaseMarker.success) { 1027 return false; 1028 } 1029 1030 if (null !== purchaseMarker.value) { 1031 return false; 1032 } 1033 1034 if (hasDataLayerPurchase(transactionId)) { 1035 storageSet(localStorage, purchaseKey, JSON.stringify({ timestamp: Date.now() })); 1036 clearCheckoutMarker(); 1037 return false; 1038 } 1039 1040 if (!pushEcommerceEvent('purchase', { 1041 transaction_id: transactionId, 1042 currency: getCurrency(), 1043 value, 1044 tax, 1045 shipping, 1046 items 1047 })) { 1048 return false; 1049 } 1050 1051 storageSet(localStorage, purchaseKey, JSON.stringify({ timestamp: Date.now() })); 1052 clearCheckoutMarker(); 1053 return true; 1054 } catch (error) { 1055 return false; 1056 } 1057 } 1058 1059 function getReceiptOrderId() { 1060 const pathSegments = location.pathname.split('/').filter(Boolean); 1061 const receiptIndex = pathSegments.lastIndexOf(RECEIPT_SEGMENT); 1062 1063 if (-1 === receiptIndex || !pathSegments[receiptIndex + 1]) { 1064 return ''; 1065 } 1066 1067 try { 1068 return decodeURIComponent(pathSegments[receiptIndex + 1]); 1069 } catch (error) { 1070 return ''; 1071 } 1072 } 1073 1074 function isMatchingLimitedOrderRequest(method, requestUrl, receiptOrderId) { 1075 if ('GET' !== String(method || 'GET').toUpperCase()) { 1076 return false;
1077 } 1078 1079 try { 1080 const url = new URL(String(requestUrl), location.href); 1081 const match = url.pathname.match(/^\/v2\/order\/([^/]+)\/limited\/?$/); 1082 1083 return Boolean( 1084 match && 1085 'api.commerce7.com' === url.hostname && 1086 decodeURIComponent(match[1]) === receiptOrderId 1087 ); 1088 } catch (error) { 1089 return false; 1090 } 1091 } 1092 1093 function parseXhrJson(xhr) { 1094 if (xhr.response && 'object' === typeof xhr.response) { 1095 return xhr.response; 1096 } 1097 1098 const responseText = 'string' === typeof xhr.responseText 1099 ? xhr.responseText 1100 : xhr.response; 1101 1102 if ('string' !== typeof responseText || !responseText) { 1103 return null; 1104 } 1105 1106 try { 1107 return JSON.parse(responseText); 1108 } catch (error) { 1109 return null; 1110 } 1111 } 1112 1113 function installLimitedOrderObserver() { 1114 const receiptOrderId = getReceiptOrderId(); 1115 1116 if (!receiptOrderId || !document.getElementById('c7-content') || !window.XMLHttpRequest) { 1117 return; 1118 } 1119 1120 const requestMatches = Symbol('foleyC7LimitedOrderRequest'); 1121 const xhrPrototype = window.XMLHttpRequest.prototype; 1122 const originalOpen = xhrPrototype.open; 1123 const originalSend = xhrPrototype.send; 1124 let restored = false; 1125 1126 function restore() { 1127 if (restored) { 1128 return; 1129 } 1130 1131 restored = true; 1132 1133 if (xhrPrototype.open === interceptedOpen) { 1134 xhrPrototype.open = originalOpen; 1135 } 1136 1137 if (xhrPrototype.send === interceptedSend) { 1138 xhrPrototype.send = originalSend; 1139 } 1140 } 1141 1142 function interceptedOpen(method, url) { 1143 if (isMatchingLimitedOrderRequest(method, url, receiptOrderId)) { 1144 this[requestMatches] = true; 1145 } else if (this[requestMatches]) { 1146 delete this[requestMatches]; 1147 } 1148 1149 return originalOpen.apply(this, arguments); 1150 } 1151 1152 function interceptedSend() { 1153 if (this[requestMatches]) { 1154 this.addEventListener('load', () => { 1155 if (this.status < 200 || this.status >= 300) { 1156 return; 1157 } 1158 1159 const order = parseXhrJson(this); 1160 restore(); 1161 1162 if (order) { 1163 emitPurchase(order); 1164 } 1165 }, { once: true }); 1166 } 1167 1168 return originalSend.apply(this, arguments); 1169 } 1170 1171 xhrPrototype.open = interceptedOpen; 1172 xhrPrototype.send = interceptedSend; 1173 window.setTimeout(restore, 120000); 1174 } 1175 1176 installDataLayerNormalizer(); 1177 registerRemoveFromCartCapture(); 1178 installLimitedOrderObserver(); 1179 1180 return { 1181 emitAddToCart, 1182 emitCustomPdpViewItem, 1183 emitViewItemList, 1184 getDataLayerLength, 1185 getTenant, 1186 registerBeginCheckout 1187 }; 1188})(); 1189 1190window.foleyC7UiGa4Analytics = foleyC7UiGa4Analytics; 1191 1192 1193/* hide cents in C7 price if == ".00" */ 1194document.querySelectorAll('.c7-buy-product').forEach(item => { 1195 new MutationObserver((mutationsList, observer) => { 1196 let priceWrapper = item.querySelector('.c7-product__add-to-cart__price'); 1197 if (priceWrapper) { 1198 observer.disconnect(); 1199 let priceEls = Array.from(priceWrapper.children).filter(el => !el.matches('.c7-sr-only,.c7-product__add-to-cart__price__variant')); 1200 priceEls.forEach(el => { 1201 let centsEl = el.querySelector('span:last-child'); 1202 if (centsEl && centsEl.innerText == '.00') { 1203 centsEl.style.display = "none"; 1204 } 1205 }); 1206 } 1207 }).observe(item, { childList: true, subtree: true }); 1208}); 1209 1210 1211 1212 1213 1214 1215document.addEventListener("DOMContentLoaded", () => { 1216 /* 1217 email:"[email protected]" 1218 firstName:"Ron" 1219 isClubMember:true 1220 lastName:"Marcelle" 1221 */ 1222 let jwt = null; 1223 const API_BASE = 'https://api.commerce7.com/v2'; 1224 1225 foleyC7UiGa4Analytics.registerBeginCheckout(); 1226 foleyC7UiGa4Analytics.emitCustomPdpViewItem(); 1227 1228 const getCookie = async (name) => {
1229 if ('cookieStore' in window) { 1230 return (await cookieStore.get(name))?.value; 1231 } 1232 1233 const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]+)`)); 1234 return match ? match[1] : null; 1235 }; 1236 1237 const setCookie = async (name, value, days = 365) => { 1238 if ('cookieStore' in window) { 1239 await cookieStore.set({ 1240 name, 1241 value, 1242 expires: Date.now() + days * 24 * 60 * 60 * 1000, 1243 path: '/', 1244 sameSite: 'lax', 1245 secure: location.protocol === 'https:' 1246 }); 1247 return; 1248 } 1249 1250 document.cookie = [ 1251 `${name}=${encodeURIComponent(value)}`, 1252 `expires=${new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString()}`, 1253 'path=/', 1254 'SameSite=Lax', 1255 location.protocol === 'https:' ? 'Secure' : '' 1256 ].filter(Boolean).join('; '); 1257 }; 1258 1259 async function getC7JWT(force = false) { 1260 if (jwt && !force) { 1261 return jwt; 1262 } 1263 1264 const token = await getCookie("customerToken"); 1265 jwt = parseC7JWT(token); 1266 return jwt; 1267 } 1268 1269 function parseC7JWT(token) { 1270 if (!token) return; 1271 const base64Url = token.split('.')[1]; 1272 const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); 1273 const jsonPayload = decodeURIComponent( 1274 window.atob(base64) 1275 .split('') 1276 .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) 1277 .join('') 1278 ); 1279 return JSON.parse(jsonPayload); 1280 } 1281 1282 async function updateC7JWT() { 1283 jwt = await getC7JWT(true); 1284 return jwt; 1285 } 1286 1287 if ('cookieStore' in window) { 1288 cookieStore.addEventListener('change', (e) => { 1289 if (e.changed.length && e.changed[0].name === 'customerToken') { 1290 updateC7JWT().then(() => { 1291 document.querySelectorAll('c7-user-menu').forEach(el => el.buildMenu()); 1292 }); 1293 } 1294 }); 1295 } 1296 1297 function getTenant() { 1298 return foleyC7UiGa4Analytics.getTenant(); 1299 } 1300 1301 function getHeaders() { 1302 const tenant = getTenant(); 1303 1304 if (!tenant) { 1305 throw new Error('Could not find Commerce7 tenant.'); 1306 } 1307 1308 return { 1309 'Content-Type': 'application/json', 1310 'Accept': 'application/json', 1311 'Pragma': 'no-cache', 1312 'tenant': tenant, 1313 'tenantId': tenant 1314 }; 1315 } 1316 1317 async function createCart() { 1318 const response = await fetch(`${API_BASE}/cart`, { 1319 method: 'POST', 1320 credentials: 'omit', 1321 mode: 'cors', 1322 headers: getHeaders(), 1323 body: JSON.stringify({ 1324 channel: 'Web', 1325 connectionInformation: { 1326 userAgent: navigator.userAgent 1327 } 1328 }) 1329 }); 1330 1331 if (!response.ok) { 1332 const text = await response.text().catch(() => ''); 1333 throw new Error(`Create cart failed: ${response.status} ${text}`); 1334 } 1335 1336 const cart = await response.json(); 1337 1338 if (!cart?.id) { 1339 throw new Error('Create cart failed: missing cart id'); 1340 } 1341 1342 await setCookie('cartId', cart.id); 1343 return cart.id; 1344 } 1345 1346 async function ensureCartId() { 1347 return await getCookie('cartId') || await createCart(); 1348 } 1349 1350 async function addToCart({ cartId, sku, quantity = 1 }) { 1351 const response = await fetch(`${API_BASE}/cart/${encodeURIComponent(cartId)}/item`, { 1352 method: 'POST', 1353 credentials: 'omit', 1354 mode: 'cors', 1355 headers: getHeaders(), 1356 body: JSON.stringify({ sku, quantity }) 1357 }); 1358 1359 if (!response.ok) { 1360 const text = await response.text().catch(() => ''); 1361 throw new Error(`Add to cart failed: ${response.status} ${text}`); 1362 } 1363 1364 return response.json(); 1365 } 1366 1367 1368 1369 1370 1371 /* CUSTOM ELEMENTS */ 1372 customElements.define('c7-user-menu', class C7UserMenu extends HTMLElement { 1373 constructor() { 1374 super(); 1375 1376 this.logo = this.innerHTML; 1377 this.innerHTML = ''; 1378 this._scaffoldMenu(); 1379 1380 /*cookieStore.addEventListener('change', (e) => { 1381 if (e.changed.length && e.changed[0].name === 'customerToken') { 1382 this._updateToken(); 1383 } 1384 });*/ 1385 } 1386 1387 connectedCallback() { 1388 getC7JWT().then(x => { 1389 this.jwt = x; 1390 this.buildMenu(); 1391 }); 1392 } 1393 1394 buildMenu() { 1395 if (this.jwt) { 1396 this.namePlate.textContent = `Hi ${this.jwt.firstName}`; 1397 this.menuWrapper.innerHTML = this._menuLoggedInContent(); 1398 } else { 1399 this.namePlate.textContent = ''; 1400 this.menuWrapper.innerHTML = this._menuLoggedOutContent(); 1401 } 1402 } 1403 1404 _scaffoldMenu() { 1405 let wrapper = document.createElement('div'); 1406 let closeButton = document.createElement('button'); 1407 let logo = document.createElement('span'); 1408 this.menuWrapper = document.createElement('div'); 1409 this.dialog = document.createElement('dialog'); 1410 this.namePlate = document.createElement('span'); 1411 1412 this.dialog.id = 'foley-c7-account-dialog'; 1413 this.dialog.classList.add('slide-in'); 1414 1415 this.dialog.addEventListener("click", ({target}) => { 1416 if (target === this.dialog) { 1417 this.dialog.close(); 1418 } 1419 }); 1420 1421 wrapper.classList.add('wrapper'); 1422 1423 closeButton.setAttribute('commandfor', 'foley-c7-account-dialog'); 1424 closeButton.setAttribute('command', 'close'); 1425 closeButton.textContent = 'Close'; 1426 1427 this.namePlate.classList.add('name-plate'); 1428 1429 logo.classList.add('logo'); 1430 logo.innerHTML = this.logo; 1431 1432 this.menuWrapper.classList.add('menu-wrapper','dialog-main-menu'); 1433 1434 this.dialog.append(wrapper); 1435 wrapper.append(closeButton); 1436 wrapper.append(this.namePlate); 1437 wrapper.append(logo); 1438 wrapper.append(this.menuWrapper); 1439 this.append(this.dialog); 1440 } 1441 1442 _menuLoggedInContent() { 1443 return `<ul> 1444 <li><a href="/profile/">Dashboard</a></li> 1445 <li><a href="/profile/club-membership">Club Memberships</a></li> 1446 <li><a href="/profile/order-history">Order History</a></li> 1447 <li><a href="/profile/reservation">Reservations</a></li> 1448 <li><a href="/profile/account">Account Details</a></li> 1449 <li><a href="/profile/logout">Log out</a></li> 1450 </ul>`; 1451 } 1452 1453 _menuLoggedOutContent() { 1454 return '<ul><li><a href="/profile/">Login</a></li></ul>' 1455 } 1456 1457 /*_updateToken() { 1458 getC7JWT().then(x => { 1459 this.jwt = parseC7JWT(x); 1460 this.buildMenu(); 1461 }); 1462 }*/ 1463 1464 /*async _getJwt() { 1465 let x = await cookieStore.get("customerToken"); 1466 return x?.value; 1467 } 1468 1469 _parseJwt(token) { 1470 if (!token) return; 1471 var base64Url = token.split('.')[1]; 1472 var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); 1473 var jsonPayload = decodeURIComponent(window.atob(base64).split('').map((c) =>
1473 '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')); 1474 return JSON.parse(jsonPayload); 1475 }*/ 1476 1477 }); 1478 1479 document.addEventListener('click', async function (event) { 1480 const button = event.target.closest('.js-c7-add-to-cart'); 1481 if (!button) return; 1482 1483 event.preventDefault(); 1484 1485 const sku = button.dataset.sku; 1486 const quantity = Math.max(1, Number(button.form?.elements?.qty?.value) || Number(button.dataset.quantity) || 1); 1487 1488 if (!sku) { 1489 alert('Missing SKU.'); 1490 return; 1491 } 1492 1493 button.disabled = true; 1494 1495 try { 1496 const dataLayerStartIndex = foleyC7UiGa4Analytics.getDataLayerLength(); 1497 const cartId = await ensureCartId(); 1498 const cart = await addToCart({ cartId, sku, quantity }); 1499 1500 foleyC7UiGa4Analytics.emitAddToCart(cart, sku, quantity, dataLayerStartIndex); 1501 1502 window.c7action?.reloadCart?.(); 1503 window.c7action?.showSideCart?.(); 1504 } catch (err) { 1505 console.error(err); 1506 alert(err.message || 'Could not add item to cart.'); 1507 } finally { 1508 button.disabled = false; 1509 } 1510 }); 1511 1512 1513 1514 let membershipLink = document.querySelector('article.full.type-wine .membership-link'); 1515 if (membershipLink) { 1516 getC7JWT().then(data => { 1517 if (!data || data.isClubMember === false) { 1518 membershipLink.style.display = "initial"; 1519 } 1520 }); 1521 } 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 customElements.define('accordion-tabs', class AccordionTabs extends HTMLElement { 1532 #allowMultiple = false; 1533 #accordionID = null; 1534 1535 constructor() { 1536 super(); 1537 this.#allowMultiple = this.allowMultiple(); 1538 this.#accordionID = crypto.randomUUID(); 1539 this.breakpoint = this.hasAttribute('breakpoint') ? this.getAttribute('breakpoint') : 700; 1540 let originalItems = Array.from(this.children).filter(item => item.matches('[title]')); 1541 if (this.breakpoint == 0) { 1542 this.breakpoint = Infinity; 1543 } 1544 1545 this.labels = originalItems.map((item,i) => { 1546 let button = document.createElement('button'); 1547 button.innerHTML = item.title; 1548 button.addEventListener("click", () => { 1549 this.items[i].open = true; 1550 this.labels.filter(el =>
1550 el.classList.contains("active")).forEach(el => el.classList.remove("active")); 1551 button.parentNode.querySelector('.active')?.classList.remove("active"); 1552 button.classList.add("active"); 1553 }); 1554 if (i === 0) { 1555 button.classList.add("active"); 1556 } 1557 return button; 1558 }); 1559 this.items = originalItems.map(item => { 1560 let details = document.createElement('details'); 1561 if (!this.#allowMultiple) { 1562 details.name = this.#accordionID; 1563 } 1564 1565 details.innerHTML = `<summary>${item.title}</summary>${item.innerHTML}`; 1566 return details; 1567 }); 1568 let labelWrap = document.createElement('div'); 1569 labelWrap.className = "label-wrap"; 1570 labelWrap.append(...this.labels); 1571 1572 let accordionWrap = document.createElement('div'); 1573 accordionWrap.className = "accordion-wrap"; 1574 accordionWrap.append(...this.items); 1575 1576 this.innerHTML = ''; 1577 this.append(labelWrap, accordionWrap); 1578 } 1579 1580 connectedCallback() { 1581 //this.getBoundingClientRect().width 1582 new ResizeObserver((e) => { 1583 if (e[0].borderBoxSize[0].inlineSize > this.breakpoint) { 1584 this.classList.add("element-large"); 1585 this.classList.remove("element-small"); 1586 if (!this.querySelector('details[open]')) { 1587 this.items[0].open = true; 1588 } 1589 } else { 1590 this.classList.add("element-small"); 1591 this.classList.remove("element-large"); 1592 } 1593 }).observe(this); 1594 } 1595 1596 static get observedAttributes() { 1597 return ['allow-multiple']; 1598 } 1599 1600 attributeChangedCallback(name, oldValue, newValue) { 1601 if (name === "allow-multiple") { 1602 this.#allowMultiple = this.allowMultiple(); 1603 1604 if (this.#allowMultiple) { 1605 this.items.forEach(item => item.removeAttribute("name")); 1606 } else { 1607 this.items.forEach(item => item.name = this.#accordionID); 1608 } 1609 } 1610 } 1611 1612 allowMultiple() { 1613 return this.hasAttribute("allow-multiple") 1614 } 1615 }); 1616 1617 1618 /* header */ 1619 document.querySelector('#foley-c7-account-icon')?.addEventListener('click', (evt) => { 1620 evt.preventDefault(); 1621 /*getC7JWT().then(jwt => { 1622 if (jwt) { 1623 let drawer = document.querySelector(`#${evt.target.getAttribute('commandfor')}`); 1624 drawer.showModal(); 1625 } else { 1626 window.open("/profile/", "_self"); 1627 } 1628 });*/ 1629 window.open("/profile/", "_self"); 1630 }); 1631 1632 1633}); 1634 1635 1636 1637 1638/************
1639**** PLP **** 1640************/ 1641addEventListener("DOMContentLoaded", () => { 1642 /*document.querySelectorAll('.foley-c7-ui-wine-listing__control:is([data-control-type="checkbox"], [data-control-type="radio"]').forEach(elem => {*/ 1643 document.querySelectorAll('.foley-c7-ui-wine-listing__control').forEach(elem => { 1644 let label = elem.querySelector('label'); 1645 if (label.children.length===0) { 1646 elem.querySelector('label').addEventListener("click", () => elem.classList.toggle("active")); 1647 1648 elem.addEventListener("click", (e) => { 1649 if (e.target.matches('input[type="radio"]')) { 1650 elem.classList.remove("active"); 1651 } 1652 }); 1653 } 1654 }); 1655 1656 let wineListingControls = document.querySelector('.foley-c7-ui-wine-listing__controls'); 1657 document.querySelector('#foley-c7-ui-wine-listing__controls-toggle')?.addEventListener("click", () => { 1658 if (matchMedia('max-width:1200px')) { 1659 wineListingControls?.classList.toggle('active'); 1660 } 1661 }); 1662 1663 document.querySelectorAll('.foley-c7-ui-wine-listing__controls-close').forEach(button => { 1664 let toggle = button.closest('.foley-c7-ui-wine-listing__controls'); 1665 button.addEventListener("click", () => { 1666 toggle.classList.remove('active'); 1667 }); 1668 }); 1669}); 1670 1671/************
1672**** PLP **** 1673************/ 1674addEventListener("DOMContentLoaded", () => { 1675 document.querySelector('#back-top-shop')?.addEventListener("click", e => { 1676 if (document.referrer.indexOf('shop')>-1) { 1677 e.preventDefault(); 1678 history.back(); 1679 } 1680 }); 1681}); 1682 1683 1684/************************ 1685**** MEMBERSHIP PAGE **** 1686************************/ 1687addEventListener("DOMContentLoaded", () => { 1688 /*document.querySelectorAll('.club-membership-type-picker').forEach(el => { 1689 let parent = el.closest('.kb-row-layout-wrap'); 1690 let buttons = Array.from(parent.querySelectorAll('.c7-club-join-button')); 1691 let toggleButtons = () => { 1692 console.log(el.value); 1693 buttons.forEach(button => button.style.display = button.getAttribute('data-club-slug') === el.value ? "block" : "none"); 1694 } 1695 el.addEventListener("change", () => toggleButtons()); 1696 toggleButtons(); 1697 });*/ 1698 1699 document.querySelectorAll('.club-picker').forEach(el => { 1700 let buttons = Array.from(el.querySelectorAll('.c7-club-join-button')); 1701 let picker = el.querySelector('select'); 1702 let flexType = el.classList.contains('inline') ? "inline-flex" : "flex"; 1703 let toggleButtons = () => { 1704 if (picker.value) { 1705 buttons.forEach(button => button.style.display = button.getAttribute('data-club-slug') === picker.value ? flexType : "none"); 1706 el.classList.remove('disabled'); 1707 } else { 1708 el.classList.add('disabled'); 1709 buttons.forEach((button,i) => button.style.display = i===0 ? flexType : "none"); 1710 } 1711 } 1712 picker.addEventListener("change", () => toggleButtons()); 1713 toggleButtons(); 1714 }); 1715}); 1716 1717 1718 1719 1720 1721/************************ 1722**** Checkout Teaser **** 1723************************/ 1724 1725/*addEventListener("DOMContentLoaded", () => { 1726 const c7Content = document.querySelector('#c7-content'); 1727 const checkoutTeaser = document.querySelector('.foley-c7-ui-checkout-teaser'); 1728 1729 function debounce(func, delay = 100) { 1730 let timer; 1731 return (...args) => { 1732 clearTimeout(timer); 1733 timer = setTimeout(() => func(...args), delay); 1734 }; 1735 } 1736 1737 if (c7Content && checkoutTeaser) { 1738 setTimeout(() => { 1739 new ResizeObserver((entries) => { 1740 c7Content.style.paddingTop = `${entries[0].borderBoxSize[0].blockSize}px`; 1741 checkoutTeaser.classList.add("loaded"); 1742 }).observe(checkoutTeaser); 1743 }, 500); 1744 1745 } 1746});*/ 1747; 1748!function(){var t={831:function(){const t=document.querySelectorAll(".js-accordion-block:not(.js-accordion-disable-auto-open)");function e(t){const e=t.closest(".js-accordion-block").querySelectorAll(".js-accordion-item"),n=t.getAttribute("aria-expanded"),r=t.nextElementSibling;"false"===n?(Array.from(e).forEach((t=>{const e=t.querySelector(".js-accordion-toggle");e.setAttribute("aria-expanded","false"),e.nextElementSibling.setAttribute("aria-hidden","true")})),t.setAttribute("aria-expanded","true"),r.setAttribute("aria-hidden","false")):(t.setAttribute("aria-expanded","false"),r.setAttribute("aria-hidden","true"))}function n(t){let n=t.target;for(;n;){if("BUTTON"===n.nodeName&&/js-accordion-toggle/.test(n.className)){e(n);break}n=n.parentNode}}Array.from(t).forEach((t=>{const e=t.querySelector(".js-accordion-toggle");e.setAttribute("aria-expanded","true"),e.nextElementSibling.setAttribute("aria-hidden","false")})),document.addEventListener?document.addEventListener("click",n,!1):document.attachEvent&&document.attachEvent("onclick",n)},417:function(){"use strict";class t{constructor(t){this.buttonEl=t,this.parentEl=this.buttonEl.parentElement,this.contentEl=this.buttonEl.nextElementSibling,this.open="true"===this.buttonEl.getAttribute("aria-expanded"),this.buttonEl.addEventListener("click",this.onButtonClick.bind(this))}onButtonClick(){this.toggle(!this.open)}toggle(t){t!==this.open&&(this.open=t,this.buttonEl.setAttribute("aria-expanded",`${t}`),t?(this.contentEl.removeAttribute("hidden"),this.parentEl.classList.add("open")):(this.contentEl.setAttribute("hidden",""),this.parentEl.classList.remove("open")))}open(){this.toggle(!0)}close(){this.toggle(!1)}}function e(){document.querySelectorAll(".js-event-accordion-trigger").forEach((e=>{window.innerWidth<782?new t(e):(e.parentNode.classList.add("open"),e.setAttribute("aria-expanded","true"),e.nextElementSibling.removeAttribute("hidden"))}))}document.addEventListener("DOMContentLoaded",e),window.addEventListener("resize",e)},769:function(){function t(t){const e=function(t){const e={classes:{slide:"js-carousel__slide"},config:{type:"carousel",gap:0}};switch(t){case"carousel-block":return{...e,classes:{...e.classes},config:{...e.config,animationDuration:0,touchRatio:0}};case"hero-img-carousel":return{...e,classes:{...e.classes},config:{...e.config}};case"footer-gallery":return{...e,classes:{...e.classes},config:{...e.config,perView:5,perTouch:1,autoplay:3e3,animationTimingFunc:"ease",animationDuration:1e3,gap:10,breakpoints:{1024:{perView:4},768:{perView:3},600:{perView:2}}}};case"logo-carousel":return{...e,classes:{...e.classes},config:{...e.config,perView:6,perTouch:1,autoplay:3e3,animationTimingFunc:"ease",animationDuration:1e3,gap:40,breakpoints:{1024:{perView:4},768:{perView:2},600:{perView:1}}}};default:return{}}}(t.dataset.carousel_config);new Glide(t,e.config).mount()}function e(){const e=document.querySelectorAll(".js-glide-carousel");if(e.length)for(let n=0,r=e.length;n<r;n++)t(e[n])}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()},432:function(){const t=document.querySelectorAll([".menu-item-has-children a",".menu-item-has-children svg"]),e=document.querySelectorAll([".mega-menu-content a",".mega-menu-content svg"]),n=document.querySelectorAll('.menu-item a[href="#"]');
1748n&&Array.from(n).forEach((t=>{t.addEventListener("click",(t=>{t.preventDefault()}))})),t&&t.forEach((t=>{t.addEventListener("click",(function(){if(window.innerWidth>"1200")return;const t=this.parentNode;t.classList.contains("open")?(t.classList.remove("open"),t.setAttribute("aria-expanded","false")):(t.classList.add("open"),t.setAttribute("aria-expanded","true"))}),!1)})),e&&e.forEach((t=>{t.addEventListener("click",(function(){const t=this.parentNode,e=document.querySelector(".menu-item.open");t.classList.contains("open")?(t.classList.remove("open"),t.setAttribute("aria-expanded","false")):(t.classList.add("open"),t.setAttribute("aria-expanded","true"),e&&window.innerWidth>"1200"&&(e.classList.remove("open"),e.setAttribute("aria-expanded","false")))}),!1)}))},473:function(){!function(){const t=function(t){const e=document.body,n=document.getElementById(t+"-open-menu"),r=document.getElementById(t+"-close-menu"),i=document.querySelectorAll(".menu-item-has-children");let o=!1;function a(){i&&i.forEach((t=>{t.setAttribute("aria-expanded","false"),t.classList.remove("open")}))}function s(){e.classList.remove(t+"-navigation-open"),e.classList.remove("lock-scrolling"),a(),o=!1}n&&r&&(n.onclick=function(){e.classList.add(t+"-navigation-open"),e.classList.add("lock-scrolling"),o=!0,r.focus()},r.onclick=function(){s(),n.focus()}),window.onresize=function(){"1199"<window.innerWidth&&!0===o&&(s(),a())},document.addEventListener("keydown",(function(r){if(!e.classList.contains(t+"-navigation-open"))return;let i=document.getElementById("masthead").querySelectorAll("a");i=Array.prototype.slice.call(i);const o=9===r.keyCode,c=r.shiftKey,u=27===r.keyCode,l=document.activeElement,d=i[i.length-1],f=i[0];u&&(r.preventDefault(),e.classList.remove(t+"-navigation-open","lock-scrolling"),n.focus(),s(),a()),!c&&o&&d===l&&(r.preventDefault(),f.focus()),c&&o&&f===l&&(r.preventDefault(),d.focus()),o&&f===d&&r.preventDefault()}))};window.addEventListener("load",(function(){new t("primary")}))}()}},e={};function n(r){var i=e[r];if(void 0!==i)return i.exports;var o=e[r]={exports:{}};return t[r](o,o.exports,n),o.exports}n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),function(){"use strict";function t(){return(t=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t}).apply(this,arguments)}function e(t){var e={exports:{}};return t(e,e.exports),e.exports}n(831),n(417),"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:"undefined"!=typeof self&&self;var r=e((function(t,e){t.exports=function(){var t="millisecond",e="second",n="minute",r="hour",i="day",o="week",a="month",s="quarter",c="year",u="date",l=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,d=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,f={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},h=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},p={s:h,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+h(r,2,"0")+":"+h(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,a),o=n-i<0,s=e.clone().add(r+(o?-1:1),a);return+(-(r+(n-i)/(o?i-s:s-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(l){return{M:a,y:c,w:o,d:i,D:u,h:r,m:n,s:e,ms:t,Q:s}[l]||String(l||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},m="en",g={};g[m]=f;var y=function(t){return t instanceof w},$=function(t,e,n){var r;if(!t)return m;if("string"==typeof t)g[t]&&(r=t),e&&(g[t]=e,r=t);else{var i=t.name;g[i]=t,r=i}return!n&&r&&(m=r),r||!n&&m},v=function(t,e){if(y(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new w(n)},b=p;b.l=$,b.i=y,b.w=function(t,e){return v(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var w=function(){function f(t){this.$L=$(t.locale,null,!0),this.parse(t)}var h=f.prototype;return h.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match(l);if(r){var i=r[2]-1||0,o=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},h.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},h.$utils=function(){return b},h.isValid=function(){return!("Invalid Date"===this.$d.toString())},h.isSame=function(t,e){var n=v(t);return this.startOf(e)<=n&&n<=this.endOf(e)},h.isAfter=function(t,e){return v(t)<this.startOf(e)},h.isBefore=function(t,e){return this.endOf(e)<v(t)},h.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},h.unix=function(){return Math.floor(this.valueOf()/1e3)},h.valueOf=function(){return this.$d.getTime()},h.startOf=function(t,s){var l=this,d=!!b.u(s)||s,f=b.p(t),h=function(t,e){var n=b.w(l.$u?Date.UTC(l.$y,e,t):new Date(l.$y,e,t),l);return d?n:n.endOf(i)},p=function(t,e){return b.w(l.toDate()[t].apply(l.toDate("s"),(d?[0,0,0,0]:[23,59,59,999]).slice(e)),l)},m=this.$W,g=this.$M,y=this.$D,$="set"+(this.$u?"UTC":"");switch(f){case c:return d?h(1,0):h(31,11);case a:return d?h(1,g):h(0,g+1);case o:var v=this.$locale().weekStart||0,w=(m<v?m+7:m)-v;return h(d?y-w:y+(6-w),g);case i:case u:return p($+"Hours",0);case r:return p($+"Minutes",1);case n:return p($+"Seconds",2);case e:return p($+"Milliseconds",3);default:return this.clone()}},h.endOf=function(t){return this.startOf(t,!1)},h.$set=function(o,s){var l,d=b.p(o),f="set"+(this.$u?"UTC":""),h=(l={},l[i]=f+"Date",l[u]=f+"Date",l[a]=f+"Month",l[c]=f+"FullYear",l[r]=f+"Hours",l[n]=f+"Minutes",l[e]=f+"Seconds",l[t]=f+"Milliseconds",l)[d],p=d===i?this.$D+(s-this.$W):s;if(d===a||d===c){var m=this.clone().set(u,1);
1748m.$d[h](p),m.init(),this.$d=m.set(u,Math.min(this.$D,m.daysInMonth())).$d}else h&&this.$d[h](p);return this.init(),this},h.set=function(t,e){return this.clone().$set(t,e)},h.get=function(t){return this[b.p(t)]()},h.add=function(t,s){var u,l=this;t=Number(t);var d=b.p(s),f=function(e){var n=v(l);return b.w(n.date(n.date()+Math.round(e*t)),l)};if(d===a)return this.set(a,this.$M+t);if(d===c)return this.set(c,this.$y+t);if(d===i)return f(1);if(d===o)return f(7);var h=(u={},u[n]=6e4,u[r]=36e5,u[e]=1e3,u)[d]||1,p=this.$d.getTime()+t*h;return b.w(p,this)},h.subtract=function(t,e){return this.add(-1*t,e)},h.format=function(t){var e=this;if(!this.isValid())return"Invalid Date";var n=t||"YYYY-MM-DDTHH:mm:ssZ",r=b.z(this),i=this.$locale(),o=this.$H,a=this.$m,s=this.$M,c=i.weekdays,u=i.months,l=function(t,r,i,o){return t&&(t[r]||t(e,n))||i[r].substr(0,o)},f=function(t){return b.s(o%12||12,t,"0")},h=i.meridiem||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r},p={YY:String(this.$y).slice(-2),YYYY:this.$y,M:s+1,MM:b.s(s+1,2,"0"),MMM:l(i.monthsShort,s,u,3),MMMM:l(u,s),D:this.$D,DD:b.s(this.$D,2,"0"),d:String(this.$W),dd:l(i.weekdaysMin,this.$W,c,2),ddd:l(i.weekdaysShort,this.$W,c,3),dddd:c[this.$W],H:String(o),HH:b.s(o,2,"0"),h:f(1),hh:f(2),a:h(o,a,!0),A:h(o,a,!1),m:String(a),mm:b.s(a,2,"0"),s:String(this.$s),ss:b.s(this.$s,2,"0"),SSS:b.s(this.$ms,3,"0"),Z:r};return n.replace(d,(function(t,e){return e||p[t]||r.replace(":","")}))},h.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},h.diff=function(t,u,l){var d,f=b.p(u),h=v(t),p=6e4*(h.utcOffset()-this.utcOffset()),m=this-h,g=b.m(this,h);return g=(d={},d[c]=g/12,d[a]=g,d[s]=g/3,d[o]=(m-p)/6048e5,d[i]=(m-p)/864e5,d[r]=m/36e5,d[n]=m/6e4,d[e]=m/1e3,d)[f]||m,l?g:b.a(g)},h.daysInMonth=function(){return this.endOf(a).$D},h.$locale=function(){return g[this.$L]},h.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=$(t,e,!0);return r&&(n.$L=r),n},h.clone=function(){return b.w(this.$d,this)},h.toDate=function(){return new Date(this.valueOf())},h.toJSON=function(){return this.isValid()?this.toISOString():null},h.toISOString=function(){return this.$d.toISOString()},h.toString=function(){return this.$d.toUTCString()},f}(),D=w.prototype;return v.prototype=D,[["$ms",t],["$s",e],["$m",n],["$H",r],["$W",i],["$M",a],["$y",c],["$D",u]].forEach((function(t){D[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),v.extend=function(t,e){return t.$i||(t(e,w,v),t.$i=!0),v},v.locale=$,v.isDayjs=y,v.unix=function(t){return v(1e3*t)},v.en=g[m],v.Ls=g,v.p={},v}()})),i=e((function(t,e){t.exports=function(t,e,n){var r=e.prototype;n.utc=function(t){return new e({date:t,utc:!0,args:arguments})},r.utc=function(t){var e=n(this.toDate(),{locale:this.$L,utc:!0});return t?e.add(this.utcOffset(),"minute"):e},r.local=function(){return n(this.toDate(),{locale:this.$L,utc:!1})};var i=r.parse;r.parse=function(t){t.utc&&(this.$u=!0),this.$utils().u(t.$offset)||(this.$offset=t.$offset),i.call(this,t)};var o=r.init;r.init=function(){if(this.$u){var t=this.$d;this.$y=t.getUTCFullYear(),this.$M=t.getUTCMonth(),this.$D=t.getUTCDate(),this.$W=t.getUTCDay(),this.$H=t.getUTCHours(),this.$m=t.getUTCMinutes(),this.$s=t.getUTCSeconds(),this.$ms=t.getUTCMilliseconds()}else o.call(this)};var a=r.utcOffset;r.utcOffset=function(t,e){var n=this.$utils().u;if(n(t))return this.$u?0:n(this.$offset)?a.call(this):this.$offset;var r=Math.abs(t)<=16?60*t:t,i=this;if(e)return i.$offset=r,i.$u=0===t,i;if(0!==t){var o=this.$u?this.toDate().getTimezoneOffset():-1*this.utcOffset();(i=this.local().add(r+o,"minute")).$offset=r,i.$x.$localOffset=o}else i=this.utc();return i};var s=r.format;r.format=function(t){return s.call(this,t||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":""))},r.valueOf=function(){var t=this.$utils().u(this.$offset)?0:this.$offset+(this.$x.$localOffset||(new Date).getTimezoneOffset());return this.$d.valueOf()-6e4*t},r.isUTC=function(){return!!this.$u},r.toISOString=function(){return this.toDate().toISOString()},r.toString=function(){return this.toDate().toUTCString()};var c=r.toDate;r.toDate=function(t){return"s"===t&&this.$offset?n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate():c.call(this)};var u=r.diff;r.diff=function(t,e,r){if(t&&this.$u===t.$u)return u.call(this,t,e,r);var i=this.local(),o=n(t).local();return u.call(i,o,e,r)}}})),o=new RegExp("%[a-f0-9]{2}","gi"),a=new RegExp("(%[a-f0-9]{2})+","gi");function s(t,e){try{return decodeURIComponent(t.join(""))}catch(t){}if(1===t.length)return t;var n=t.slice(0,e=e||1),r=t.slice(e);return Array.prototype.concat.call([],s(n),s(r))}function c(t){try{return decodeURIComponent(t)}catch(r){for(var e=t.match(o),n=1;n<e.length;n++)e=(t=s(e,n).join("")).match(o);return t}}var u=function(t){if("string"!=typeof t)throw new TypeError("Expected `encodedURI` to be of type `string`, got `"+typeof t+"`");try{return t=t.replace(/\+/g," "),decodeURIComponent(t)}catch(e){return function(t){for(var e={"%FE%FF":"��","%FF%FE":"��"},n=a.exec(t);n;){try{e[n[0]]=decodeURIComponent(n[0])}catch(t){var r=c(n[0]);r!==n[0]&&(e[n[0]]=r)}n=a.exec(t)}e["%C2"]="�";for(var i=Object.keys(e),o=0;o<i.length;o++){var s=i[o];t=t.replace(new RegExp(s,"g"),e[s])}return t}(t)}},l=(t,e)=>{if("string"!=typeof t||"string"!=typeof e)throw new TypeError("Expected the arguments to be of type `string`");if(""===e)return[t];const n=t.indexOf(e);return-1===n?[t]:[t.slice(0,n),t.slice(n+e.length)]},d=function(t,e){for(var n={},r=Object.keys(t),i=Array.isArray(e),o=0;o<r.length;o++){var a=r[o],s=t[a];(i?-1!==e.indexOf(a):e(a,s,t))&&(n[a]=s)}return n},f=e((function(t,e){function n(t){if("string"!=typeof t||1!==t.length)throw new TypeError("arrayFormatSeparator must be single character string")}function r(t,e){return e.encode?e.strict?encodeURIComponent(t).replace(/[!'()*]/g,(t=>`%${t.charCodeAt(0).toString(16).toUpperCase()}`)):encodeURIComponent(t):t}function i(t,e){return e.decode?u(t):t}function o(t){return Array.isArray(t)?t.sort():"object"==typeof t?o(Object.keys(t)).sort(((t,e)=>Number(t)-Number(e))).map((e=>t[e])):t}function a(t){const e=t.indexOf("#");return-1!==e&&(t=t.slice(0,e)),t}function s(t){const e=(t=a(t)).indexOf("?");return-1===e?"":t.slice(e+1)}function c(t,e){return e.parseNumbers&&!Number.isNaN(Number(t))&&"string"==typeof t&&""!==t.trim()?t=Number(t):!e.parseBooleans||null===t||"true"!==t.toLowerCase()&&"false"!==t.toLowerCase()||(t="true"===t.toLowerCase()),t}function f(t,e){n((e=Object.assign({decode:!0,sort:!0,arrayFormat:"none",arrayFormatSeparator:",",parseNumbers:!1,parseBooleans:!1},e)).arrayFormatSeparator);const r=function(t){let e;switch(t.arrayFormat){case"index":return(t,n,r)=>{e=/\[(\d*)\]$/.exec(t),t=t.replace(/\[\d*\]$/,""),e?(void 0===r[t]&&(r[t]={}),r[t][e[1]]=n):r[t]=n};case"bracket":return(t,n,r)=>{e=/(\[\])$/.exec(t),r[t=t.replace(/\[\]$/,"")]=e?void 0!==r[t]?[].concat(r[t],n):[n]:n};case"comma":case"separator":return(e,n,r)=>{const o="string"==typeof n&&n.includes(t.arrayFormatSeparator),a="string"==typeof n&&!o&&i(n,t).includes(t.arrayFormatSeparator);n=a?i(n,t):n;const s=o||a?n.split(t.arrayFormatSeparator).map((e=>i(e,t))):null===n?n:i(n,t);r[e]=s};default:return(t,e,n)=>{n[t]=void 0!==n[t]?[].concat(n[t],e):e}}}(e),a=Object.create(null);if("string"!=typeof t)return a;
1748if(!(t=t.trim().replace(/^[?#&]/,"")))return a;for(const n of t.split("&")){if(""===n)continue;let[t,o]=l(e.decode?n.replace(/\+/g," "):n,"=");o=void 0===o?null:["comma","separator"].includes(e.arrayFormat)?o:i(o,e),r(i(t,e),o,a)}for(const t of Object.keys(a)){const n=a[t];if("object"==typeof n&&null!==n)for(const t of Object.keys(n))n[t]=c(n[t],e);else a[t]=c(n,e)}return!1===e.sort?a:(!0===e.sort?Object.keys(a).sort():Object.keys(a).sort(e.sort)).reduce(((t,e)=>{const n=a[e];return t[e]=Boolean(n)&&"object"==typeof n&&!Array.isArray(n)?o(n):n,t}),Object.create(null))}e.extract=s,e.parse=f,e.stringify=(t,e)=>{if(!t)return"";n((e=Object.assign({encode:!0,strict:!0,arrayFormat:"none",arrayFormatSeparator:","},e)).arrayFormatSeparator);const i=n=>e.skipNull&&null==t[n]||e.skipEmptyString&&""===t[n],o=function(t){switch(t.arrayFormat){case"index":return e=>(n,i)=>{const o=n.length;return void 0===i||t.skipNull&&null===i||t.skipEmptyString&&""===i?n:null===i?[...n,[r(e,t),"[",o,"]"].join("")]:[...n,[r(e,t),"[",r(o,t),"]=",r(i,t)].join("")]};case"bracket":return e=>(n,i)=>void 0===i||t.skipNull&&null===i||t.skipEmptyString&&""===i?n:null===i?[...n,[r(e,t),"[]"].join("")]:[...n,[r(e,t),"[]=",r(i,t)].join("")];case"comma":case"separator":return e=>(n,i)=>null==i||0===i.length?n:0===n.length?[[r(e,t),"=",r(i,t)].join("")]:[[n,r(i,t)].join(t.arrayFormatSeparator)];default:return e=>(n,i)=>void 0===i||t.skipNull&&null===i||t.skipEmptyString&&""===i?n:null===i?[...n,r(e,t)]:[...n,[r(e,t),"=",r(i,t)].join("")]}}(e),a={};for(const e of Object.keys(t))i(e)||(a[e]=t[e]);const s=Object.keys(a);return!1!==e.sort&&s.sort(e.sort),s.map((n=>{const i=t[n];return void 0===i?"":null===i?r(n,e):Array.isArray(i)?i.reduce(o(n),[]).join("&"):r(n,e)+"="+r(i,e)})).filter((t=>t.length>0)).join("&")},e.parseUrl=(t,e)=>{e=Object.assign({decode:!0},e);const[n,r]=l(t,"#");return Object.assign({url:n.split("?")[0]||"",query:f(s(t),e)},e&&e.parseFragmentIdentifier&&r?{fragmentIdentifier:i(r,e)}:{})},e.stringifyUrl=(t,n)=>{n=Object.assign({encode:!0,strict:!0},n);const i=a(t.url).split("?")[0]||"",o=e.extract(t.url),s=e.parse(o,{sort:!1}),c=Object.assign(s,t.query);let u=e.stringify(c,n);u&&(u=`?${u}`);let l=function(t){let e="";const n=t.indexOf("#");return-1!==n&&(e=t.slice(n)),e}(t.url);return t.fragmentIdentifier&&(l=`#${r(t.fragmentIdentifier,n)}`),`${i}${u}${l}`},e.pick=(t,n,r)=>{r=Object.assign({parseFragmentIdentifier:!0},r);const{url:i,query:o,fragmentIdentifier:a}=e.parseUrl(t,r);return e.stringifyUrl({url:i,query:d(o,n),fragmentIdentifier:a},r)},e.exclude=(t,n,r)=>{const i=Array.isArray(n)?t=>!n.includes(t):(t,e)=>!n(t,e);return e.pick(t,i,r)}}));const h={dateTimeLocal:"YYYY-MM-DD[T]HH:mm:ss",dateTimeUTC:"YYYYMMDD[T]HHmmss[Z]",allDay:"YYYYMMDD"};function p({startTime:t,endTime:e},n){const r=h[n];return{start:t.format(r),end:e.format(r)}}r.extend(i);const m=(e,n=!0)=>{const{start:i,end:o,duration:a}=e,s=function(t,e){if(null==t)return{};var n,r,i={},o=Object.keys(t);for(r=0;r<o.length;r++)e.indexOf(n=o[r])>=0||(i[n]=t[n]);return i}(e,["start","end","duration"]),c=n?r(i).utc():r(i),u=o?n?r(o).utc():r(o):(()=>{if(e.allDay)return c.add(1,"day");if(a&&2==a.length){const t=Number(a[0]);return c.add(t,a[1])}return n?r().utc():r()})();return t({},s,{startTime:c,endTime:u})},g=document.querySelector(".js-calendar-links"),y=document.querySelector(".js-calendar-link-google"),$=document.querySelector(".js-calendar-link-outlook"),v=document.querySelector(".js-calendar-link-ics");if(g){const t={title:g.getAttribute("data-title"),description:g.getAttribute("data-excerpt"),start:new Date(1e3*Number(g.getAttribute("data-start"))),end:new Date(1e3*Number(g.getAttribute("data-end"))),location:g.getAttribute("data-location")};y.addEventListener("click",(()=>{window.open((t=>{const e=m(t),{start:n,end:r}=p(e,e.allDay?"allDay":"dateTimeUTC"),i={action:"TEMPLATE",text:e.title,details:e.description,location:e.location,trp:e.busy,dates:n+"/"+r,recur:e.rRule?"RRULE:"+e.rRule:void 0};return e.guests&&e.guests.length&&(i.add=e.guests.join()),`https://calendar.google.com/calendar/render?${f.stringify(i)}`})(t))})),$.addEventListener("click",(()=>{window.open((t=>{const e=m(t,!1),{start:n,end:r}=p(e,"dateTimeLocal");return`https://outlook.live.com/calendar/0/deeplink/compose?${f.stringify({path:"/calendar/action/compose",rru:"addevent",startdt:n,enddt:r,subject:e.title,body:e.description,location:e.location,allday:e.allDay||!1})}`})(t))})),v.addEventListener("click",(()=>{window.open("data:text/calendar;charset=utf8;"+(t=>{const e=m(t),n=(e.description||"").replace(/,/gm,",").replace(/;/gm,";").replace(/\r\n/gm,"\n").replace(/\n/gm,"\\n").replace(/(\\n)[\s\t]+/gm,"\\n"),r=(e.location||"").replace(/,/gm,",").replace(/;/gm,";").replace(/\r\n/gm,"\n").replace(/\n/gm,"\\n").replace(/(\\n)[\s\t]+/gm,"\\n"),{start:i,end:o}=p(e,e.allDay?"allDay":"dateTimeUTC");let a="";return[{key:"BEGIN",value:"VCALENDAR"},{key:"VERSION",value:"2.0"},{key:"BEGIN",value:"VEVENT"},{key:"URL",value:e.url},{key:"DTSTART",value:i},{key:"DTEND",value:o},{key:"RRULE",value:e.rRule},{key:"SUMMARY",value:e.title},{key:"DESCRIPTION",value:n},{key:"LOCATION",value:r},{key:"ORGANIZER",value:e.organizer},{key:"END",value:"VEVENT"},{key:"END",value:"VCALENDAR"}
1748].forEach((t=>{if(t.value)if("ORGANIZER"==t.key){const e=t.value;a+=`${t.key};${encodeURIComponent(`CN=${e.name}:MAILTO:${e.email}\n`)}`}else a+=`${t.key}:${encodeURIComponent(`${t.value}\n`)}`})),`data:text/calendar;charset=utf8,${a}`})(t))}))}n(769),n(432),n(473)}()}();;
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.