1(function () { 2 var PRICE_STEP = 1; 3 var SEARCH_MIN_LENGTH = 3; 4 var SEARCH_DEBOUNCE_DELAY = 500; 5 6 function parseConfig(block) { 7 var script = block.querySelector('.js-foley-c7-ui-config'); 8 9 if (!script) { 10 return null; 11 } 12 13 try { 14 return JSON.parse(script.textContent || '{}'); 15 } catch (error) { 16 return null; 17 } 18 } 19 20 function splitValues(value) { 21 return String(value || '') 22 .split(',') 23 .map(function (item) { 24 return item.trim(); 25 }) 26 .filter(Boolean); 27 } 28 29 function parseNumber(value) { 30 var parsed = parseFloat(String(value || '').replace(/[^0-9.-]/g, '')); 31 32 return Number.isNaN(parsed) ? null : parsed; 33 } 34 35 function formatDecimal(value) { 36 return String(Math.round(value * 100) / 100); 37 } 38 39 function snapPriceValue(value) { 40 return Math.round(value / PRICE_STEP) * PRICE_STEP; 41 } 42 43 function formatPrice(value) { 44 var rounded = Math.round(value * 100) / 100; 45 46 return '$' + rounded.toLocaleString(undefined, { 47 minimumFractionDigits: Number.isInteger(rounded) ? 0 : 2, 48 maximumFractionDigits: 2 49 }); 50 } 51 52 function cleanSearchValue(value) { 53 return String(value || '').trim().slice(0, 100); 54 } 55 56 function normalizeSearchValue(value) { 57 var cleaned = cleanSearchValue(value); 58 59 return Array.from(cleaned).length >= SEARCH_MIN_LENGTH ? cleaned : ''; 60 } 61 62 function WineListingController(block) { 63 this.block = block; 64 this.config = parseConfig(block); 65 this.form = block.querySelector('.js-foley-c7-ui-form'); 66 this.searchInput = block.querySelector('.js-foley-c7-ui-search-input'); 67 this.itemsContainer = block.querySelector('.js-foley-c7-ui-items'); 68 this.navigationContainer = block.querySelector('.js-foley-c7-ui-navigation'); 69 this.statusNode = block.querySelector('.js-foley-c7-ui-status'); 70 this.restUrl = (window.FoleyC7UiWineProductListingView || {}).restUrl || ''; 71 this.searchDebounceTimer = null; 72 this.abortController = null; 73 this.requestSequence = 0; 74 } 75 76 WineListingController.prototype.getEmptyState = function (page) { 77 return { 78 filters: {}, 79 search: '', 80 priceRange: null, 81 ninetyPlus: false, 82 sort: '', 83 perPage: this.getDefaultPerPage(), 84 page: page || 1 85 }; 86 }; 87 88 WineListingController.prototype.readStateFromUrl = function () { 89 var stateConfig = this.config.stateConfig || {}; 90 var url = new URL(window.location.href); 91 var state = this.getEmptyState(1); 92 var page = parseInt(url.searchParams.get(stateConfig.page || '') || '1', 10); 93 var perPage = parseInt(url.searchParams.get(stateConfig.perPage || '') || '', 10); 94 var priceKeys = stateConfig.priceRange || {}; 95 var priceMin = priceKeys.min ? url.searchParams.get(priceKeys.min) : null; 96 var priceMax = priceKeys.max ? url.searchParams.get(priceKeys.max) : null; 97 var ninetyPlusValue = stateConfig.ninetyPlus ? url.searchParams.get(stateConfig.ninetyPlus) : ''; 98 var searchValue = stateConfig.search ? url.searchParams.get(stateConfig.search) : ''; 99 100 Object.keys(stateConfig.filters || {}).forEach(function (taxonomy) { 101 var queryKey = stateConfig.filters[taxonomy]; 102 var values = splitValues(queryKey ? url.searchParams.get(queryKey) : ''); 103 104 if (values.length) { 105 state.filters[taxonomy] = values; 106 } 107 }); 108 109 if (priceMin !== null || priceMax !== null) { 110 state.priceRange = { 111 min: priceMin !== null ? parseNumber(priceMin) : null, 112 max: priceMax !== null ? parseNumber(priceMax) : null 113 }; 114 } 115 116 state.ninetyPlus = ['1', 'true', 'yes', 'on'].indexOf(String(ninetyPlusValue || '').toLowerCase()) > -1; 117 state.search = normalizeSearchValue(searchValue); 118 state.sort = url.searchParams.get(stateConfig.sort || '') || ''; 119 state.perPage = Number.isNaN(perPage) || perPage < 1 ? this.getDefaultPerPage() : perPage; 120 state.page = Number.isNaN(page) || page < 1 ? 1 : page; 121 122 return state; 123 }; 124 125 WineListingController.prototype.init = function () { 126 if (!this.config || !this.restUrl) { 127 return; 128 } 129 130 this.syncControlsFromUrl(); 131 this.syncHiddenInputs(); 132 this.bindEvents(); 133 }; 134 135 WineListingController.prototype.bindEvents = function () { 136 var self = this; 137 138 if (this.form) { 139 this.form.addEventListener('submit', function (event) { 140 event.preventDefault(); 141 self.cancelSearchDebounce(); 142 143 if ( 144 event.submitter 145 && event.submitter.classList.contains('js-foley-c7-ui-search-submit') 146 && !normalizeSearchValue(self.searchInput ? self.searchInput.value : '') 147 ) { 148 return; 149 } 150 151 self.submit(self.buildState(1), false, true); 152 }); 153 154 this.form.addEventListener('change', function (event) { 155 if (event.target.closest('.js-foley-c7-ui-search-input')) { 156 return; 157 } 158
159 self.syncHiddenInputs(); 160 self.submit(self.buildState(1), false, true); 161 }); 162 163 var resetButton = this.form.querySelector('.js-foley-c7-ui-reset'); 164 165 if (resetButton) { 166 resetButton.addEventListener('click', function (event) { 167 event.preventDefault(); 168 self.resetControls(); 169 self.submit(self.buildState(1, true), false, true); 170 }); 171 } 172 } 173 174 if (this.searchInput) { 175 this.searchInput.addEventListener('input', function () { 176 self.handleSearchInput(); 177 }); 178 179 this.searchInput.addEventListener('keydown', function (event) { 180 if (event.key !== 'Enter') { 181 return; 182 } 183 184 event.preventDefault(); 185 self.submitSearchImmediately(); 186 }); 187 } 188 189 var perPageSelect = this.block.querySelector('.js-foley-c7-ui-per-page'); 190 if (perPageSelect) { 191 perPageSelect.addEventListener('change', function () { 192 self.submit(self.buildState(1), false, true); 193 }); 194 } 195 196 var sortSelect = this.block.querySelector('.js-foley-c7-ui-sort'); 197 if (sortSelect) { 198 sortSelect.addEventListener('change', function () { 199 self.submit(self.buildState(1), false, true); 200 }); 201 } 202 203 this.block.addEventListener('click', function (event) { 204 var paginationLink = event.target.closest('.foley-c7-ui-wine-listing__page'); 205 var loadMoreLink = event.target.closest('.js-foley-c7-ui-load-more'); 206 207 if (paginationLink) { 208 event.preventDefault(); 209 self.submit(self.buildState(parseInt(paginationLink.getAttribute('data-page') || '1', 10)), false, true); 210 } 211 212 if (loadMoreLink) { 213 event.preventDefault(); 214 self.submit(self.buildState(parseInt(loadMoreLink.getAttribute('data-page') || '1', 10)), true, true); 215 } 216 }); 217 218 this.block.addEventListener('pointerdown', function (event) { 219 var priceTarget = event.target.closest('.js-foley-c7-ui-price-handle, .js-foley-c7-ui-price-track'); 220 221 if (!priceTarget) { 222 return; 223 } 224 225 self.startPriceDrag(event, priceTarget); 226 }); 227 228 this.block.addEventListener('keydown', function (event) { 229 var handle = event.target.closest('.js-foley-c7-ui-price-handle'); 230 231 if (!handle) { 232 return; 233 } 234 235 self.handlePriceKeydown(event, handle); 236 }); 237 }; 238 239 WineListingController.prototype.cancelSearchDebounce = function () { 240 if (this.searchDebounceTimer) { 241 window.clearTimeout(this.searchDebounceTimer); 242 this.searchDebounceTimer = null; 243 } 244 }; 245 246 WineListingController.prototype.handleSearchInput = function () { 247 var self = this; 248 var rawValue = cleanSearchValue(this.searchInput ? this.searchInput.value : ''); 249 250 this.cancelSearchDebounce(); 251 252 if (rawValue === '') { 253 if (this.readStateFromUrl().search !== '') { 254 this.submit(this.buildState(1), false, true); 255 } 256 257 return; 258 } 259 260 if (!normalizeSearchValue(rawValue)) { 261 return; 262 } 263 264 this.searchDebounceTimer = window.setTimeout(function () { 265 self.searchDebounceTimer = null; 266 self.submit(self.buildState(1), false, true); 267 }, SEARCH_DEBOUNCE_DELAY); 268 }; 269 270 WineListingController.prototype.submitSearchImmediately = function () { 271 var search = normalizeSearchValue(this.searchInput ? this.searchInput.value : ''); 272 273 this.cancelSearchDebounce(); 274 275 if (!search) { 276 return; 277 } 278 279 this.submit(this.buildState(1), false, true); 280 }; 281 282 WineListingController.prototype.handlePopState = function () { 283 if (!this.config || !this.restUrl) { 284 return; 285 } 286 287 this.syncControlsFromUrl(); 288 this.syncHiddenInputs(); 289 this.submit(this.buildState(this.getPageFromUrl()), false, false); 290 }; 291 292 WineListingController.prototype.resetControls = function () { 293 if (!this.form) { 294 return; 295 } 296 297 this.block.querySelectorAll('.foley-c7-ui-wine-listing__control[data-taxonomy]').forEach(function (control) { 298 var type = control.getAttribute('data-control-type'); 299 300 if (type === 'checkbox' || type === 'radio') { 301 control.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function (input) { 302 input.checked = false; 303 }); 304 305 if (type === 'radio') { 306 var allRadio = control.querySelector('input[type="radio"][value=""]'); 307 308 if (allRadio) { 309 allRadio.checked = true; 310 } 311 } 312 } 313 314 if (type === 'select') { 315 var select = control.querySelector('select'); 316 if (select) { 317 select.value = ''; 318 } 319 } 320 321 if (type === 'multiselect') { 322 var multiSelect = control.querySelector('select'); 323 if (multiSelect) {
324 Array.from(multiSelect.options).forEach(function (option) { 325 option.selected = false; 326 }); 327 } 328 } 329 }); 330 331 this.block.querySelectorAll('.js-foley-c7-ui-price-control').forEach(function (control) { 332 this.setPriceRangeControl(control, this.getPriceBounds()); 333 }, this); 334 335 this.block.querySelectorAll('.js-foley-c7-ui-ninety-plus').forEach(function (input) { 336 input.checked = false; 337 }); 338 339 this.cancelSearchDebounce(); 340 if (this.searchInput) { 341 this.searchInput.value = ''; 342 } 343 344 var sortSelect = this.block.querySelector('.js-foley-c7-ui-sort'); 345 if (sortSelect) { 346 sortSelect.value = ''; 347 } 348 349 var perPageSelect = this.block.querySelector('.js-foley-c7-ui-per-page'); 350 if (perPageSelect) { 351 perPageSelect.value = String(this.getDefaultPerPage()); 352 } 353 354 this.syncHiddenInputs(); 355 }; 356 357 WineListingController.prototype.getPriceBounds = function () { 358 var bounds = this.config.priceBounds || {}; 359 var minimum = parseNumber(bounds.min); 360 var maximum = parseNumber(bounds.max); 361 362 if (minimum === null || maximum === null) { 363 return null; 364 } 365 366 return { 367 min: Math.min(minimum, maximum), 368 max: Math.max(minimum, maximum) 369 }; 370 }; 371 372 WineListingController.prototype.getDefaultPerPage = function () { 373 var attributes = this.config.attributes || {}; 374 var perPage = parseInt(attributes.itemsPerPage || '12', 10); 375 376 return Number.isNaN(perPage) || perPage < 1 ? 12 : perPage; 377 }; 378 379 WineListingController.prototype.normalizePriceRange = function (range) { 380 var bounds = this.getPriceBounds(); 381 382 if (!bounds) { 383 return null; 384 } 385 386 var minimum = range && range.min !== null && range.min !== undefined ? parseNumber(range.min) : null; 387 var maximum = range && range.max !== null && range.max !== undefined ? parseNumber(range.max) : null; 388 389 minimum = minimum === null ? bounds.min : minimum; 390 maximum = maximum === null ? bounds.max : maximum; 391 minimum = Math.min(Math.max(minimum, bounds.min), bounds.max); 392 maximum = Math.min(Math.max(maximum, bounds.min), bounds.max); 393 394 if (minimum > maximum) { 395 var nextMinimum = maximum; 396 maximum = minimum; 397 minimum = nextMinimum; 398 } 399 400 return { 401 min: Math.round(minimum * 100) / 100, 402 max: Math.round(maximum * 100) / 100 403 }; 404 }; 405 406 WineListingController.prototype.isDefaultPriceRange = function (range) { 407 var bounds = this.getPriceBounds(); 408 var normalized = this.normalizePriceRange(range); 409 410 if (!bounds || !normalized) { 411 return true; 412 } 413 414 return Math.abs(normalized.min - bounds.min) < 0.01 415 && Math.abs(normalized.max - bounds.max) < 0.01; 416 }; 417 418 WineListingController.prototype.setPriceRangeControl = function (control, range) { 419 var normalized = this.normalizePriceRange(range); 420 var bounds = this.getPriceBounds(); 421 422 if (!normalized || !bounds) { 423 return null; 424 } 425 426 var minInput = control.querySelector('.js-foley-c7-ui-price-min-input'); 427 var maxInput = control.querySelector('.js-foley-c7-ui-price-max-input'); 428 var minLabel = control.querySelector('.js-foley-c7-ui-price-min-label'); 429 var maxLabel = control.querySelector('.js-foley-c7-ui-price-max-label'); 430 var minHandle = control.querySelector('.js-foley-c7-ui-price-min-handle'); 431 var maxHandle = control.querySelector('.js-foley-c7-ui-price-max-handle'); 432 var span = bounds.max - bounds.min || 1; 433 var minPosition = ((normalized.min - bounds.min) / span) * 100; 434 var maxPosition = ((normalized.max - bounds.min) / span) * 100; 435 436 control.style.setProperty('--price-min-position', minPosition + '%'); 437 control.style.setProperty('--price-max-position', maxPosition + '%'); 438 439 if (minInput) { 440 minInput.value = formatDecimal(normalized.min); 441 } 442 443 if (maxInput) { 444 maxInput.value = formatDecimal(normalized.max); 445 } 446 447 if (minLabel) { 448 minLabel.textContent = formatPrice(normalized.min); 449 } 450 451 if (maxLabel) { 452 maxLabel.textContent = formatPrice(normalized.max); 453 } 454 455 if (minHandle) { 456 minHandle.setAttribute('aria-valuemin', formatDecimal(bounds.min)); 457 minHandle.setAttribute('aria-valuemax', formatDecimal(normalized.max)); 458 minHandle.setAttribute('aria-valuenow', formatDecimal(normalized.min)); 459 minHandle.setAttribute('aria-valuetext', formatPrice(normalized.min)); 460 } 461 462 if (maxHandle) { 463 maxHandle.setAttribute('aria-valuemin', formatDecimal(normalized.min)); 464 maxHandle.setAttribute('aria-valuemax', formatDecimal(bounds.max)
464); 465 maxHandle.setAttribute('aria-valuenow', formatDecimal(normalized.max)); 466 maxHandle.setAttribute('aria-valuetext', formatPrice(normalized.max)); 467 } 468 469 return normalized; 470 }; 471 472 WineListingController.prototype.getCurrentPriceRange = function (control) { 473 var minInput = control.querySelector('.js-foley-c7-ui-price-min-input'); 474 var maxInput = control.querySelector('.js-foley-c7-ui-price-max-input'); 475 476 return this.normalizePriceRange({ 477 min: minInput ? minInput.value : null, 478 max: maxInput ? maxInput.value : null 479 }); 480 }; 481 482 WineListingController.prototype.syncPriceRangeControl = function (control, changedHandle) { 483 var current = this.getCurrentPriceRange(control); 484 485 if (!current) { 486 return null; 487 } 488 489 var minimum = current.min; 490 var maximum = current.max; 491 492 if (minimum !== null && maximum !== null && minimum > maximum) { 493 if (changedHandle && changedHandle.getAttribute('data-price-handle') === 'min') { 494 minimum = maximum; 495 } else { 496 maximum = minimum; 497 } 498 } 499 500 return this.setPriceRangeControl(control, { 501 min: minimum, 502 max: maximum 503 }); 504 }; 505 506 WineListingController.prototype.priceValueFromPointer = function (control, clientX) { 507 var bounds = this.getPriceBounds(); 508 var track = control.querySelector('.js-foley-c7-ui-price-track'); 509 510 if (!bounds || !track) { 511 return null; 512 } 513 514 var rect = track.getBoundingClientRect(); 515 var ratio = rect.width > 0 ? (clientX - rect.left) / rect.width : 0; 516 var value = bounds.min + (Math.min(Math.max(ratio, 0), 1) * (bounds.max - bounds.min)); 517 518 return snapPriceValue(value); 519 }; 520 521 WineListingController.prototype.getNearestPriceHandle = function (control, clientX) { 522 var value = this.priceValueFromPointer(control, clientX); 523 var current = this.getCurrentPriceRange(control); 524 525 if (value === null || !current) { 526 return control.querySelector('.js-foley-c7-ui-price-min-handle'); 527 } 528 529 var minHandle = control.querySelector('.js-foley-c7-ui-price-min-handle'); 530 var maxHandle = control.querySelector('.js-foley-c7-ui-price-max-handle'); 531 532 return Math.abs(value - current.min) <= Math.abs(value - current.max) 533 ? minHandle 534 : maxHandle; 535 }; 536 537 WineListingController.prototype.setPriceHandleValue = function (control, handle, value) { 538 var current = this.getCurrentPriceRange(control); 539 540 if (!current || value === null || !handle) { 541 return null; 542 } 543 544 value = snapPriceValue(value); 545 546 if (handle.getAttribute('data-price-handle') === 'min') { 547 current.min = Math.min(value, current.max); 548 } else { 549 current.max = Math.max(value, current.min); 550 } 551 552 return this.setPriceRangeControl(control, current); 553 }; 554 555 WineListingController.prototype.startPriceDrag = function (event, target) { 556 var self = this; 557 var control = target.closest('.js-foley-c7-ui-price-control'); 558 var handle = target.classList.contains('js-foley-c7-ui-price-handle') 559 ? target 560 : this.getNearestPriceHandle(control, event.clientX); 561 562 if (!control || !handle) { 563 return; 564 } 565 566 event.preventDefault(); 567 handle.focus(); 568 this.setPriceHandleValue(control, handle, this.priceValueFromPointer(control, event.clientX)); 569 570 var onMove = function (moveEvent) { 571 self.setPriceHandleValue(control, handle, self.priceValueFromPointer(control, moveEvent.clientX)); 572 }; 573 574 var onUp = function () { 575 document.removeEventListener('pointermove', onMove); 576 document.removeEventListener('pointerup', onUp); 577 document.removeEventListener('pointercancel', onUp); 578 self.syncHiddenInputs(); 579 self.submit(self.buildState(1), false, true); 580 }; 581 582 document.addEventListener('pointermove', onMove); 583 document.addEventListener('pointerup', onUp); 584 document.addEventListener('pointercancel', onUp); 585 }; 586 587 WineListingController.prototype.handlePriceKeydown = function (event, handle) { 588 var control = handle.closest('.js-foley-c7-ui-price-control'); 589 var bounds = this.getPriceBounds(); 590 var current = control ? this.getCurrentPriceRange(control) : null; 591 592 if (!control || !bounds || !current) { 593 return; 594 } 595 596 var step = PRICE_STEP; 597 var value = handle.getAttribute('data-price-handle') === 'min' ? current.min : current.max; 598 599 if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') { 600 value -= step; 601 } else if (event.key === 'ArrowRight' || event.key === 'ArrowUp') { 602 value += step; 603 } else if (event.key === 'PageDown') { 604 value -= step * 10; 605 } else if (event.key === 'PageUp') { 606 value += step * 10; 607 } else if (event.key === 'Home') { 608 value = bounds.min; 609 } else if (event.key === 'End') { 610 value = bounds.max; 611 } else { 612 return; 613 } 614 615 event.preventDefault(); 616 this.setPriceHandleValue(control, handle, value);
617 this.syncHiddenInputs(); 618 this.submit(this.buildState(1), false, true); 619 }; 620 621 WineListingController.prototype.getPriceRangeState = function () { 622 var control = this.block.querySelector('.js-foley-c7-ui-price-control'); 623 624 if (!control) { 625 return null; 626 } 627 628 return this.syncPriceRangeControl(control); 629 }; 630 631 WineListingController.prototype.getSelectedValues = function (control) { 632 var type = control.getAttribute('data-control-type'); 633 634 if (type === 'checkbox') { 635 return Array.from(control.querySelectorAll('input[type="checkbox"]:checked')).map(function (input) { 636 return input.value; 637 }); 638 } 639 640 if (type === 'radio') { 641 var checked = control.querySelector('input[type="radio"]:checked'); 642 return checked && checked.value ? [checked.value] : []; 643 } 644 645 if (type === 'multiselect') { 646 var multiSelect = control.querySelector('select'); 647 648 return multiSelect 649 ? Array.from(multiSelect.options).filter(function (option) { 650 return option.selected; 651 }).map(function (option) { 652 return option.value; 653 }) 654 : []; 655 } 656 657 var select = control.querySelector('select'); 658 return select && select.value ? [select.value] : []; 659 }; 660 661 WineListingController.prototype.syncHiddenInputs = function () { 662 this.block.querySelectorAll('.foley-c7-ui-wine-listing__control[data-taxonomy]').forEach(function (control) { 663 var hiddenInput = control.querySelector('.js-foley-c7-ui-hidden-input'); 664 665 if (!hiddenInput) { 666 return; 667 } 668 669 hiddenInput.value = this.getSelectedValues(control).join(','); 670 }, this); 671 672 this.block.querySelectorAll('.js-foley-c7-ui-price-control').forEach(function (control) { 673 this.syncPriceRangeControl(control); 674 }, this); 675 }; 676 677 WineListingController.prototype.buildState = function (page, clearUrlState) { 678 var state = clearUrlState ? this.getEmptyState(page || 1) : this.readStateFromUrl(); 679 state.page = page || 1; 680 681 this.block.querySelectorAll('.foley-c7-ui-wine-listing__control[data-taxonomy]').forEach(function (control) { 682 var taxonomy = control.getAttribute('data-taxonomy'); 683 var values = this.getSelectedValues(control); 684 685 if (taxonomy) { 686 state.filters[taxonomy] = values; 687 } 688 }, this); 689 690 if (this.form) { 691 if (this.searchInput) { 692 state.search = normalizeSearchValue(this.searchInput.value); 693 } 694 695 if (this.block.querySelector('.js-foley-c7-ui-price-control')) { 696 state.priceRange = this.getPriceRangeState(); 697 } 698 699 var ninetyPlus = this.form.querySelector('.js-foley-c7-ui-ninety-plus'); 700 if (ninetyPlus) { 701 state.ninetyPlus = !!ninetyPlus.checked; 702 } 703 704 } 705 706 var sortSelect = this.block.querySelector('.js-foley-c7-ui-sort'); 707 if (sortSelect) { 708 state.sort = sortSelect.value || ''; 709 } 710 711 if (this.block.getAttribute('data-navigation-mode') === 'pagination') { 712 var perPageSelect = this.block.querySelector('.js-foley-c7-ui-per-page'); 713 var perPage = perPageSelect ? parseInt(perPageSelect.value || '', 10) : this.getDefaultPerPage(); 714 715 state.perPage = Number.isNaN(perPage) || perPage < 1 ? this.getDefaultPerPage() : perPage; 716 } 717 718 if (!this.block.getAttribute('data-navigation-mode') || this.block.getAttribute('data-navigation-mode') === 'none') { 719 state.page = 1; 720 } 721 722 return state; 723 }; 724 725 WineListingController.prototype.getPageFromUrl = function () { 726 return this.readStateFromUrl().page; 727 }; 728 729 WineListingController.prototype.syncControlsFromUrl = function () { 730 if (!this.config) { 731 return; 732 } 733 734 var stateConfig = this.config.stateConfig || {}; 735 var url = new URL(window.location.href); 736 737 if (this.searchInput) { 738 this.searchInput.value = normalizeSearchValue(stateConfig.search ? url.searchParams.get(stateConfig.search) : ''); 739 } 740 741 this.block.querySelectorAll('.foley-c7-ui-wine-listing__control[data-taxonomy]').forEach(function (control) { 742 var taxonomy = control.getAttribute('data-taxonomy'); 743 var type = control.getAttribute('data-control-type'); 744 var queryKey = (stateConfig.filters || {})[taxonomy]; 745 var values = splitValues(url.searchParams.get(queryKey || '')); 746 747 if (type === 'checkbox') { 748 control.querySelectorAll('input[type="checkbox"]').forEach(function (input) { 749 input.checked = values.indexOf(input.value) > -1; 750 }); 751 } 752 753 if (type === 'radio') { 754 control.querySelectorAll('input[type="radio"]').forEach(function (input) { 755 input.checked = values.length 756 ? values.indexOf(input.value) > -1 757 : input.value === ''; 758 }); 759 } 760 761 if (type === 'select') { 762 var select = control.querySelector('select'); 763 if (select) { 764 select.value = values[0] || ''; 765 } 766 } 767 768 if (type === 'multiselect') { 769 var multiSelect = control.querySelector('select'); 770 if (multiSelect) {
771 Array.from(multiSelect.options).forEach(function (option) { 772 option.selected = values.indexOf(option.value) > -1; 773 }); 774 } 775 } 776 }); 777 778 this.block.querySelectorAll('.js-foley-c7-ui-price-control').forEach(function (control) { 779 var priceKeys = stateConfig.priceRange || {}; 780 var minimum = priceKeys.min ? parseNumber(url.searchParams.get(priceKeys.min)) : null; 781 var maximum = priceKeys.max ? parseNumber(url.searchParams.get(priceKeys.max)) : null; 782 783 this.setPriceRangeControl(control, { 784 min: minimum, 785 max: maximum 786 }); 787 }, this); 788 789 var ninetyPlus = this.form ? this.form.querySelector('.js-foley-c7-ui-ninety-plus') : null; 790 if (ninetyPlus) { 791 var ninetyPlusValue = stateConfig.ninetyPlus ? url.searchParams.get(stateConfig.ninetyPlus) : ''; 792 ninetyPlus.checked = ['1', 'true', 'yes', 'on'].indexOf(String(ninetyPlusValue || '').toLowerCase()) > -1; 793 } 794 795 var sortSelect = this.block.querySelector('.js-foley-c7-ui-sort'); 796 if (sortSelect) { 797 sortSelect.value = url.searchParams.get(stateConfig.sort || '') || ''; 798 } 799 800 var perPageSelect = this.block.querySelector('.js-foley-c7-ui-per-page'); 801 if (perPageSelect) { 802 var perPageValue = stateConfig.perPage ? url.searchParams.get(stateConfig.perPage) : ''; 803 var selectedPerPage = perPageValue || String(this.getDefaultPerPage()); 804 var hasPerPageOption = Array.from(perPageSelect.options).some(function (option) { 805 return option.value === selectedPerPage; 806 }); 807 808 perPageSelect.value = hasPerPageOption ? selectedPerPage : String(this.getDefaultPerPage()); 809 } 810 }; 811 812 WineListingController.prototype.buildUrl = function (state) { 813 var stateConfig = this.config.stateConfig || {}; 814 var url = new URL(window.location.href); 815 var normalizedPriceRange = this.normalizePriceRange(state.priceRange); 816 817 [stateConfig.search, stateConfig.sort, stateConfig.page, stateConfig.perPage].forEach(function (queryKey) { 818 if (queryKey) { 819 url.searchParams.delete(queryKey); 820 } 821 }); 822 823 Object.keys(stateConfig.filters || {}).forEach(function (taxonomy) { 824 url.searchParams.delete(stateConfig.filters[taxonomy]); 825 }); 826 827 Object.keys(stateConfig.priceRange || {}).forEach(function (key) { 828 url.searchParams.delete(stateConfig.priceRange[key]); 829 }); 830 831 if (stateConfig.ninetyPlus) { 832 url.searchParams.delete(stateConfig.ninetyPlus); 833 } 834 835 Array.from(url.searchParams.keys()).forEach(function (queryKey) { 836 if (queryKey.indexOf('c7ui_wine_listing_') === 0) { 837 url.searchParams.delete(queryKey); 838 } 839 }); 840 841 Object.keys(state.filters || {}).forEach(function (taxonomy) { 842 var values = state.filters[taxonomy]; 843 var queryKey = (stateConfig.filters || {})[taxonomy]; 844 845 if (queryKey && values && values.length) { 846 url.searchParams.set(queryKey, values.join(',')); 847 } 848 }); 849 850 if (normalizedPriceRange && !this.isDefaultPriceRange(normalizedPriceRange)) { 851 if (stateConfig.priceRange && stateConfig.priceRange.min && stateConfig.priceRange.max) { 852 url.searchParams.set(stateConfig.priceRange.min, formatDecimal(normalizedPriceRange.min)); 853 url.searchParams.set(stateConfig.priceRange.max, formatDecimal(normalizedPriceRange.max)); 854 } 855 } 856 857 if (state.ninetyPlus && stateConfig.ninetyPlus) { 858 url.searchParams.set(stateConfig.ninetyPlus, '1'); 859 } 860 861 if (state.search && stateConfig.search) { 862 url.searchParams.set(stateConfig.search, state.search); 863 } 864 865 if (state.sort) { 866 url.searchParams.set(stateConfig.sort, state.sort); 867 } 868 869 if (stateConfig.perPage && state.perPage && state.perPage !== this.getDefaultPerPage()) { 870 url.searchParams.set(stateConfig.perPage, String(state.perPage)); 871 } 872 873 if (state.page > 1) { 874 url.searchParams.set(stateConfig.page, String(state.page)); 875 } 876 877 return url; 878 }; 879 880 WineListingController.prototype.setLoading = function (isLoading) { 881 this.block.classList.toggle('is-loading', !!isLoading); 882 883 if (this.statusNode) { 884 this.statusNode.textContent = isLoading 885 ? ((window.FoleyC7UiWineProductListingView || {}).i18n || {}).loading || 'Loading' 886 : ''; 887 } 888 }; 889 890 WineListingController.prototype.request = function (state, appendMode, signal) { 891 return fetch(this.restUrl, { 892 method: 'POST', 893 headers: { 894 'Content-Type': 'application/json' 895 }, 896 signal: signal, 897 body: JSON.stringify({ 898 attributes: this.config.attributes || {}, 899 state: state, 900 append: !!appendMode 901 }) 902 }).then(function (response) { 903 return response.json(); 904 }); 905 }; 906 907 WineListingController.prototype.emitListImpression = function (startIndex, reset) { 908 var analytics = window.foleyC7UiGa4Analytics; 909 910 if (!analytics || typeof analytics.emitViewItemList !== 'function') { 911 return; 912 } 913 914 analytics.emitViewItemList(this.block, { 915 startIndex: startIndex, 916 reset: !!reset 917 }); 918 }; 919 920 WineListingController.prototype.updateDom = function (payload, appendMode) { 921 if (!this.itemsContainer || !this.navigationContainer) { 922 return; 923 } 924 925 var startIndex = appendMode 926 ? this.itemsContainer.querySelectorAll('.foley-c7-ui-wine-card[data-ga4-item-id]').length 927 : 0; 928 929 if (appendMode && payload.itemsHtml) { 930 this.itemsContainer.insertAdjacentHTML('beforeend', payload.itemsHtml); 931 } else { 932 this.itemsContainer.innerHTML = payload.itemsHtml || payload.emptyHtml || ''; 933 } 934 935 this.navigationContainer.innerHTML = payload.navigationHtml || ''; 936 this.emitListImpression(startIndex, !appendMode); 937 }; 938 939 WineListingController.prototype.submit = function (state, appendMode, pushState) { 940 var self = this; 941 var url = this.buildUrl(state); 942 var requestSequence = ++this.requestSequence; 943 944 if (this.abortController) { 945 this.abortController.abort(); 946 } 947 948 this.abortController = typeof window.AbortController === 'function' 949 ? new window.AbortController() 950 : null; 951 952 if (pushState) { 953 window.history.pushState({}, '', url); 954 } 955 956 this.setLoading(true); 957 958 this.request(state, appendMode, this.abortController ? this.abortController.signal : undefined) 959 .then(function (payload) { 960 if (requestSequence === self.requestSequence) { 961 self.updateDom(payload, appendMode); 962 } 963 }) 964 .catch(function (error) {
965 if (!error || error.name !== 'AbortError') { 966 window.console.error('Wine Product Listing request failed.', error); 967 } 968 }) 969 .finally(function () { 970 if (requestSequence === self.requestSequence) { 971 self.abortController = null; 972 self.setLoading(false); 973 } 974 }); 975 }; 976 977 var controllers = []; 978 979 document.addEventListener('DOMContentLoaded', function () { 980 document.querySelectorAll('.js-foley-c7-ui-wine-listing').forEach(function (block) { 981 var controller = new WineListingController(block); 982 controller.init(); 983 controller.emitListImpression(0, true); 984 controllers.push(controller); 985 }); 986 }); 987 988 window.addEventListener('popstate', function () { 989 controllers.forEach(function (controller) { 990 controller.handlePopState(); 991 }); 992 }); 993})(); 994; 995/*! This file is auto-generated */ 996(function() { 997"use strict";var wp;(wp||={}).domReady=(()=>{var o=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var f=(e,t)=>{for(var n in t)o(e,n,{get:t[n],enumerable:!0})},m=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let d of a(t))!i.call(e,d)&&d!==n&&o(e,d,{get:()=>t[d],enumerable:!(r=u(t,d))||r.enumerable});return e};var c=e=>m(o({},"__esModule",{value:!0}),e);var p={};f(p,{default:()=>y});function y(e){if(!(typeof document>"u")){if(document.readyState==="complete"||document.readyState==="interactive")return void e();document.addEventListener("DOMContentLoaded",e)}}return c(p);})(); 998if (typeof wp.domReady === 'object' && wp.domReady.default) { wp.domReady = wp.domReady.default; } 999(window.wp ||= {}).domReady = wp.domReady; 1000})(); 1001; 1002/*! This file is auto-generated */ 1003(function() { 1004"use strict";var wp;(wp||={}).hooks=(()=>{var v=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var w=(e,n)=>{for(var s in n)v(e,s,{get:n[s],enumerable:!0})},D=(e,n,s,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of g(n))!I.call(e,t)&&t!==s&&v(e,t,{get:()=>n[t],enumerable:!(r=S(n,t))||r.enumerable});return e};var T=e=>D(v({},"__esModule",{value:!0}),e);var le={};w(le,{actions:()=>ae,addAction:()=>J,addFilter:()=>K,applyFilters:()=>N,applyFiltersAsync:()=>ee,createHooks:()=>F,currentAction:()=>te,currentFilter:()=>re,defaultHooks:()=>b,didAction:()=>ie,didFilter:()=>se,doAction:()=>X,doActionAsync:()=>Y,doingAction:()=>ne,doingFilter:()=>oe,filters:()=>ce,hasAction:()=>P,hasFilter:()=>Q,removeAction:()=>L,removeAllActions:()=>U,removeAllFilters:()=>W,removeFilter:()=>M});function z(e){return typeof e!="string"||e===""?(console.error("The namespace must be a non-empty string."),!1):/^[a-zA-Z][a-zA-Z0-9_.\-\/]*$/.test(e)?!0:(console.error("The namespace can only contain numbers, letters, dashes, periods, underscores and slashes."),!1)}var m=z;function E(e){return typeof e!="string"||e===""?(console.error("The hook name must be a non-empty string."),!1):/^__/.test(e)?(console.error("The hook name cannot begin with `__`."),!1):/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(e)?!0:(console.error("The hook name can only contain numbers, letters, dashes, periods and underscores."),!1)}var f=E;function Z(e,n){return function(r,t,a,i=10){let c=e[n];if(!f(r)||!m(t))return;if(typeof a!="function"){console.error("The hook callback must be a function.");return}if(typeof i!="number"){console.error("If specified, the hook priority must be a number.");return}let l={callback:a,priority:i,namespace:t};if(c[r]){let o=c[r].handlers,d;for(d=o.length;d>0&&!(i>=o[d-1].priority);d--);d===o.length?o[d]=l:o.splice(d,0,l),c.__current.forEach(h=>{h.name===r&&h.currentIndex>=d&&h.currentIndex++})}else c[r]={handlers:[l],runs:0};r!=="hookAdded"&&e.doAction("hookAdded",r,t,a,i)}}var H=Z;function C(e,n,s=!1){return function(t,a){let i=e[n];if(!f(t)||!s&&!m(a))return;if(!i[t])return 0;let c=0;if(s)c=i[t].handlers.length,i[t]={runs:i[t].runs,handlers:[]};else{let l=i[t].handlers;for(let o=l.length-1;o>=0;o--)l[o].namespace===a&&(l.splice(o,1),c++,i.__current.forEach(d=>{d.name===t&&d.currentIndex>=o&&d.currentIndex--}))}return t!=="hookRemoved"&&e.doAction("hookRemoved",t,a),c}}var p=C;function O(e,n){return function(r,t){let a=e[n];return typeof t<"u"?r in a&&a[r].handlers.some(i=>i.namespace===t):r in a}}var _=O;function j(e,n,s,r){return function(a,...i){let c=e[n];c[a]||(c[a]={handlers:[],runs:0}),c[a].runs++;let l=c[a].handlers;if(!l||!l.length)return s?i[0]:void 0;let o={name:a,currentIndex:0};async function d(){try{c.__current.add(o);let u=s?i[0]:void 0;for(;o.currentIndex<l.length;
1004)u=await l[o.currentIndex].callback.apply(null,i),s&&(i[0]=u),o.currentIndex++;return s?u:void 0}finally{c.__current.delete(o)}}function h(){try{c.__current.add(o);let u=s?i[0]:void 0;for(;o.currentIndex<l.length;)u=l[o.currentIndex].callback.apply(null,i),s&&(i[0]=u),o.currentIndex++;return s?u:void 0}finally{c.__current.delete(o)}}return(r?d:h)()}}var A=j;function $(e,n){return function(){let r=e[n];return Array.from(r.__current).at(-1)?.name??null}}var y=$;function V(e,n){return function(r){let t=e[n];return typeof r>"u"?t.__current.size>0:Array.from(t.__current).some(a=>a.name===r)}}var k=V;function q(e,n){return function(r){let t=e[n];if(f(r))return t[r]&&t[r].runs?t[r].runs:0}}var x=q;var B=class{actions;filters;addAction;addFilter;removeAction;removeFilter;hasAction;hasFilter;removeAllActions;removeAllFilters;doAction;doActionAsync;applyFilters;applyFiltersAsync;currentAction;currentFilter;doingAction;doingFilter;didAction;didFilter;constructor(){this.actions=Object.create(null),this.actions.__current=new Set,this.filters=Object.create(null),this.filters.__current=new Set,this.addAction=H(this,"actions"),this.addFilter=H(this,"filters"),this.removeAction=p(this,"actions"),this.removeFilter=p(this,"filters"),this.hasAction=_(this,"actions"),this.hasFilter=_(this,"filters"),this.removeAllActions=p(this,"actions",!0),this.removeAllFilters=p(this,"filters",!0),this.doAction=A(this,"actions",!1,!1),this.doActionAsync=A(this,"actions",!1,!0),this.applyFilters=A(this,"filters",!0,!1),this.applyFiltersAsync=A(this,"filters",!0,!0),this.currentAction=y(this,"actions"),this.currentFilter=y(this,"filters"),this.doingAction=k(this,"actions"),this.doingFilter=k(this,"filters"),this.didAction=x(this,"actions"),this.didFilter=x(this,"filters")}};function G(){return new B}var F=G;var b=F(),{addAction:J,addFilter:K,removeAction:L,removeFilter:M,hasAction:P,hasFilter:Q,removeAllActions:U,removeAllFilters:W,doAction:X,doActionAsync:Y,applyFilters:N,applyFiltersAsync:ee,currentAction:te,currentFilter:re,doingAction:ne,doingFilter:oe,didAction:ie,didFilter:se,actions:ae,filters:ce}=b;return T(le);})(); 1005(window.wp ||= {}).hooks = wp.hooks; 1006})(); 1007;
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.