PageSourceSearch

https://www.highvibesdrinks.com/cdn/shop/t/5/assets/global_v-85297797553816670871676424627.js

js highvibesdrinks.com collected 2026-10-09 23:02:46 UTC 38,295 bytes, 1,068 lines download raw bytes

1/** Shopify CDN: Minification failed
2
3Line 40:0 Transforming const to the configured target environment ("es5") is not supported yet
4Line 42:45 Transforming default arguments to the configured target environment ("es5") is not supported yet
5Line 102:2 Transforming const to the configured target environment ("es5") is not supported yet
6Line 103:2 Transforming let to the configured target environment ("es5") is not supported yet
7Line 104:2 Transforming let to the configured target environment ("es5") is not supported yet
8Line 140:40 Transforming default arguments to the configured target environment ("es5") is not supported yet
9Line 151:2 Transforming const to the configured target environment ("es5") is not supported yet
10Line 154:2 Transforming const to the configured target environment ("es5") is not supported yet
11Line 160:0 Transforming class syntax to the configured target environment ("es5") is not supported yet
12Line 161:13 Transforming object literal extensions to the configured target environment ("es5") is not supported yet
13... and 157 more hidden warnings
14
15**/
16function getFocusableElements(container) {
17  return Array.from(
18    container.querySelectorAll(
19      "summary, a[href], button:enabled, [tabindex]:not([tabindex^='-']), [draggable], area, input:not([type=hidden]):enabled, select:enabled, textarea:enabled, object, iframe"
20    )
21  );
22}
23
24document.querySelectorAll('[id^="Details-"] summary').forEach((summary) => {
25  summary.setAttribute('role', 'button');
26  summary.setAttribute('aria-expanded', summary.parentNode.hasAttribute('open'));
27
28  if(summary.nextElementSibling.getAttribute('id')) {
29    summary.setAttribute('aria-controls', summary.nextElementSibling.id);
30  }
31
32  summary.addEventListener('click', (event) => {
33    event.currentTarget.setAttribute('aria-expanded', !event.currentTarget.closest('details').hasAttribute('open'));
34  });
35
36  if (summary.closest('header-drawer')) return;
37  summary.parentElement.addEventListener('keyup', onKeyUpEscape);
38});
39
40const trapFocusHandlers = {};
41
42function trapFocus(container, elementToFocus = container) {
43  var elements = getFocusableElements(container);
44  var first = elements[0];
45  var last = elements[elements.length - 1];
46
47  removeTrapFocus();
48
49  trapFocusHandlers.focusin = (event) => {
50    if (
51      event.target !== container &&
52      event.target !== last &&
53      event.target !== first
54    )
55      return;
56
57    document.addEventListener('keydown', trapFocusHandlers.keydown);
58  };
59
60  trapFocusHandlers.focusout = function() {
61    document.removeEventListener('keydown', trapFocusHandlers.keydown);
62  };
63
64  trapFocusHandlers.keydown = function(event) {
65    if (event.code.toUpperCase() !== 'TAB') return; // If not TAB key
66    // On the last focusable element and tab forward, focus the first element.
67    if (event.target === last && !event.shiftKey) {
68      event.preventDefault();
69      first.focus();
70    }
71
72    //  On the first focusable element and tab backward, focus the last element.
73    if (
74      (event.target === container || event.target === first) &&
75      event.shiftKey
76    ) {
77      event.preventDefault();
78      last.focus();
79    }
80  };
81
82  document.addEventListener('focusout', trapFocusHandlers.focusout);
83  document.addEventListener('focusin', trapFocusHandlers.focusin);
84
85  elementToFocus.focus();
86
87  if (elementToFocus.tagName === 'INPUT' &&
88    ['search', 'text', 'email', 'url'].includes(elementToFocus.type) &&
89    elementToFocus.value) {
90    elementToFocus.setSelectionRange(0, elementToFocus.value.length);
91  }
92}
93
94// Here run the querySelector to figure out if the browser supports :focus-visible or not and run code based on it.
95try {
96  document.querySelector(":focus-visible");
97} catch(e) {
98  focusVisiblePolyfill();
99}
100
101function focusVisiblePolyfill() {
102  const navKeys = ['ARROWUP', 'ARROWDOWN', 'ARROWLEFT', 'ARROWRIGHT', 'TAB', 'ENTER', 'SPACE', 'ESCAPE', 'HOME', 'END', 'PAGEUP', 'PAGEDOWN']
103  let currentFocusedElement = null;
104  let mouseClick = null;
105
106  window.addEventListener('keydown', (event) => {
107    if(navKeys.includes(event.code.toUpperCase())) {
108      mouseClick = false;
109    }
110  });
111
112  window.addEventListener('mousedown', (event) => {
113    mouseClick = true;
114  });
115
116  window.addEventListener('focus', () => {
117    if (currentFocusedElement) currentFocusedElement.classList.remove('focused');
118
119    if (mouseClick) return;
120
121    currentFocusedElement = document.activeElement;
122    currentFocusedElement.classList.add('focused');
123
124  }, true);
125}
126
127function pauseAllMedia() {
128  document.querySelectorAll('.js-youtube').forEach((video) => {
129    video.contentWindow.postMessage('{"event":"command","func":"' + 'pauseVideo' + '","args":""}', '*');
130  });
131  document.querySelectorAll('.js-vimeo').forEach((video) => {
132    video.contentWindow.postMessage('{"method":"pause"}', '*');
133  });
134  document.querySelectorAll('video').forEach((video) => video.pause());
135  document.querySelectorAll('product-model').forEach((model) => {
136    if (model.modelViewerUI) model.modelViewerUI.pause();
137  });
138}
139
140function removeTrapFocus(elementToFocus = null) {
141  document.removeEventListener('focusin', trapFocusHandlers.focusin);
142  document.removeEventListener('focusout', trapFocusHandlers.focusout);
143  document.removeEventListener('keydown', trapFocusHandlers.keydown);
144
145  if (elementToFocus) elementToFocus.focus();
146}
147
148function onKeyUpEscape(event) {
149  if (event.code.toUpperCase() !== 'ESCAPE') return;
150
151  const openDetailsElement = event.target.closest('details[open]');
152  if (!openDetailsElement) return;
153
154  const summaryElement = openDetailsElement.querySelector('summary');
155  openDetailsElement.removeAttribute('open');
156  summaryElement.setAttribute('aria-expanded', false);
157  summaryElement.focus();
158}
159
160class QuantityInput extends HTMLElement {
161  constructor() {
162    super();
163    this.input = this.querySelector('input');
164    this.changeEvent = new Event('change', { bubbles: true });
165
166    this.input.addEventListener('change', this.onInputChange.bind(this));
167    this.querySelectorAll('button').forEach(
168      (button) => button.addEventListener('click', this.onButtonClick.bind(this))
169    );
170  }
171
172  quantityUpdateUnsubscriber = undefined;
173
174  connectedCallback() {
175    this.validateQtyRules();
176    this.quantityUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.quantityUpdate, this.validateQtyRules.bind(this));
177  }
178
179  disconnectedCallback() {
180    if (this.quantityUpdateUnsubscriber) {
181      this.quantityUpdateUnsubscriber();
182    }
183  }
184
185  onInputChange(event) {
186    this.validateQtyRules();
187  }
188
189  onButtonClick(event) {
190    event.preventDefault();
191    const previousValue = this.input.value;
192
193    event.target.name === 'plus' ? this.input.stepUp() : this.input.stepDown();
194    if (previousValue !== this.input.value) this.input.dispatchEvent(this.changeEvent);
195  }
196
197  validateQtyRules() {
198    const value = parseInt(this.input.value);
199    if (this.input.min) {
200      const min = parseInt(this.input.min);
201      const buttonMinus = this.querySelector(".quantity__button[name='minus']");
202      buttonMinus.classList.toggle('disabled', value <= min);
203    }
204    if (this.input.max) {
205      const max = parseInt(this.input.max);
206      const buttonPlus = this.querySelector(".quantity__button[name='plus']");
207      buttonPlus.classList.toggle('disabled', value >= max);
208    } 
209  }
210}
211
212customElements.define('quantity-input', QuantityInput);
213
214function debounce(fn, wait) {
215  let t;
216  return (...args) => {
217    clearTimeout(t);
218    t = setTimeout(() => fn.apply(this, args), wait);
219  };
220}
221
222function fetchConfig(type = 'json') {
223  return {
224    method: 'POST',
225    headers: { 'Content-Type': 'application/json', 'Accept': `application/${type}` }
226  };
227}
228
229/*
230 * Shopify Common JS
231 *
232 */
233if ((typeof window.Shopify) == 'undefined') {
234  window.Shopify = {};
235}
236
237Shopify.bind = function(fn, scope) {
238  return function() {
239    return fn.apply(scope, arguments);
240  }
241};
242
243Shopify.setSelectorByValue = function(selector, value) {
244  for (var i = 0, count = selector.options.length; i < count; i++) {
245    var option = selector.options[i];
246    if (value == option.value || value == option.innerHTML) {
247      selector.selectedIndex = i;
248      return i;
249    }
250  }
251};
252
253Shopify.addListener = function(target, eventName, callback) {
254  target.addEventListener ? target.addEventListener(eventName, callback, false) : target.attachEvent('on'+eventName, callback);
255};
256
257Shopify.postLink = function(path, options) {
258  options = options || {};
259  var method = options['method'] || 'post';
260  var params = options['parameters'] || {};
261
262  var form = document.createElement("form");
263  form.setAttribute("method", method);
264  form.setAttribute("action", path);
265
266  for(var key in params) {
267    var hiddenField = document.createElement("input");
268    hiddenField.setAttribute("type", "hidden");
269    hiddenField.setAttribute("name", key);
270    hiddenField.setAttribute("value", params[key]);
271    form.appendChild(hiddenField);
272  }
273  document.body.appendChild(form);
274  form.submit();
275  document.body.removeChild(form);
276};
277
278Shopify.CountryProvinceSelector = function(country_domid, province_domid, options) {
279  this.countryEl         = document.getElementById(country_domid);
280  this.provinceEl        = document.getElementById(province_domid);
281  this.provinceContainer = document.getElementById(options['hideElement'] || province_domid);
282
283  Shopify.addListener(this.countryEl, 'change', Shopify.bind(this.countryHandler,this));
284
285  this.initCountry();
286  this.initProvince();
287};
288
289Shopify.CountryProvinceSelector.prototype = {
290  initCountry: function() {
291    var value = this.countryEl.getAttribute('data-default');
292    Shopify.setSelectorByValue(this.countryEl, value);
293    this.countryHandler();
294  },
295
296  initProvince: function() {
297    var value = this.provinceEl.getAttribute('data-default');
298    if (value && this.provinceEl.options.length > 0) {
299      Shopify.setSelectorByValue(this.provinceEl, value);
300    }
301  },
302
303  countryHandler: function(e) {
304    var opt       = this.countryEl.options[this.countryEl.selectedIndex];
305    var raw       = opt.getAttribute('data-provinces');
306    var provinces = JSON.parse(raw);
307
308    this.clearOptions(this.provinceEl);
309    if (provinces && provinces.length == 0) {
310      this.provinceContainer.style.display = 'none';
311    } else {
312      for (var i = 0; i < provinces.length; i++) {
313        var opt = document.createElement('option');
314        opt.value = provinces[i][0];
315        opt.innerHTML = provinces[i][1];
316        this.provinceEl.appendChild(opt);
317      }
318
319      this.provinceContainer.style.display = "";
320    }
321  },
322
323  clearOptions: function(selector) {
324    while (selector.firstChild) {
325      selector.removeChild(selector.firstChild);
326    }
327  },
328
329  setOptions: function(selector, values) {
330    for (var i = 0, count = values.length; i < values.length; i++) {
331      var opt = document.createElement('option');
332      opt.value = values[i];
333      opt.innerHTML = values[i];
334      selector.appendChild(opt);
335    }
336  }
337};
338
339class MenuDrawer extends HTMLElement {
340  constructor() {
341    super();
342
343    this.mainDetailsToggle = this.querySelector('details');
344
345    this.addEventListener('keyup', this.onKeyUp.bind(this));
346    this.addEventListener('focusout', this.onFocusOut.bind(this));
347    this.bindEvents();
348  }
349
350  bindEvents() {
351    this.querySelectorAll('summary').forEach(summary => summary.addEventListener('click', this.onSummaryClick.bind(this)));
352    this.querySelectorAll('button').forEach(button => button.addEventListener('click', this.onCloseButtonClick.bind(this)));
353  }
354
355  onKeyUp(event) {
356    if(event.code.toUpperCase() !== 'ESCAPE') return;
357
358    const openDetailsElement = event.target.closest('details[open]');
359    if(!openDetailsElement) return;
360
361    openDetailsElement === this.mainDetailsToggle ? this.closeMenuDrawer(event, this.mainDetailsToggle.querySelector('summary')) : this.closeSubmenu(openDetailsElement);
362  }
363
364  onSummaryClick(event) {
365    const summaryElement = event.currentTarget;
366    const detailsElement = summaryElement.parentNode;
367    const parentMenuElement = detailsElement.closest('.has-submenu');
368    const isOpen = detailsElement.hasAttribute('open');
369    const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
370
371    function addTrapFocus() {
372      trapFocus(summaryElement.nextElementSibling, detailsElement.querySelector('button'));
373      summaryElement.nextElementSibling.removeEventListener('transitionend', addTrapFocus);
374    }
375
376    if (detailsElement === this.mainDetailsToggle) {
377      if(isOpen) event.preventDefault();
378      isOpen ? this.closeMenuDrawer(event, summaryElement) : this.openMenuDrawer(summaryElement);
379
380      if (window.matchMedia('(max-width: 990px)')) {
381        document.documentElement.style.setProperty('--viewport-height', `${window.innerHeight}px`);
382      }
383    } else {
384      setTimeout(() => {
385        detailsElement.classList.add('menu-opening');
386        summaryElement.setAttribute('aria-expanded', true);
387        parentMenuElement && parentMenuElement.classList.add('submenu-open');
388        !reducedMotion || reducedMotion.matches ? addTrapFocus() : summaryElement.nextElementSibling.addEventListener('transitionend', addTrapFocus);
389      }, 100);
390    }
391  }
392
393  openMenuDrawer(summaryElement) {
394    setTimeout(() => {
395      this.mainDetailsToggle.classList.add('menu-opening');
396    });
397    summaryElement.setAttribute('aria-expanded', true);
398    trapFocus(this.mainDetailsToggle, summaryElement);
399    document.body.classList.add(`overflow-hidden-${this.dataset.breakpoint}`);
400  }
401
402  closeMenuDrawer(event, elementToFocus = false) {
403    if (event === undefined) return;
404
405    this.mainDetailsToggle.classList.remove('menu-opening');
406    this.mainDetailsToggle.querySelectorAll('details').forEach(detail
406s => {
407      details.removeAttribute('open');
408      details.classList.remove('menu-opening');
409    });
410    this.mainDetailsToggle.querySelectorAll('.submenu-open').forEach(submenu => {
411      submenu.classList.remove('submenu-open');
412    });
413    document.body.classList.remove(`overflow-hidden-${this.dataset.breakpoint}`);
414    removeTrapFocus(elementToFocus);
415    this.closeAnimation(this.mainDetailsToggle);
416  }
417
418  onFocusOut(event) {
419    setTimeout(() => {
420      if (this.mainDetailsToggle.hasAttribute('open') && !this.mainDetailsToggle.contains(document.activeElement)) this.closeMenuDrawer();
421    });
422  }
423
424  onCloseButtonClick(event) {
425    const detailsElement = event.currentTarget.closest('details');
426    this.closeSubmenu(detailsElement);
427  }
428
429  closeSubmenu(detailsElement) {
430    const parentMenuElement = detailsElement.closest('.submenu-open');
431    parentMenuElement && parentMenuElement.classList.remove('submenu-open');
432    detailsElement.classList.remove('menu-opening');
433    detailsElement.querySelector('summary').setAttribute('aria-expanded', false);
434    removeTrapFocus(detailsElement.querySelector('summary'));
435    this.closeAnimation(detailsElement);
436  }
437
438  closeAnimation(detailsElement) {
439    let animationStart;
440
441    const handleAnimation = (time) => {
442      if (animationStart === undefined) {
443        animationStart = time;
444      }
445
446      const elapsedTime = time - animationStart;
447
448      if (elapsedTime < 400) {
449        window.requestAnimationFrame(handleAnimation);
450      } else {
451        detailsElement.removeAttribute('open');
452        if (detailsElement.closest('details[open]')) {
453          trapFocus(detailsElement.closest('details[open]'), detailsElement.querySelector('summary'));
454        }
455      }
456    }
457
458    window.requestAnimationFrame(handleAnimation);
459  }
460}
461
462customElements.define('menu-drawer', MenuDrawer);
463
464class HeaderDrawer extends MenuDrawer {
465  constructor() {
466    super();
467  }
468
469  openMenuDrawer(summaryElement) {
470    this.header = this.header || document.querySelector('.section-header');
471    this.borderOffset = this.borderOffset || this.closest('.header-wrapper').classList.contains('header-wrapper--border-bottom') ? 1 : 0;
472    document.documentElement.style.setProperty('--header-bottom-position', `${parseInt(this.header.getBoundingClientRect().bottom - this.borderOffset)}px`);
473    this.header.classList.add('menu-open');
474
475    setTimeout(() => {
476      this.mainDetailsToggle.classList.add('menu-opening');
477    });
478
479    summaryElement.setAttribute('aria-expanded', true);
480    trapFocus(this.mainDetailsToggle, summaryElement);
481    document.body.classList.add(`overflow-hidden-${this.dataset.breakpoint}`);
482  }
483
484  closeMenuDrawer(event, elementToFocus) {
485    super.closeMenuDrawer(event, elementToFocus);
486    this.header.classList.remove('menu-open');
487  }
488}
489
490customElements.define('header-drawer', HeaderDrawer);
491
492class ModalDialog extends HTMLElement {
493  constructor() {
494    super();
495    this.querySelector('[id^="ModalClose-"]').addEventListener(
496      'click',
497      this.hide.bind(this, false)
498    );
499    this.addEventListener('keyup', (event) => {
500      if (event.code.toUpperCase() === 'ESCAPE') this.hide();
501    });
502    if (this.classList.contains('media-modal')) {
503      this.addEventListener('pointerup', (event) => {
504        if (event.pointerType === 'mouse' && !event.target.closest('deferred-media, product-model')) this.hide();
505      });
506    } else {
507      this.addEventListener('click', (event) => {
508        if (event.target === this) this.hide();
509      });
510    }
511  }
512
513  connectedCallback() {
514    if (this.moved) return;
515    this.moved = true;
516    document.body.appendChild(this);
517  }
518
519  show(opener) {
520    this.openedBy = opener;
521    const popup = this.querySelector('.template-popup');
522    document.body.classList.add('overflow-hidden');
523    this.setAttribute('open', '');
524    if (popup) popup.loadContent();
525    trapFocus(this, this.querySelector('[role="dialog"]'));
526    window.pauseAllMedia();
527  }
528
529  hide() {
530    document.body.classList.remove('overflow-hidden');
531    document.body.dispatchEvent(new CustomEvent('modalClosed'));
532    this.removeAttribute('open');
533    removeTrapFocus(this.openedBy);
534    window.pauseAllMedia();
535  }
536}
537customElements.define('modal-dialog', ModalDialog);
538
539class ModalOpener extends HTMLElement {
540  constructor() {
541    super();
542
543    const button = this.querySelector('button');
544
545    if (!button) return;
546    button.addEventListener('click', () => {
547      const modal = document.querySelector(this.getAttribute('data-modal'));
548      if (modal) modal.show(button);
549    });
550  }
551}
552customElements.define('modal-opener', ModalOpener);
553
554class DeferredMedia extends HTMLElement {
555  constructor() {
556    super();
557    const poster = this.querySelector('[id^="Deferred-Poster-"]');
558    if (!poster) return;
559    poster.addEventListener('click', this.loadContent.bind(this));
560  }
561
562  loadContent(focus = true) {
563    window.pauseAllMedia();
564    if (!this.getAttribute('loaded')) {
565      const content = document.createElement('div');
566      content.appendChild(this.querySelector('template').content.firstElementChild.cloneNode(true));
567
568      this.setAttribute('loaded', true);
569      const deferredElement = this.appendChild(content.querySelector('video, model-viewer, iframe'));
570      if (focus) deferredElement.focus();
571    }
572  }
573}
574
575customElements.define('deferred-media', DeferredMedia);
576
577class SliderComponent extends HTMLElement {
578  constructor() {
579    super();
580    this.slider = this.querySelector('[id^="Slider-"]');
581    this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
582    this.enableSliderLooping = false;
583    this.currentPageElement = this.querySelector('.slider-counter--current');
584    this.pageTotalElement = this.querySelector('.slider-counter--total');
585    this.prevButton = this.querySelector('button[name="previous"]');
586    this.nextButton = this.querySelector('button[name="next"]');
587
588    if (!this.slider || !this.nextButton) return;
589
590    this.initPages();
591    const resizeObserver = new ResizeObserver(entries => this.initPages());
592    resizeObserver.observe(this.slider);
593
594    this.slider.addEventListener('scroll', this.update.bind(this));
595    this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
596    this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
597  }
598
599  initPages() {
600    this.sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0);
601    if (this.sliderItemsToShow.length < 2) return;
602    this.sliderItemOffset = this.sliderItemsToShow[1].offsetLeft - this.sliderItemsToShow[0].offsetLeft;
603    this.slidesPerPage = Math.floor((this.slider.clientWidth - this.sliderItemsToShow[0].offsetLeft) / this.sliderItemOffset);
604    this.totalPages = this.sliderItemsToShow.length - this.slidesPerPage + 1;
605    this.update();
606  }
607
608  resetPages() {
609    this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
610    this.initPages();
611  }
612
613  update() {
614    // Temporarily prevents unneeded updates resulting from variant changes
615    // This should be refactored as part of https://github.com/Shopify/dawn/issues/2057
616    if (!this.slider || !this.nextButton) return;
617
618    const previousPage = this.currentPage;
619    this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItemOffset) + 1;
620
621    if (this.currentPageElement && this.pageTotalElement) {
622      this.currentPageElement.textContent = this.currentPage;
623      this.pageTotalElement.textContent = this.totalPages;
624    }
625
626    if (this.currentPage != previousPage) {
627      this.dispatchEvent(new CustomEvent('slideChanged', { detail: {
628        currentPage: this.currentPage,
629        currentElement: this.sliderItemsToShow[this.currentPage - 1]
630      }}));
631    }
632
633    if (this.enableSliderLooping) return;
634
635    if (this.isSlideVisible(this.sliderItemsToShow[0]) && this.slider.scrollLeft === 0) {
636      this.prevButton.setAttribute('disabled', 'disabled');
637    } else {
638      this.prevButton.removeAttribute('disabled');
639    }
640
641    if (this.isSlideVisible(this.sliderItemsToShow[this.sliderItemsToShow.length - 1])) {
642      this.nextButton.setAttribute('disabled', 'disabled');
643    } else {
644      this.nextButton.removeAttribute('disabled');
645    }
646  }
647
648  isSlideVisible(element, offset = 0) {
649    const lastVisibleSlide = this.slider.clientWidth + this.slider.scrollLeft - offset;
650    return (element.offsetLeft + element.clientWidth) <= lastVisibleSlide && element.offsetLeft >= this.slider.scrollLeft;
651  }
652
653  onButtonClick(event) {
654    event.preventDefault();
655    const step = event.currentTarget.dataset.step || 1;
656    this.slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + (step * this.sliderItemOffset) : this.slider.scrollLeft - (step * this.sliderItemOffset);
657    this.slider.scrollTo({
658      left: this.slideScrollPosition
659    });
660  }
661}
662
663customElements.define('slider-component', SliderComponent);
664
665class SlideshowComponent extends SliderComponent {
666  constructor() {
667    super();
668    this.sliderControlWrapper = this.querySelector('.slider-buttons');
669    this.enableSliderLooping = true;
670
671    if (!this.sliderControlWrapper) return;
672
673    this.sliderFirstItemNode = this.slider.querySelector('.slideshow__slide');
674    if (this.sliderItemsToShow.length > 0) this.currentPage = 1;
675
676    this.sliderControlLinksArray = Array.from(this.sliderControlWrapper.querySelectorAll('.slider-counter__link'));
677    this.sliderControlLinksArray.forEach(link => link.addEventListener('click', this.linkToSlide.bind(this)));
678    this.slider.addEventListener('scroll', this.setSlideVisibility.bind(this));
679    this.setSlideVisibility();
680
681    if (this.slider.getAttribute('data-autoplay') === 'true') this.setAutoPlay();
682  }
683
684  setAutoPlay() {
685    this.sliderAutoplayButton = this.querySelector('.slideshow__autoplay');
686    this.autoplaySpeed = this.slider.dataset.speed * 1000;
687
688    this.sliderAutoplayButton.addEventListener('click', this.autoPlayToggle.bind(this));
689    this.addEventListener('mouseover', this.focusInHandling.bind(this));
690    this.addEventListener('mouseleave', this.focusOutHandling.bind(this));
691    this.addEventListener('focusin', this.focusInHandling.bind(this));
692    this.addEventListener('focusout', this.focusOutHandling.bind(this));
693
694    this.play();
695    this.autoplayButtonIsSetToPlay = true;
696  }
697
698  onButtonClick(event) {
699    super.onButtonClick(event);
700    const isFirstSlide = this.currentPage === 1;
701    const isLastSlide = this.currentPage === this.sliderItemsToShow.length;
702
703    if (!isFirstSlide && !isLastSlide) return;
704
705    if (isFirstSlide && event.currentTarget.name === 'previous') {
706      this.slideScrollPosition = this.slider.scrollLeft + this.sliderFirstItemNode.clientWidth * this.sliderItemsToShow.length;
707    } else if (isLastSlide && event.currentTarget.name === 'next') {
708      this.slideScrollPosition = 0;
709    }
710    this.slider.scrollTo({
711      left: this.slideScrollPosition
712    });
713  }
714
715  update() {
716    super.update();
717    this.sliderControlButtons = this.querySelectorAll('.slider-counter__link');
718    this.prevButton.removeAttribute('disabled');
719
720    if (!this.sliderControlButtons.length) return;
721
722    this.sliderControlButtons.forEach(link => {
723      link.classList.remove('slider-counter__link--active');
724      link.removeAttribute('aria-current');
725    });
726    this.sliderControlButtons[this.currentPage - 1].classList.add('slider-counter__link--active');
727    this.sliderControlButtons[this.currentPage - 1].setAttribute('aria-current', true);
728  }
729
730  autoPlayToggle() {
731    this.togglePlayButtonState(this.autoplayButtonIsSetToPlay);
732    this.autoplayButtonIsSetToPlay ? this.pause() : this.play();
733    this.autoplayButtonIsSetToPlay = !this.autoplayButtonIsSetToPlay;
734  }
735
736  focusOutHandling(event) {
737    const focusedOnAutoplayButton = event.target === this.sliderAutoplayButton || this.sliderAutoplayButton.contains(event.target);
738    if (!this.autoplayButtonIsSetToPlay || focusedOnAutoplayButton) return;
739    this.play();
740  }
741
742  focusInHandling(event) {
743    const focusedOnAutoplayButton = event.target === this.sliderAutoplayButton || this.sliderAutoplayButton.contains(event.target);
744    if (focusedOnAutoplayButton && this.autoplayButtonIsSetToPlay) {
745      this.play();
746    } else if (this.autoplayButtonIsSetToPlay) {
747      this.pause();
748    }
749  }
750
751  play() {
752    this.slider.setAttribute('aria-live', 'off');
753    clearInterval(this.autoplay);
754    this.autoplay = setInterval(this.autoRotateSlides.bind(this), this.autoplaySpeed);
755  }
756
757  pause() {
758    this.slider.setAttribute('aria-live', 'polite');
759    clearInterval(this.autoplay);
760  }
761
762  togglePlayButtonState(pauseAutoplay) {
763    if (pauseAutoplay) {
764      this.sliderAutoplayButton.classList.add('slideshow__autoplay--paused');
765      this.sliderAutoplayButton.setAttribute('aria-label', window.accessibilityStrings.playSlideshow);
766    } else {
767      this.sliderAutoplayButton.classList.remove('slideshow__autoplay--paused');
768      this.sliderAutoplayButton.setAttribute('aria-label', window.accessibilityStrings.pauseSlideshow);
769    }
770  }
771
772  autoRotateSlides() {
773    const slideScrollPosition = this.currentPage === this.sliderItems.length ? 0 : this.slider.scrollLeft + this.slider.querySelector('.slideshow__slide').clientWidth;
774    this.slider.scrollTo({
775      left: slideScrollPosition
776    });
777  }
778
779  setSlideVisibility() {
780    this.sliderItemsToShow.forEach((item, index) => {
781      const linkElements = item.querySelectorAll('a');
782      if (index === this.currentPage - 1) {
783        if (linkElements.length) linkElements.forEach(button => {
784          button.removeAttribute('tabindex');
785        });
786        item.setAttribute('aria-hidden', 'false');
787        item.removeAttribute('tabindex');
788      } else {
789        if (linkElements.length) linkElements.forEach(button => {
790          button.setAttribute('tabindex', '-1');
791        });
792        item.setAttribute('aria-hidden', 'true');
793        item.setAttribute('tabindex', '-1');
794      }
795    });
796  }
797
798  linkToSlide(event) {
799    event.preventDefault();
800    const slideScrollPosition = this.slider.scrollLeft + this.sliderFirstItemNode.clientWidth * (this.sliderControlLinksArray.indexOf(event.currentTarget) + 1 - this.currentPage);
801    this.slider.scrollTo({
802      left: slideScrollPosition
803    });
804  }
805}
806
807customElements.define('slideshow-component', SlideshowComponent);
808
809class VariantSelects extends HTMLElement {
810  constructor() {
811    super();
812    this.addEventListener('change', this.onVariantChange);
813  }
814
815  onVariantChange() {
816    this.updateOptions();
817    this.updateMasterId();
818    this.toggleAddButton(true, '', false);
819    this.updatePickupAvailability();
820    this.removeErrorMessage();
821    this.updateVariantStatuses();
822
823    if (!this.currentVariant) {
824      this.toggleAddButton(true, '', true);
825      this.setUnavailable();
826    } else {
827      this.updateMedia();
828      this.updateURL();
829      this.updateVariantInput();
830      this.renderProductInfo();
831      this.updateShareUrl();
832    }
833  }
834
835  updateOptions() {
836    this.options = Array.from(this.querySelectorAll('select'), (select) => select.value);
837  }
838
839  updateMasterId() {
840    this.currentVariant = this.getVariantData().find((variant) => {
841      return !variant.options.map((option, index) => {
842        return this.options[index] === option;
843      }).includes(false);
844    });
845  }
846
847  updateMedia() {
848    if (!this.currentVariant) return;
849    if (!this.currentVariant.featured_media) return;
850
851    const mediaGalleries = document.querySelectorAll(`[id^="MediaGallery-${this.dataset.section}"]`);
852    mediaGalleries.forEach(mediaGallery => mediaGallery.setActiveMedia(`${this.dataset.section}-${this.currentVariant.featured_media.id}`, true));
853
854    const modalContent = document.querySelector(`#ProductModal-${this.dataset.section} .product-media-modal__content`);
855    if (!modalContent) return;
856    const newMediaModal = modalContent.querySelector( `[data-media-id="${this.currentVariant.featured_media.id}"]`);
857    modalContent.prepend(newMediaModal);
858  }
859
860  updateURL() {
861    if (!this.currentVariant || this.dataset.updateUrl === 'false') return;
862    window.history.propertyIsEnumerable({ }, '', `${this.dataset.url}?variant=${this.currentVariant.id}`);
863  }
864
865  updateShareUrl() {
866    const shareButton = document.getElementById(`Share-${this.dataset.section}`);
867    if (!shareButton || !shareButton.updateUrl) return;
868    shareButton.updateUrl(`${window.shopUrl}${this.dataset.url}?variant=${this.currentVariant.id}`);
869  }
870
871  updateVariantInput() {
872    const productForms = document.querySelectorAll(`#product-form-${this.dataset.section}, #product-form-installment-${this.dataset.section}`);
873    productForms.forEach((productForm) => {
874      const input = productForm.querySelector('input[name="id"]');
875      input.value = this.currentVariant.id;
876      input.dispatchEvent(new Event('change', { bubbles: true }));
877    });
878  }
879
880  updateVariantStatuses() {
881    const selectedOptionOneVariants = this.variantData.filter(variant => this.querySelector(':checked').value === variant.option1);
882    const inputWrappers = [...this.querySelectorAll('.product-form__input')];
883    inputWrappers.forEach((option, index) => {
884      if (index === 0) return;
885      const optionInputs = [...option.querySelectorAll('input[type="radio"], option')]
886      const previousOptionSelected = inputWrappers[index - 1].querySelector(':checked').value;
887      const availableOptionInputsValue = selectedOptionOneVariants.filter(variant => variant.available && variant[`option${ index }`] === previousOptionSelected).map(variantOption => variantOption[`option${ index + 1 }`]);
888      this.setInputAvailability(optionInputs, availableOptionInputsValue)
889    });
890  }
891
892  setInputAvailability(listOfOptions, listOfAvailableOptions) {
893    listOfOptions.forEach(input => {
894      if (listOfAvailableOptions.includes(input.getAttribute('value'))) {
895        input.innerText = input.getAttribute('value');
896      } else {
897        input.innerText = window.variantStrings.unavailable_with_option.replace('[value]', input.getAttribute('value'));
898      }
899    });
900  }
901
902  updatePickupAvailability() {
903    const pickUpAvailability = document.querySelector('pickup-availability');
904    if (!pickUpAvailability) return;
905
906    if (this.currentVariant && this.currentVariant.available) {
907      pickUpAvailability.fetchAvailability(this.currentVariant.id);
908    } else {
909      pickUpAvailability.removeAttribute('available');
910      pickUpAvailability.innerHTML = '';
911    }
912  }
913
914  removeErrorMessage() {
915    const section = this.closest('section');
916    if (!section) return;
917
918    const productForm = section.querySelector('product-form');
919    if (productForm) productForm.handleErrorMessage();
920  }
921
922  renderProductInfo() {
923    const requestedVariantId = this.currentVariant.id;
924    const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section;
925
926    fetch(`${this.dataset.url}?variant=${requestedVariantId}&section_id=${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}`)
927      .then((response) => response.text())
928      .then((responseText) => {
929        // prevent unnecessary ui changes from abandoned selections
930        if (this.currentVariant.id !== requestedVariantId) return;
931
932        const html = new DOMParser().parseFromString(responseText, 'text/html')
933        const destination = document.getElementById(`price-${this.dataset.section}`);
934        const source = html.getElementById(`price-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}`);
935        const skuSource = html.getElementById(`Sku-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}`);
936        const skuDestination = document.getElementById(`Sku-${this.dataset.section}`);
937        const inventorySource = html.getElementById(`Inventory-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}`);
938        const inventoryDestination = document.getElementById(`Inventory-${this.dataset.section}`);
939
940        if (source && destination) destination.innerHTML = source.innerHTML;
941        if (inventorySource && inventoryDestination) inventoryDestination.innerHTML = inventorySource.innerHTML;
942        if (skuSource && skuDestination) {
943          skuDestination.innerHTML = skuSource.innerHTML;
944          skuDestination.classList.toggle('visibility-hidden', skuSource.classList.contains('visibility-hidden'));
945        }
946
947        const price = document.getElementById(`price-${this.dataset.section}`);
948
949        if (price) price.classList.remove('visibility-hidden');
950
951        if (inventoryDestination) inventoryDestination.classList.toggle('visibility-hidden', inventorySource.innerText === '');
952
953        const addButtonUpdated = html.getElementById(`ProductSubmitButton-${sectionId}`);
954        this.toggleAddButton(addButtonUpdated ? addButtonUpdated.hasAttribute('disabled') : true, window.variantStrings.soldOut);
955
956        publish(PUB_SUB_EVENTS.variantChange, {data: {
957          sectionId,
958          html,
959          variant: this.currentVariant
960        }});
961      });
962  }
963
964  toggleAddButton(disable = true, text, modifyClass = true) {
965    const productForm = document.getElementById(`product-form-${this.dataset.section}`);
966    if (!productForm) return;
967    const addButton = productForm.querySelector('[name="add"]');
968    const addButtonText = productForm.querySelector('[name="add"] > span');
969    if (!addButton) return;
970
971    if (disable) {
972      addButton.setAttribute('disabled', 'disabled');
973      if (text) addButtonText.textContent = text;
974    } else {
975      addButton.removeAttribute('disabled');
976      addButtonText.textContent = window.variantStrings.addToCart;
977    }
978
979    if (!modifyClass) return;
980  }
981
982  setUnavailable() {
983    const button = document.getElementById(`product-form-${this.dataset.section}`);
984    const addButton = button.querySelector('[name="add"]');
985    const addButtonText = button.querySelector('[name="add"] > span');
986    const price = document.getElementById(`price-${this.dataset.section}`);
987    const inventory = document.getElementById(`Inventory-${this.dataset.section}`);
988    const sku = document.getElementById(`Sku-${this.dataset.section}`);
989
990    if (!addButton) return;
991    addButtonText.textContent = window.variantStrings.unavailable;
992    if (price) price.classList.add('visibility-hidden');
993    if (inventory) inventory.classList.add('visibility-hidden');
994    if (sku) sku.classList.add('visibility-hidden');
995  }
996
997  getVariantData() {
998    this.variantData = this.variantData || JSON.parse(this.querySelector('[type="application/json"]').textContent);
999    return this.variantData;
1000  }
1001}
1002
1003customElements.define('variant-selects', VariantSelects);
1004
1005class VariantRadios extends VariantSelects {
1006  constructor() {
1007    super();
1008  }
1009
1010  setInputAvailability(listOfOptions, listOfAvailableOptions) {
1011    listOfOptions.forEach(input => {
1012      if (listOfAvailableOptions.includes(input.getAttribute('value'))) {
1013        input.classList.remove('disabled');
1014      } else {
1015        input.classList.add('disabled');
1016      }
1017    });
1018  }
1019
1020  updateOptions() {
1021    const fieldsets = Array.from(this.querySelectorAll('fieldset'));
1022    this.options = fieldsets.map((fieldset) => {
1023      return Array.from(fieldset.querySelectorAll('input')).find((radio) => radio.checked).value;
1024    });
1025  }
1026}
1027
1028customElements.define('variant-radios', VariantRadios);
1029
1030class ProductRecommendations extends HTMLElement {
1031  constructor() {
1032    super();
1033  }
1034
1035  connectedCallback() {
1036    const handleIntersection = (entries, observer) => {
1037      if (!entries[0].isIntersecting) return;
1038      observer.unobserve(this);
1039
1040      fetch(this.dataset.url)
1041        .then(response => response.text())
1042        .then(text => {
1043          const html = document.createElement('div');
1044          html.innerHTML = text;
1045          const recommendations = html.querySelector('product-recommendations');
1046
1047          if (recommendations && recommendations.innerHTML.trim().length) {
1048            this.innerHTML = recommendations.innerHTML;
1049          }
1050
1051          if (!this.querySelector('slideshow-component') && this.classList.contains('complementary-products')) {
1052            this.remove();
1053          }
1054
1055          if (html.querySelector('.grid__item')) {
1056            this.classList.add('product-recommendations--loaded');
1057          }
1058        })
1059        .catch(e => {
1060          console.error(e);
1061        });
1062    }
1063
1064    new IntersectionObserver(handleIntersection.bind(this), {rootMargin: '0px 0px 400px 0px'}).observe(this);
1065  }
1066}
1067
1068customElements.define('product-recommendations', ProductRecommendations);

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.