Advertisement
Guest User

theme.js

a guest
Jan 20th, 2021
602
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 278.63 KB | None | 0 0
  1. window.theme = window.theme || {};
  2.  
  3. /* ================ SLATE ================ */
  4. window.theme = window.theme || {};
  5.  
  6. theme.Sections = function Sections() {
  7.   this.constructors = {};
  8.   this.instances = [];
  9.  
  10.   document.addEventListener(
  11.     'shopify:section:load',
  12.     this._onSectionLoad.bind(this)
  13.   );
  14.   document.addEventListener(
  15.     'shopify:section:unload',
  16.     this._onSectionUnload.bind(this)
  17.   );
  18.   document.addEventListener(
  19.     'shopify:section:select',
  20.     this._onSelect.bind(this)
  21.   );
  22.   document.addEventListener(
  23.     'shopify:section:deselect',
  24.     this._onDeselect.bind(this)
  25.   );
  26.   document.addEventListener(
  27.     'shopify:block:select',
  28.     this._onBlockSelect.bind(this)
  29.   );
  30.   document.addEventListener(
  31.     'shopify:block:deselect',
  32.     this._onBlockDeselect.bind(this)
  33.   );
  34. };
  35.  
  36. theme.Sections.prototype = Object.assign({}, theme.Sections.prototype, {
  37.   _createInstance: function(container, constructor) {
  38.     var id = container.getAttribute('data-section-id');
  39.     var type = container.getAttribute('data-section-type');
  40.  
  41.     constructor = constructor || this.constructors[type];
  42.  
  43.     if (typeof constructor === 'undefined') {
  44.       return;
  45.     }
  46.  
  47.     var instance = Object.assign(new constructor(container), {
  48.       id: id,
  49.       type: type,
  50.       container: container
  51.     });
  52.  
  53.     this.instances.push(instance);
  54.   },
  55.  
  56.   _onSectionLoad: function(evt) {
  57.     var container = document.querySelector(
  58.       '[data-section-id="' + evt.detail.sectionId + '"]'
  59.     );
  60.  
  61.     if (container) {
  62.       this._createInstance(container);
  63.     }
  64.   },
  65.  
  66.   _onSectionUnload: function(evt) {
  67.     this.instances = this.instances.filter(function(instance) {
  68.       var isEventInstance = instance.id === evt.detail.sectionId;
  69.  
  70.       if (isEventInstance) {
  71.         if (typeof instance.onUnload === 'function') {
  72.           instance.onUnload(evt);
  73.         }
  74.       }
  75.  
  76.       return !isEventInstance;
  77.     });
  78.   },
  79.  
  80.   _onSelect: function(evt) {
  81.     // eslint-disable-next-line no-shadow
  82.     var instance = this.instances.find(function(instance) {
  83.       return instance.id === evt.detail.sectionId;
  84.     });
  85.  
  86.     if (
  87.       typeof instance !== 'undefined' &&
  88.      typeof instance.onSelect === 'function'
  89.    ) {
  90.      instance.onSelect(evt);
  91.     }
  92.   },
  93.  
  94.   _onDeselect: function(evt) {
  95.     // eslint-disable-next-line no-shadow
  96.     var instance = this.instances.find(function(instance) {
  97.       return instance.id === evt.detail.sectionId;
  98.     });
  99.  
  100.     if (
  101.       typeof instance !== 'undefined' &&
  102.      typeof instance.onDeselect === 'function'
  103.    ) {
  104.      instance.onDeselect(evt);
  105.     }
  106.   },
  107.  
  108.   _onBlockSelect: function(evt) {
  109.     // eslint-disable-next-line no-shadow
  110.     var instance = this.instances.find(function(instance) {
  111.       return instance.id === evt.detail.sectionId;
  112.     });
  113.  
  114.     if (
  115.       typeof instance !== 'undefined' &&
  116.      typeof instance.onBlockSelect === 'function'
  117.    ) {
  118.      instance.onBlockSelect(evt);
  119.     }
  120.   },
  121.  
  122.   _onBlockDeselect: function(evt) {
  123.     // eslint-disable-next-line no-shadow
  124.     var instance = this.instances.find(function(instance) {
  125.       return instance.id === evt.detail.sectionId;
  126.     });
  127.  
  128.     if (
  129.       typeof instance !== 'undefined' &&
  130.      typeof instance.onBlockDeselect === 'function'
  131.    ) {
  132.      instance.onBlockDeselect(evt);
  133.     }
  134.   },
  135.  
  136.   register: function(type, constructor) {
  137.     this.constructors[type] = constructor;
  138.  
  139.     document.querySelectorAll('[data-section-type="' + type + '"]').forEach(
  140.       function(container) {
  141.         this._createInstance(container, constructor);
  142.       }.bind(this)
  143.     );
  144.   }
  145. });
  146.  
  147. window.slate = window.slate || {};
  148.  
  149. /**
  150.  * Slate utilities
  151.  * -----------------------------------------------------------------------------
  152.  * A collection of useful utilities to help build your theme
  153.  *
  154.  *
  155.  * @namespace utils
  156.  */
  157.  
  158. slate.utils = {
  159.   /**
  160.    * Get the query params in a Url
  161.    * Ex
  162.    * https://mysite.com/search?q=noodles&b
  163.   * getParameterByName('q') = "noodles"
  164.   * getParameterByName('b') = "" (empty value)
  165.   * getParameterByName('test') = null (absent)
  166.   */
  167.  getParameterByName: function(name, url) {
  168.    if (!url) url = window.location.href;
  169.     name = name.replace(/[[\]]/g, '\\$&');
  170.     var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
  171.      results = regex.exec(url);
  172.     if (!results) return null;
  173.     if (!results[2]) return '';
  174.     return decodeURIComponent(results[2].replace(/\+/g, ' '));
  175.   },
  176.  
  177.   resizeSelects: function(selects) {
  178.     selects.forEach(function(select) {
  179.       var arrowWidth = 55;
  180.  
  181.       var test = document.createElement('span');
  182.       test.innerHTML = select.selectedOptions[0].label;
  183.  
  184.       document.querySelector('.site-footer').appendChild(test);
  185.  
  186.       var width = test.offsetWidth + arrowWidth;
  187.       test.remove();
  188.  
  189.       select.style.width = width + 'px';
  190.     });
  191.   },
  192.  
  193.   keyboardKeys: {
  194.     TAB: 9,
  195.     ENTER: 13,
  196.     ESCAPE: 27,
  197.     LEFTARROW: 37,
  198.     RIGHTARROW: 39
  199.   }
  200. };
  201.  
  202. window.slate = window.slate || {};
  203.  
  204. /**
  205.  * iFrames
  206.  * -----------------------------------------------------------------------------
  207.  * Wrap videos in div to force responsive layout.
  208.  *
  209.  * @namespace iframes
  210.  */
  211.  
  212. slate.rte = {
  213.   /**
  214.    * Wrap tables in a container div to make them scrollable when needed
  215.    *
  216.    * @param {object} options - Options to be used
  217.    * @param {NodeList} options.tables - Elements of the table(s) to wrap
  218.    * @param {string} options.tableWrapperClass - table wrapper class name
  219.    */
  220.   wrapTable: function(options) {
  221.     options.tables.forEach(function(table) {
  222.       var wrapper = document.createElement('div');
  223.       wrapper.classList.add(options.tableWrapperClass);
  224.  
  225.       table.parentNode.insertBefore(wrapper, table);
  226.       wrapper.appendChild(table);
  227.     });
  228.   },
  229.  
  230.   /**
  231.    * Wrap iframes in a container div to make them responsive
  232.    *
  233.    * @param {object} options - Options to be used
  234.    * @param {NodeList} options.iframes - Elements of the iframe(s) to wrap
  235.    * @param {string} options.iframeWrapperClass - class name used on the wrapping div
  236.    */
  237.   wrapIframe: function(options) {
  238.     options.iframes.forEach(function(iframe) {
  239.       var wrapper = document.createElement('div');
  240.       wrapper.classList.add(options.iframeWrapperClass);
  241.  
  242.       iframe.parentNode.insertBefore(wrapper, iframe);
  243.       wrapper.appendChild(iframe);
  244.  
  245.       iframe.src = iframe.src;
  246.     });
  247.   }
  248. };
  249.  
  250. window.slate = window.slate || {};
  251.  
  252. /**
  253.  * A11y Helpers
  254.  * -----------------------------------------------------------------------------
  255.  * A collection of useful functions that help make your theme more accessible
  256.  * to users with visual impairments.
  257.  *
  258.  *
  259.  * @namespace a11y
  260.  */
  261.  
  262. slate.a11y = {
  263.   state: {
  264.     firstFocusable: null,
  265.     lastFocusable: null
  266.   },
  267.   /**
  268.    * For use when focus shifts to a container rather than a link
  269.    * eg for In-page links, after scroll, focus shifts to content area so that
  270.    * next `tab` is where user expects
  271.    *
  272.    * @param {HTMLElement} element - The element to be acted upon
  273.    */
  274.   pageLinkFocus: function(element) {
  275.     if (!element) return;
  276.     var focusClass = 'js-focus-hidden';
  277.  
  278.     element.setAttribute('tabIndex', '-1');
  279.     element.focus();
  280.     element.classList.add(focusClass);
  281.     element.addEventListener('blur', callback, { once: true });
  282.  
  283.     function callback() {
  284.       element.classList.remove(focusClass);
  285.       element.removeAttribute('tabindex');
  286.     }
  287.   },
  288.  
  289.   /**
  290.    * Traps the focus in a particular container
  291.    *
  292.    * @param {object} options - Options to be used
  293.    * @param {HTMLElement} options.container - Container to trap focus within
  294.    * @param {HTMLElement} options.elementToFocus - Element to be focused when focus leaves container
  295.    */
  296.   trapFocus: function(options) {
  297.     var focusableElements = Array.from(
  298.       options.container.querySelectorAll(
  299.         'button, [href], input, select, textarea, [tabindex]:not([tabindex^="-"])'
  300.       )
  301.     ).filter(function(element) {
  302.       var width = element.offsetWidth;
  303.       var height = element.offsetHeight;
  304.  
  305.       return (
  306.         width !== 0 &&
  307.        height !== 0 &&
  308.        getComputedStyle(element).getPropertyValue('display') !== 'none'
  309.      );
  310.     });
  311.  
  312.     this.state.firstFocusable = focusableElements[0];
  313.     this.state.lastFocusable = focusableElements[focusableElements.length - 1];
  314.  
  315.     if (!options.elementToFocus) {
  316.       options.elementToFocus = options.container;
  317.     }
  318.  
  319.     options.container.setAttribute('tabindex', '-1');
  320.     options.elementToFocus.focus();
  321.  
  322.     this._setupHandlers();
  323.  
  324.     document.addEventListener('focusin', this._onFocusInHandler);
  325.     document.addEventListener('focusout', this._onFocusOutHandler);
  326.   },
  327.  
  328.   _setupHandlers: function() {
  329.     if (!this._onFocusInHandler) {
  330.       this._onFocusInHandler = this._onFocusIn.bind(this);
  331.     }
  332.  
  333.     if (!this._onFocusOutHandler) {
  334.       this._onFocusOutHandler = this._onFocusIn.bind(this);
  335.     }
  336.  
  337.     if (!this._manageFocusHandler) {
  338.       this._manageFocusHandler = this._manageFocus.bind(this);
  339.     }
  340.   },
  341.  
  342.   _onFocusOut: function() {
  343.     document.removeEventListener('keydown', this._manageFocusHandler);
  344.   },
  345.  
  346.   _onFocusIn: function(evt) {
  347.     if (
  348.       evt.target !== this.state.lastFocusable &&
  349.      evt.target !== this.state.firstFocusable
  350.    )
  351.      return;
  352.  
  353.     document.addEventListener('keydown', this._manageFocusHandler);
  354.   },
  355.  
  356.   _manageFocus: function(evt) {
  357.     if (evt.keyCode !== slate.utils.keyboardKeys.TAB) return;
  358.  
  359.     /**
  360.      * On the last focusable element and tab forward,
  361.      * focus the first element.
  362.      */
  363.     if (evt.target === this.state.lastFocusable && !evt.shiftKey) {
  364.      evt.preventDefault();
  365.       this.state.firstFocusable.focus();
  366.     }
  367.     /**
  368.      * On the first focusable element and tab backward,
  369.      * focus the last element.
  370.      */
  371.     if (evt.target === this.state.firstFocusable && evt.shiftKey) {
  372.      evt.preventDefault();
  373.       this.state.lastFocusable.focus();
  374.     }
  375.   },
  376.  
  377.   /**
  378.    * Removes the trap of focus in a particular container
  379.    *
  380.    * @param {object} options - Options to be used
  381.    * @param {HTMLElement} options.container - Container to trap focus within
  382.    */
  383.   removeTrapFocus: function(options) {
  384.     if (options.container) {
  385.       options.container.removeAttribute('tabindex');
  386.     }
  387.     document.removeEventListener('focusin', this._onFocusInHandler);
  388.   },
  389.  
  390.   /**
  391.    * Add aria-describedby attribute to external and new window links
  392.    *
  393.    * @param {object} options - Options to be used
  394.    * @param {object} options.messages - Custom messages to be used
  395.    * @param {HTMLElement} options.links - Specific links to be targeted
  396.    */
  397.   accessibleLinks: function(options) {
  398.     var body = document.querySelector('body');
  399.  
  400.     var idSelectors = {
  401.       newWindow: 'a11y-new-window-message',
  402.       external: 'a11y-external-message',
  403.       newWindowExternal: 'a11y-new-window-external-message'
  404.     };
  405.  
  406.     if (options.links === undefined || !options.links.length) {
  407.       options.links = document.querySelectorAll(
  408.         'a[href]:not([aria-describedby])'
  409.       );
  410.     }
  411.  
  412.     function generateHTML(customMessages) {
  413.       if (typeof customMessages !== 'object') {
  414.         customMessages = {};
  415.       }
  416.  
  417.       var messages = Object.assign(
  418.         {
  419.           newWindow: 'Opens in a new window.',
  420.           external: 'Opens external website.',
  421.           newWindowExternal: 'Opens external website in a new window.'
  422.         },
  423.         customMessages
  424.       );
  425.  
  426.       var container = document.createElement('ul');
  427.       var htmlMessages = '';
  428.  
  429.       for (var message in messages) {
  430.         htmlMessages +=
  431.           '<li id=' + idSelectors[message] + '>' + messages[message] + '</li>';
  432.       }
  433.  
  434.       container.setAttribute('hidden', true);
  435.       container.innerHTML = htmlMessages;
  436.  
  437.       body.appendChild(container);
  438.     }
  439.  
  440.     function _externalSite(link) {
  441.       var hostname = window.location.hostname;
  442.  
  443.       return link.hostname !== hostname;
  444.     }
  445.  
  446.     options.links.forEach(function(link) {
  447.       var target = link.getAttribute('target');
  448.       var rel = link.getAttribute('rel');
  449.       var isExternal = _externalSite(link);
  450.       var isTargetBlank = target === '_blank';
  451.  
  452.       if (isExternal) {
  453.         link.setAttribute('aria-describedby', idSelectors.external);
  454.       }
  455.  
  456.       if (isTargetBlank) {
  457.         if (!rel || rel.indexOf('noopener') === -1) {
  458.           var relValue = rel === undefined ? '' : rel + ' ';
  459.           relValue = relValue + 'noopener';
  460.           link.setAttribute('rel', relValue);
  461.         }
  462.  
  463.         link.setAttribute('aria-describedby', idSelectors.newWindow);
  464.       }
  465.  
  466.       if (isExternal && isTargetBlank) {
  467.        link.setAttribute('aria-describedby', idSelectors.newWindowExternal);
  468.       }
  469.     });
  470.  
  471.     generateHTML(options.messages);
  472.   }
  473. };
  474.  
  475. /**
  476.  * Image Helper Functions
  477.  * -----------------------------------------------------------------------------
  478.  * A collection of functions that help with basic image operations.
  479.  *
  480.  */
  481.  
  482. theme.Images = (function() {
  483.   /**
  484.    * Preloads an image in memory and uses the browsers cache to store it until needed.
  485.    *
  486.    * @param {Array} images - A list of image urls
  487.    * @param {String} size - A shopify image size attribute
  488.    */
  489.  
  490.   function preload(images, size) {
  491.     if (typeof images === 'string') {
  492.       images = [images];
  493.     }
  494.  
  495.     for (var i = 0; i < images.length; i++) {
  496.      var image = images[i];
  497.      this.loadImage(this.getSizedImageUrl(image, size));
  498.    }
  499.  }
  500.  
  501.  /**
  502.   * Loads and caches an image in the browsers cache.
  503.   * @param {string} path - An image url
  504.   */
  505.  function loadImage(path) {
  506.    new Image().src = path;
  507.  }
  508.  
  509.  /**
  510.   * Swaps the src of an image for another OR returns the imageURL to the callback function
  511.   * @param image
  512.   * @param element
  513.   * @param callback
  514.   */
  515.  function switchImage(image, element, callback) {
  516.    var size = this.imageSize(element.src);
  517.    var imageUrl = this.getSizedImageUrl(image.src, size);
  518.  
  519.    if (callback) {
  520.      callback(imageUrl, image, element); // eslint-disable-line callback-return
  521.    } else {
  522.      element.src = imageUrl;
  523.    }
  524.  }
  525.  
  526.  /**
  527.   * +++ Useful
  528.   * Find the Shopify image attribute size
  529.   *
  530.   * @param {string} src
  531.   * @returns {null}
  532.   */
  533.  function imageSize(src) {
  534.    var match = src.match(
  535.      /.+_((?:pico|icon|thumb|small|compact|medium|large|grande)|\d{1,4}x\d{0,4}|x\d{1,4})[_\\.@]/
  536.    );
  537.  
  538.    if (match !== null) {
  539.      if (match[2] !== undefined) {
  540.        return match[1] + match[2];
  541.      } else {
  542.        return match[1];
  543.      }
  544.    } else {
  545.      return null;
  546.    }
  547.  }
  548.  
  549.  /**
  550.   * +++ Useful
  551.   * Adds a Shopify size attribute to a URL
  552.   *
  553.   * @param src
  554.   * @param size
  555.   * @returns {*}
  556.   */
  557.  function getSizedImageUrl(src, size) {
  558.    if (size === null) {
  559.      return src;
  560.    }
  561.  
  562.    if (size === 'master') {
  563.      return this.removeProtocol(src);
  564.    }
  565.  
  566.    var match = src.match(
  567.      /\.(jpg|jpeg|gif|png|bmp|bitmap|tiff|tif)(\?v=\d+)?$/i
  568.    );
  569.  
  570.    if (match !== null) {
  571.      var prefix = src.split(match[0]);
  572.      var suffix = match[0];
  573.  
  574.      return this.removeProtocol(prefix[0] + '_' + size + suffix);
  575.    }
  576.  
  577.    return null;
  578.  }
  579.  
  580.  function removeProtocol(path) {
  581.    return path.replace(/http(s)?:/, '');
  582.  }
  583.  
  584.  return {
  585.    preload: preload,
  586.    loadImage: loadImage,
  587.    switchImage: switchImage,
  588.    imageSize: imageSize,
  589.    getSizedImageUrl: getSizedImageUrl,
  590.    removeProtocol: removeProtocol
  591.  };
  592. })();
  593.  
  594. /**
  595. * Currency Helpers
  596. * -----------------------------------------------------------------------------
  597. * A collection of useful functions that help with currency formatting
  598. *
  599. * Current contents
  600. * - formatMoney - Takes an amount in cents and returns it as a formatted dollar value.
  601. *
  602. * Alternatives
  603. * - Accounting.js - http://openexchangerates.github.io/accounting.js/
  604. *
  605. */
  606.  
  607. theme.Currency = (function() {
  608.  var moneyFormat = '${{amount}}'; // eslint-disable-line camelcase
  609.  
  610.  function formatMoney(cents, format) {
  611.    if (typeof cents === 'string') {
  612.      cents = cents.replace('.', '');
  613.    }
  614.    var value = '';
  615.    var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
  616.    var formatString = format || moneyFormat;
  617.  
  618.    function formatWithDelimiters(number, precision, thousands, decimal) {
  619.      thousands = thousands || ',';
  620.      decimal = decimal || '.';
  621.  
  622.      if (isNaN(number) || number === null) {
  623.        return 0;
  624.      }
  625.  
  626.      number = (number / 100.0).toFixed(precision);
  627.  
  628.      var parts = number.split('.');
  629.      var dollarsAmount = parts[0].replace(
  630.        /(\d)(?=(\d\d\d)+(?!\d))/g,
  631.        '$1' + thousands
  632.      );
  633.      var centsAmount = parts[1] ? decimal + parts[1] : '';
  634.  
  635.      return dollarsAmount + centsAmount;
  636.    }
  637.  
  638.    switch (formatString.match(placeholderRegex)[1]) {
  639.      case 'amount':
  640.        value = formatWithDelimiters(cents, 2);
  641.        break;
  642.      case 'amount_no_decimals':
  643.        value = formatWithDelimiters(cents, 0);
  644.        break;
  645.      case 'amount_with_comma_separator':
  646.        value = formatWithDelimiters(cents, 2, '.', ',');
  647.        break;
  648.      case 'amount_no_decimals_with_comma_separator':
  649.        value = formatWithDelimiters(cents, 0, '.', ',');
  650.        break;
  651.      case 'amount_no_decimals_with_space_separator':
  652.        value = formatWithDelimiters(cents, 0, ' ');
  653.        break;
  654.      case 'amount_with_apostrophe_separator':
  655.        value = formatWithDelimiters(cents, 2, "'");
  656.        break;
  657.    }
  658.  
  659.    return formatString.replace(placeholderRegex, value);
  660.  }
  661.  
  662.  return {
  663.    formatMoney: formatMoney
  664.  };
  665. })();
  666.  
  667. /**
  668. * Variant Selection scripts
  669. * ------------------------------------------------------------------------------
  670. *
  671. * Handles change events from the variant inputs in any `cart/add` forms that may
  672. * exist.  Also updates the master select and triggers updates when the variants
  673. * price or image changes.
  674. *
  675. * @namespace variants
  676. */
  677.  
  678. slate.Variants = (function() {
  679.  /**
  680.   * Variant constructor
  681.   *
  682.   * @param {object} options - Settings from `product.js`
  683.   */
  684.  function Variants(options) {
  685.    this.container = options.container;
  686.    this.product = options.product;
  687.    this.originalSelectorId = options.originalSelectorId;
  688.    this.enableHistoryState = options.enableHistoryState;
  689.    this.singleOptions = this.container.querySelectorAll(
  690.      options.singleOptionSelector
  691.    );
  692.    this.currentVariant = this._getVariantFromOptions();
  693.  
  694.    this.singleOptions.forEach(
  695.      function(option) {
  696.        option.addEventListener('change', this._onSelectChange.bind(this));
  697.      }.bind(this)
  698.    );
  699.  }
  700.  
  701.  Variants.prototype = Object.assign({}, Variants.prototype, {
  702.    /**
  703.     * Get the currently selected options from add-to-cart form. Works with all
  704.     * form input elements.
  705.     *
  706.     * @return {array} options - Values of currently selected variants
  707.     */
  708.    _getCurrentOptions: function() {
  709.      var result = [];
  710.  
  711.      this.singleOptions.forEach(function(option) {
  712.        var type = option.getAttribute('type');
  713.        var isRadioOrCheckbox = type === 'radio' || type === 'checkbox';
  714.  
  715.        if (!isRadioOrCheckbox || option.checked) {
  716.          result.push({
  717.            value: option.value,
  718.            index: option.getAttribute('data-index')
  719.          });
  720.        }
  721.      });
  722.  
  723.      return result;
  724.    },
  725.  
  726.    /**
  727.     * Find variant based on selected values.
  728.     *
  729.     * @param  {array} selectedValues - Values of variant inputs
  730.     * @return {object || undefined} found - Variant object from product.variants
  731.     */
  732.    _getVariantFromOptions: function() {
  733.      var selectedValues = this._getCurrentOptions();
  734.      var variants = this.product.variants;
  735.  
  736.      var found = variants.find(function(variant) {
  737.        return selectedValues.every(function(values) {
  738.          return variant[values.index] === values.value;
  739.        });
  740.      });
  741.  
  742.      return found;
  743.    },
  744.  
  745.    /**
  746.     * Event handler for when a variant input changes.
  747.     */
  748.    _onSelectChange: function() {
  749.      var variant = this._getVariantFromOptions();
  750.  
  751.      this.container.dispatchEvent(
  752.        new CustomEvent('variantChange', {
  753.          detail: {
  754.            variant: variant
  755.          },
  756.          bubbles: true,
  757.          cancelable: true
  758.        })
  759.      );
  760.  
  761.      if (!variant) {
  762.        return;
  763.      }
  764.  
  765.      this._updateMasterSelect(variant);
  766.      this._updateImages(variant);
  767.      this._updatePrice(variant);
  768.      this._updateSKU(variant);
  769.      this.currentVariant = variant;
  770.  
  771.      if (this.enableHistoryState) {
  772.        this._updateHistoryState(variant);
  773.      }
  774.    },
  775.  
  776.    /**
  777.     * Trigger event when variant image changes
  778.     *
  779.     * @param  {object} variant - Currently selected variant
  780.     * @return {event}  variantImageChange
  781.     */
  782.    _updateImages: function(variant) {
  783.      var variantImage = variant.featured_image || {};
  784.      var currentVariantImage = this.currentVariant.featured_image || {};
  785.  
  786.      if (
  787.        !variant.featured_image ||
  788.        variantImage.src === currentVariantImage.src
  789.      ) {
  790.        return;
  791.      }
  792.  
  793.      this.container.dispatchEvent(
  794.        new CustomEvent('variantImageChange', {
  795.          detail: {
  796.            variant: variant
  797.          },
  798.          bubbles: true,
  799.          cancelable: true
  800.        })
  801.      );
  802.    },
  803.  
  804.    /**
  805.     * Trigger event when variant price changes.
  806.     *
  807.     * @param  {object} variant - Currently selected variant
  808.     * @return {event} variantPriceChange
  809.     */
  810.    _updatePrice: function(variant) {
  811.      if (
  812.        variant.price === this.currentVariant.price &&
  813.        variant.compare_at_price === this.currentVariant.compare_at_price
  814.      ) {
  815.        return;
  816.      }
  817.  
  818.      this.container.dispatchEvent(
  819.        new CustomEvent('variantPriceChange', {
  820.          detail: {
  821.            variant: variant
  822.          },
  823.          bubbles: true,
  824.          cancelable: true
  825.        })
  826.      );
  827.    },
  828.  
  829.    /**
  830.     * Trigger event when variant sku changes.
  831.     *
  832.     * @param  {object} variant - Currently selected variant
  833.     * @return {event} variantSKUChange
  834.     */
  835.    _updateSKU: function(variant) {
  836.      if (variant.sku === this.currentVariant.sku) {
  837.        return;
  838.      }
  839.  
  840.      this.container.dispatchEvent(
  841.        new CustomEvent('variantSKUChange', {
  842.          detail: {
  843.            variant: variant
  844.          },
  845.          bubbles: true,
  846.          cancelable: true
  847.        })
  848.      );
  849.    },
  850.  
  851.    /**
  852.     * Update history state for product deeplinking
  853.     *
  854.     * @param  {variant} variant - Currently selected variant
  855.     * @return {k}         [description]
  856.     */
  857.    _updateHistoryState: function(variant) {
  858.      if (!history.replaceState || !variant) {
  859.        return;
  860.      }
  861.  
  862.      var newurl =
  863.        window.location.protocol +
  864.        '//' +
  865.        window.location.host +
  866.        window.location.pathname +
  867.        '?variant=' +
  868.        variant.id;
  869.      window.history.replaceState({ path: newurl }, '', newurl);
  870.    },
  871.  
  872.    /**
  873.     * Update hidden master select of variant change
  874.     *
  875.     * @param  {variant} variant - Currently selected variant
  876.     */
  877.    _updateMasterSelect: function(variant) {
  878.      var masterSelect = this.container.querySelector(this.originalSelectorId);
  879.  
  880.      if (!masterSelect) return;
  881.      masterSelect.value = variant.id;
  882.    }
  883.  });
  884.  
  885.  return Variants;
  886. })();
  887.  
  888. this.Shopify = this.Shopify || {};
  889. this.Shopify.theme = this.Shopify.theme || {};
  890. this.Shopify.theme.PredictiveSearch = (function() {
  891.  'use strict';
  892.  
  893.  function validateQuery(query) {
  894.    var error;
  895.  
  896.    if (query === null || query === undefined) {
  897.      error = new TypeError("'query' is missing");
  898.      error.type = 'argument';
  899.      throw error;
  900.    }
  901.  
  902.    if (typeof query !== 'string') {
  903.      error = new TypeError("'query' is not a string");
  904.      error.type = 'argument';
  905.      throw error;
  906.    }
  907.  }
  908.  
  909.  function GenericError() {
  910.    var error = Error.call(this);
  911.  
  912.    error.name = 'Server error';
  913.    error.message = 'Something went wrong on the server';
  914.    error.status = 500;
  915.  
  916.    return error;
  917.  }
  918.  
  919.  function NotFoundError(status) {
  920.    var error = Error.call(this);
  921.  
  922.    error.name = 'Not found';
  923.    error.message = 'Not found';
  924.    error.status = status;
  925.  
  926.    return error;
  927.  }
  928.  
  929.  function ServerError() {
  930.    var error = Error.call(this);
  931.  
  932.    error.name = 'Server error';
  933.    error.message = 'Something went wrong on the server';
  934.    error.status = 500;
  935.  
  936.    return error;
  937.  }
  938.  
  939.  function ContentTypeError(status) {
  940.    var error = Error.call(this);
  941.  
  942.    error.name = 'Content-Type error';
  943.    error.message = 'Content-Type was not provided or is of wrong type';
  944.    error.status = status;
  945.  
  946.    return error;
  947.  }
  948.  
  949.  function JsonParseError(status) {
  950.    var error = Error.call(this);
  951.  
  952.    error.name = 'JSON parse error';
  953.    error.message = 'JSON syntax error';
  954.    error.status = status;
  955.  
  956.    return error;
  957.  }
  958.  
  959.  function ThrottledError(status, name, message, retryAfter) {
  960.    var error = Error.call(this);
  961.  
  962.    error.name = name;
  963.    error.message = message;
  964.    error.status = status;
  965.    error.retryAfter = retryAfter;
  966.  
  967.    return error;
  968.  }
  969.  
  970.  function InvalidParameterError(status, name, message) {
  971.    var error = Error.call(this);
  972.  
  973.    error.name = name;
  974.    error.message = message;
  975.    error.status = status;
  976.  
  977.    return error;
  978.  }
  979.  
  980.  function ExpectationFailedError(status, name, message) {
  981.    var error = Error.call(this);
  982.  
  983.    error.name = name;
  984.    error.message = message;
  985.    error.status = status;
  986.  
  987.    return error;
  988.  }
  989.  
  990.  function request(searchUrl, queryParams, query, onSuccess, onError) {
  991.    var xhr = new XMLHttpRequest();
  992.    var route = searchUrl + '/suggest.json';
  993.  
  994.    xhr.onreadystatechange = function() {
  995.      if (xhr.readyState !== XMLHttpRequest.DONE) {
  996.        return;
  997.      }
  998.  
  999.      var contentType = xhr.getResponseHeader('Content-Type');
  1000.  
  1001.      if (xhr.status >= 500) {
  1002.         onError(new ServerError());
  1003.  
  1004.         return;
  1005.       }
  1006.  
  1007.       if (xhr.status === 404) {
  1008.         onError(new NotFoundError(xhr.status));
  1009.  
  1010.         return;
  1011.       }
  1012.  
  1013.       if (
  1014.         typeof contentType !== 'string' ||
  1015.         contentType.toLowerCase().match('application/json') === null
  1016.       ) {
  1017.         onError(new ContentTypeError(xhr.status));
  1018.  
  1019.         return;
  1020.       }
  1021.  
  1022.       if (xhr.status === 417) {
  1023.         try {
  1024.           var invalidParameterJson = JSON.parse(xhr.responseText);
  1025.  
  1026.           onError(
  1027.             new InvalidParameterError(
  1028.               xhr.status,
  1029.               invalidParameterJson.message,
  1030.               invalidParameterJson.description
  1031.             )
  1032.           );
  1033.         } catch (error) {
  1034.           onError(new JsonParseError(xhr.status));
  1035.         }
  1036.  
  1037.         return;
  1038.       }
  1039.  
  1040.       if (xhr.status === 422) {
  1041.         try {
  1042.           var expectationFailedJson = JSON.parse(xhr.responseText);
  1043.  
  1044.           onError(
  1045.             new ExpectationFailedError(
  1046.               xhr.status,
  1047.               expectationFailedJson.message,
  1048.               expectationFailedJson.description
  1049.             )
  1050.           );
  1051.         } catch (error) {
  1052.           onError(new JsonParseError(xhr.status));
  1053.         }
  1054.  
  1055.         return;
  1056.       }
  1057.  
  1058.       if (xhr.status === 429) {
  1059.         try {
  1060.           var throttledJson = JSON.parse(xhr.responseText);
  1061.  
  1062.           onError(
  1063.             new ThrottledError(
  1064.               xhr.status,
  1065.               throttledJson.message,
  1066.               throttledJson.description,
  1067.               xhr.getResponseHeader('Retry-After')
  1068.             )
  1069.           );
  1070.         } catch (error) {
  1071.           onError(new JsonParseError(xhr.status));
  1072.         }
  1073.  
  1074.         return;
  1075.       }
  1076.  
  1077.       if (xhr.status === 200) {
  1078.         try {
  1079.           var res = JSON.parse(xhr.responseText);
  1080.           res.query = query;
  1081.           onSuccess(res);
  1082.         } catch (error) {
  1083.           onError(new JsonParseError(xhr.status));
  1084.         }
  1085.  
  1086.         return;
  1087.       }
  1088.  
  1089.       try {
  1090.         var genericErrorJson = JSON.parse(xhr.responseText);
  1091.         onError(
  1092.           new GenericError(
  1093.             xhr.status,
  1094.             genericErrorJson.message,
  1095.             genericErrorJson.description
  1096.           )
  1097.         );
  1098.       } catch (error) {
  1099.         onError(new JsonParseError(xhr.status));
  1100.       }
  1101.  
  1102.       return;
  1103.     };
  1104.  
  1105.     xhr.open(
  1106.       'get',
  1107.       route + '?q=' + encodeURIComponent(query) + '&' + queryParams
  1108.    );
  1109.  
  1110.     xhr.setRequestHeader('Content-Type', 'application/json');
  1111.  
  1112.     xhr.send();
  1113.   }
  1114.  
  1115.   function Cache(config) {
  1116.     this._store = {};
  1117.     this._keys = [];
  1118.     if (config && config.bucketSize) {
  1119.      this.bucketSize = config.bucketSize;
  1120.     } else {
  1121.       this.bucketSize = 20;
  1122.     }
  1123.   }
  1124.  
  1125.   Cache.prototype.set = function(key, value) {
  1126.     if (this.count() >= this.bucketSize) {
  1127.       var deleteKey = this._keys.splice(0, 1);
  1128.       this.delete(deleteKey);
  1129.     }
  1130.  
  1131.     this._keys.push(key);
  1132.     this._store[key] = value;
  1133.  
  1134.     return this._store;
  1135.   };
  1136.  
  1137.   Cache.prototype.get = function(key) {
  1138.     return this._store[key];
  1139.   };
  1140.  
  1141.   Cache.prototype.has = function(key) {
  1142.     return Boolean(this._store[key]);
  1143.   };
  1144.  
  1145.   Cache.prototype.count = function() {
  1146.     return Object.keys(this._store).length;
  1147.   };
  1148.  
  1149.   Cache.prototype.delete = function(key) {
  1150.     var exists = Boolean(this._store[key]);
  1151.     delete this._store[key];
  1152.     return exists && !this._store[key];
  1153.   };
  1154.  
  1155.   function Dispatcher() {
  1156.     this.events = {};
  1157.   }
  1158.  
  1159.   Dispatcher.prototype.on = function(eventName, callback) {
  1160.     var event = this.events[eventName];
  1161.     if (!event) {
  1162.       event = new DispatcherEvent(eventName);
  1163.       this.events[eventName] = event;
  1164.     }
  1165.     event.registerCallback(callback);
  1166.   };
  1167.  
  1168.   Dispatcher.prototype.off = function(eventName, callback) {
  1169.     var event = this.events[eventName];
  1170.     if (event && event.callbacks.indexOf(callback) > -1) {
  1171.      event.unregisterCallback(callback);
  1172.       if (event.callbacks.length === 0) {
  1173.         delete this.events[eventName];
  1174.       }
  1175.     }
  1176.   };
  1177.  
  1178.   Dispatcher.prototype.dispatch = function(eventName, payload) {
  1179.     var event = this.events[eventName];
  1180.     if (event) {
  1181.       event.fire(payload);
  1182.     }
  1183.   };
  1184.  
  1185.   function DispatcherEvent(eventName) {
  1186.     this.eventName = eventName;
  1187.     this.callbacks = [];
  1188.   }
  1189.  
  1190.   DispatcherEvent.prototype.registerCallback = function(callback) {
  1191.     this.callbacks.push(callback);
  1192.   };
  1193.  
  1194.   DispatcherEvent.prototype.unregisterCallback = function(callback) {
  1195.     var index = this.callbacks.indexOf(callback);
  1196.     if (index > -1) {
  1197.       this.callbacks.splice(index, 1);
  1198.     }
  1199.   };
  1200.  
  1201.   DispatcherEvent.prototype.fire = function(payload) {
  1202.     var callbacks = this.callbacks.slice(0);
  1203.     callbacks.forEach(function(callback) {
  1204.       callback(payload);
  1205.     });
  1206.   };
  1207.  
  1208.   function debounce(func, wait) {
  1209.     var timeout = null;
  1210.     return function() {
  1211.       var context = this;
  1212.       var args = arguments;
  1213.       clearTimeout(timeout);
  1214.       timeout = setTimeout(function() {
  1215.         timeout = null;
  1216.         func.apply(context, args);
  1217.       }, wait || 0);
  1218.     };
  1219.   }
  1220.  
  1221.   function objectToQueryParams(obj, parentKey) {
  1222.     var output = '';
  1223.     parentKey = parentKey || null;
  1224.  
  1225.     Object.keys(obj).forEach(function(key) {
  1226.       var outputKey = key + '=';
  1227.       if (parentKey) {
  1228.         outputKey = parentKey + '[' + key + ']';
  1229.       }
  1230.  
  1231.       switch (trueTypeOf(obj[key])) {
  1232.         case 'object':
  1233.           output += objectToQueryParams(obj[key], parentKey ? outputKey : key);
  1234.           break;
  1235.         case 'array':
  1236.           output += outputKey + '=' + obj[key].join(',') + '&';
  1237.           break;
  1238.         default:
  1239.           if (parentKey) {
  1240.             outputKey += '=';
  1241.           }
  1242.           output += outputKey + encodeURIComponent(obj[key]) + '&';
  1243.           break;
  1244.       }
  1245.     });
  1246.  
  1247.     return output;
  1248.   }
  1249.  
  1250.   function trueTypeOf(obj) {
  1251.     return Object.prototype.toString
  1252.       .call(obj)
  1253.       .slice(8, -1)
  1254.       .toLowerCase();
  1255.   }
  1256.  
  1257.   var DEBOUNCE_RATE = 10;
  1258.   var requestDebounced = debounce(request, DEBOUNCE_RATE);
  1259.  
  1260.   function PredictiveSearch(params, searchUrl) {
  1261.     if (!params) {
  1262.       throw new TypeError('No params object was specified');
  1263.     }
  1264.  
  1265.     this.searchUrl = searchUrl;
  1266.  
  1267.     this._retryAfter = null;
  1268.     this._currentQuery = null;
  1269.  
  1270.     this.dispatcher = new Dispatcher();
  1271.     this.cache = new Cache({ bucketSize: 40 });
  1272.  
  1273.     this.queryParams = objectToQueryParams(params);
  1274.   }
  1275.  
  1276.   PredictiveSearch.TYPES = {
  1277.     PRODUCT: 'product',
  1278.     PAGE: 'page',
  1279.     ARTICLE: 'article'
  1280.   };
  1281.  
  1282.   PredictiveSearch.FIELDS = {
  1283.     AUTHOR: 'author',
  1284.     BODY: 'body',
  1285.     PRODUCT_TYPE: 'product_type',
  1286.     TAG: 'tag',
  1287.     TITLE: 'title',
  1288.     VARIANTS_BARCODE: 'variants.barcode',
  1289.     VARIANTS_SKU: 'variants.sku',
  1290.     VARIANTS_TITLE: 'variants.title',
  1291.     VENDOR: 'vendor'
  1292.   };
  1293.  
  1294.   PredictiveSearch.UNAVAILABLE_PRODUCTS = {
  1295.     SHOW: 'show',
  1296.     HIDE: 'hide',
  1297.     LAST: 'last'
  1298.   };
  1299.  
  1300.   PredictiveSearch.prototype.query = function query(query) {
  1301.     try {
  1302.       validateQuery(query);
  1303.     } catch (error) {
  1304.       this.dispatcher.dispatch('error', error);
  1305.       return;
  1306.     }
  1307.  
  1308.     if (query === '') {
  1309.       return this;
  1310.     }
  1311.  
  1312.     this._currentQuery = normalizeQuery(query);
  1313.     var cacheResult = this.cache.get(this._currentQuery);
  1314.     if (cacheResult) {
  1315.       this.dispatcher.dispatch('success', cacheResult);
  1316.       return this;
  1317.     }
  1318.  
  1319.     requestDebounced(
  1320.       this.searchUrl,
  1321.       this.queryParams,
  1322.       query,
  1323.       function(result) {
  1324.         this.cache.set(normalizeQuery(result.query), result);
  1325.         if (normalizeQuery(result.query) === this._currentQuery) {
  1326.           this._retryAfter = null;
  1327.           this.dispatcher.dispatch('success', result);
  1328.         }
  1329.       }.bind(this),
  1330.       function(error) {
  1331.         if (error.retryAfter) {
  1332.           this._retryAfter = error.retryAfter;
  1333.         }
  1334.         this.dispatcher.dispatch('error', error);
  1335.       }.bind(this)
  1336.     );
  1337.  
  1338.     return this;
  1339.   };
  1340.  
  1341.   PredictiveSearch.prototype.on = function on(eventName, callback) {
  1342.     this.dispatcher.on(eventName, callback);
  1343.  
  1344.     return this;
  1345.   };
  1346.  
  1347.   PredictiveSearch.prototype.off = function on(eventName, callback) {
  1348.     this.dispatcher.off(eventName, callback);
  1349.  
  1350.     return this;
  1351.   };
  1352.  
  1353.   function normalizeQuery(query) {
  1354.     if (typeof query !== 'string') {
  1355.       return null;
  1356.     }
  1357.  
  1358.     return query
  1359.       .trim()
  1360.       .replace(' ', '-')
  1361.       .toLowerCase();
  1362.   }
  1363.  
  1364.   return PredictiveSearch;
  1365. })();
  1366.  
  1367. this.Shopify = this.Shopify || {};
  1368. this.Shopify.theme = this.Shopify.theme || {};
  1369. this.Shopify.theme.PredictiveSearchComponent = (function(PredictiveSearch) {
  1370.   'use strict';
  1371.  
  1372.   PredictiveSearch =
  1373.     PredictiveSearch && PredictiveSearch.hasOwnProperty('default')
  1374.      ? PredictiveSearch['default']
  1375.      : PredictiveSearch;
  1376.  
  1377.   var DEFAULT_PREDICTIVE_SEARCH_API_CONFIG = {
  1378.     resources: {
  1379.       type: [PredictiveSearch.TYPES.PRODUCT],
  1380.       options: {
  1381.         unavailable_products: PredictiveSearch.UNAVAILABLE_PRODUCTS.LAST,
  1382.         fields: [
  1383.           PredictiveSearch.FIELDS.TITLE,
  1384.           PredictiveSearch.FIELDS.VENDOR,
  1385.           PredictiveSearch.FIELDS.PRODUCT_TYPE,
  1386.           PredictiveSearch.FIELDS.VARIANTS_TITLE
  1387.         ]
  1388.       }
  1389.     }
  1390.   };
  1391.  
  1392.   function PredictiveSearchComponent(config) {
  1393.     // validate config
  1394.     if (
  1395.       !config ||
  1396.       !config.selectors ||
  1397.       !config.selectors.input ||
  1398.       !isString(config.selectors.input) ||
  1399.       !config.selectors.result ||
  1400.       !isString(config.selectors.result) ||
  1401.       !config.resultTemplateFct ||
  1402.       !isFunction(config.resultTemplateFct) ||
  1403.       !config.numberOfResultsTemplateFct ||
  1404.       !isFunction(config.numberOfResultsTemplateFct) ||
  1405.       !config.loadingResultsMessageTemplateFct ||
  1406.       !isFunction(config.loadingResultsMessageTemplateFct)
  1407.     ) {
  1408.       var error = new TypeError(
  1409.         'PredictiveSearchComponent config is not valid'
  1410.       );
  1411.       error.type = 'argument';
  1412.       throw error;
  1413.     }
  1414.  
  1415.     // Find nodes
  1416.     this.nodes = findNodes(config.selectors);
  1417.  
  1418.     // Validate nodes
  1419.     if (!isValidNodes(this.nodes)) {
  1420.       // eslint-disable-next-line no-console
  1421.       console.warn('Could not find valid nodes');
  1422.       return;
  1423.     }
  1424.  
  1425.     this.searchUrl = config.searchUrl || '/search';
  1426.  
  1427.     // Store the keyword that was used for the search
  1428.     this._searchKeyword = '';
  1429.  
  1430.     // Assign result template
  1431.     this.resultTemplateFct = config.resultTemplateFct;
  1432.  
  1433.     // Assign number of results template
  1434.     this.numberOfResultsTemplateFct = config.numberOfResultsTemplateFct;
  1435.  
  1436.     // Assign loading state template function
  1437.     this.loadingResultsMessageTemplateFct =
  1438.       config.loadingResultsMessageTemplateFct;
  1439.  
  1440.     // Assign number of search results
  1441.     this.numberOfResults = config.numberOfResults || 4;
  1442.  
  1443.     // Set classes
  1444.     this.classes = {
  1445.       visibleVariant: config.visibleVariant
  1446.         ? config.visibleVariant
  1447.         : 'predictive-search-wrapper--visible',
  1448.       itemSelected: config.itemSelectedClass
  1449.         ? config.itemSelectedClass
  1450.         : 'predictive-search-item--selected',
  1451.       clearButtonVisible: config.clearButtonVisibleClass
  1452.         ? config.clearButtonVisibleClass
  1453.         : 'predictive-search__clear-button--visible'
  1454.     };
  1455.  
  1456.     this.selectors = {
  1457.       searchResult: config.searchResult
  1458.         ? config.searchResult
  1459.         : '[data-search-result]'
  1460.     };
  1461.  
  1462.     // Assign callbacks
  1463.     this.callbacks = assignCallbacks(config);
  1464.  
  1465.     // Add input attributes
  1466.     addInputAttributes(this.nodes.input);
  1467.  
  1468.     // Add input event listeners
  1469.     this._addInputEventListeners();
  1470.  
  1471.     // Add body listener
  1472.     this._addBodyEventListener();
  1473.  
  1474.     // Add accessibility announcer
  1475.     this._addAccessibilityAnnouncer();
  1476.  
  1477.     // Display the reset button if the input is not empty
  1478.     this._toggleClearButtonVisibility();
  1479.  
  1480.     // Instantiate Predictive Search API
  1481.     this.predictiveSearch = new PredictiveSearch(
  1482.       config.PredictiveSearchAPIConfig
  1483.         ? config.PredictiveSearchAPIConfig
  1484.         : DEFAULT_PREDICTIVE_SEARCH_API_CONFIG,
  1485.       this.searchUrl
  1486.     );
  1487.  
  1488.     // Add predictive search success event listener
  1489.     this.predictiveSearch.on(
  1490.       'success',
  1491.       this._handlePredictiveSearchSuccess.bind(this)
  1492.     );
  1493.  
  1494.     // Add predictive search error event listener
  1495.     this.predictiveSearch.on(
  1496.       'error',
  1497.       this._handlePredictiveSearchError.bind(this)
  1498.     );
  1499.   }
  1500.  
  1501.   /**
  1502.    * Private methods
  1503.    */
  1504.   function findNodes(selectors) {
  1505.     return {
  1506.       input: document.querySelector(selectors.input),
  1507.       reset: document.querySelector(selectors.reset),
  1508.       result: document.querySelector(selectors.result)
  1509.     };
  1510.   }
  1511.  
  1512.   function isValidNodes(nodes) {
  1513.     if (
  1514.       !nodes ||
  1515.       !nodes.input ||
  1516.       !nodes.result ||
  1517.       nodes.input.tagName !== 'INPUT'
  1518.     ) {
  1519.       return false;
  1520.     }
  1521.  
  1522.     return true;
  1523.   }
  1524.  
  1525.   function assignCallbacks(config) {
  1526.     return {
  1527.       onBodyMousedown: config.onBodyMousedown,
  1528.       onBeforeOpen: config.onBeforeOpen,
  1529.       onOpen: config.onOpen,
  1530.       onBeforeClose: config.onBeforeClose,
  1531.       onClose: config.onClose,
  1532.       onInputFocus: config.onInputFocus,
  1533.       onInputKeyup: config.onInputKeyup,
  1534.       onInputBlur: config.onInputBlur,
  1535.       onInputReset: config.onInputReset,
  1536.       onBeforeDestroy: config.onBeforeDestroy,
  1537.       onDestroy: config.onDestroy
  1538.     };
  1539.   }
  1540.  
  1541.   function addInputAttributes(input) {
  1542.     input.setAttribute('autocorrect', 'off');
  1543.     input.setAttribute('autocomplete', 'off');
  1544.     input.setAttribute('autocapitalize', 'off');
  1545.     input.setAttribute('spellcheck', 'false');
  1546.   }
  1547.  
  1548.   function removeInputAttributes(input) {
  1549.     input.removeAttribute('autocorrect', 'off');
  1550.     input.removeAttribute('autocomplete', 'off');
  1551.     input.removeAttribute('autocapitalize', 'off');
  1552.     input.removeAttribute('spellcheck', 'false');
  1553.   }
  1554.  
  1555.   /**
  1556.    * Public variables
  1557.    */
  1558.   PredictiveSearchComponent.prototype.isResultVisible = false;
  1559.   PredictiveSearchComponent.prototype.results = {};
  1560.  
  1561.   /**
  1562.    * "Private" variables
  1563.    */
  1564.   PredictiveSearchComponent.prototype._latencyTimer = null;
  1565.   PredictiveSearchComponent.prototype._resultNodeClicked = false;
  1566.  
  1567.   /**
  1568.    * "Private" instance methods
  1569.    */
  1570.   PredictiveSearchComponent.prototype._addInputEventListeners = function() {
  1571.     var input = this.nodes.input;
  1572.     var reset = this.nodes.reset;
  1573.  
  1574.     if (!input) {
  1575.       return;
  1576.     }
  1577.  
  1578.     this._handleInputFocus = this._handleInputFocus.bind(this);
  1579.     this._handleInputBlur = this._handleInputBlur.bind(this);
  1580.     this._handleInputKeyup = this._handleInputKeyup.bind(this);
  1581.     this._handleInputKeydown = this._handleInputKeydown.bind(this);
  1582.  
  1583.     input.addEventListener('focus', this._handleInputFocus);
  1584.     input.addEventListener('blur', this._handleInputBlur);
  1585.     input.addEventListener('keyup', this._handleInputKeyup);
  1586.     input.addEventListener('keydown', this._handleInputKeydown);
  1587.  
  1588.     if (reset) {
  1589.       this._handleInputReset = this._handleInputReset.bind(this);
  1590.       reset.addEventListener('click', this._handleInputReset);
  1591.     }
  1592.   };
  1593.  
  1594.   PredictiveSearchComponent.prototype._removeInputEventListeners = function() {
  1595.     var input = this.nodes.input;
  1596.  
  1597.     input.removeEventListener('focus', this._handleInputFocus);
  1598.     input.removeEventListener('blur', this._handleInputBlur);
  1599.     input.removeEventListener('keyup', this._handleInputKeyup);
  1600.     input.removeEventListener('keydown', this._handleInputKeydown);
  1601.   };
  1602.  
  1603.   PredictiveSearchComponent.prototype._addBodyEventListener = function() {
  1604.     this._handleBodyMousedown = this._handleBodyMousedown.bind(this);
  1605.  
  1606.     document
  1607.       .querySelector('body')
  1608.       .addEventListener('mousedown', this._handleBodyMousedown);
  1609.   };
  1610.  
  1611.   PredictiveSearchComponent.prototype._removeBodyEventListener = function() {
  1612.     document
  1613.       .querySelector('body')
  1614.       .removeEventListener('mousedown', this._handleBodyMousedown);
  1615.   };
  1616.  
  1617.   PredictiveSearchComponent.prototype._removeClearButtonEventListener = function() {
  1618.     var reset = this.nodes.reset;
  1619.  
  1620.     if (!reset) {
  1621.       return;
  1622.     }
  1623.  
  1624.     reset.removeEventListener('click', this._handleInputReset);
  1625.   };
  1626.  
  1627.   /**
  1628.    * Event handlers
  1629.    */
  1630.   PredictiveSearchComponent.prototype._handleBodyMousedown = function(evt) {
  1631.     if (this.isResultVisible && this.nodes !== null) {
  1632.      if (
  1633.        evt.target.isEqualNode(this.nodes.input) ||
  1634.        this.nodes.input.contains(evt.target) ||
  1635.        evt.target.isEqualNode(this.nodes.result) ||
  1636.        this.nodes.result.contains(evt.target)
  1637.      ) {
  1638.        this._resultNodeClicked = true;
  1639.       } else {
  1640.         if (isFunction(this.callbacks.onBodyMousedown)) {
  1641.           var returnedValue = this.callbacks.onBodyMousedown(this.nodes);
  1642.           if (isBoolean(returnedValue) && returnedValue) {
  1643.            this.close();
  1644.           }
  1645.         } else {
  1646.           this.close();
  1647.         }
  1648.       }
  1649.     }
  1650.   };
  1651.  
  1652.   PredictiveSearchComponent.prototype._handleInputFocus = function(evt) {
  1653.     if (isFunction(this.callbacks.onInputFocus)) {
  1654.       var returnedValue = this.callbacks.onInputFocus(this.nodes);
  1655.       if (isBoolean(returnedValue) && !returnedValue) {
  1656.        return false;
  1657.       }
  1658.     }
  1659.  
  1660.     if (evt.target.value.length > 0) {
  1661.       this._search();
  1662.     }
  1663.  
  1664.     return true;
  1665.   };
  1666.  
  1667.   PredictiveSearchComponent.prototype._handleInputBlur = function() {
  1668.     // This has to be done async, to wait for the focus to be on the next
  1669.     // element and avoid closing the results.
  1670.     // Example: Going from the input to the reset button.
  1671.     setTimeout(
  1672.       function() {
  1673.         if (isFunction(this.callbacks.onInputBlur)) {
  1674.           var returnedValue = this.callbacks.onInputBlur(this.nodes);
  1675.           if (isBoolean(returnedValue) && !returnedValue) {
  1676.            return false;
  1677.           }
  1678.         }
  1679.  
  1680.         if (document.activeElement.isEqualNode(this.nodes.reset)) {
  1681.           return false;
  1682.         }
  1683.  
  1684.         if (this._resultNodeClicked) {
  1685.           this._resultNodeClicked = false;
  1686.           return false;
  1687.         }
  1688.  
  1689.         this.close();
  1690.       }.bind(this)
  1691.     );
  1692.  
  1693.     return true;
  1694.   };
  1695.  
  1696.   PredictiveSearchComponent.prototype._addAccessibilityAnnouncer = function() {
  1697.     this._accessibilityAnnouncerDiv = window.document.createElement('div');
  1698.  
  1699.     this._accessibilityAnnouncerDiv.setAttribute(
  1700.       'style',
  1701.       'position: absolute !important; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0;'
  1702.     );
  1703.  
  1704.     this._accessibilityAnnouncerDiv.setAttribute('data-search-announcer', '');
  1705.     this._accessibilityAnnouncerDiv.setAttribute('aria-live', 'polite');
  1706.     this._accessibilityAnnouncerDiv.setAttribute('aria-atomic', 'true');
  1707.  
  1708.     this.nodes.result.parentElement.appendChild(
  1709.       this._accessibilityAnnouncerDiv
  1710.     );
  1711.   };
  1712.  
  1713.   PredictiveSearchComponent.prototype._removeAccessibilityAnnouncer = function() {
  1714.     this.nodes.result.parentElement.removeChild(
  1715.       this._accessibilityAnnouncerDiv
  1716.     );
  1717.   };
  1718.  
  1719.   PredictiveSearchComponent.prototype._updateAccessibilityAttributesAfterSelectingElement = function(
  1720.     previousSelectedElement,
  1721.     currentSelectedElement
  1722.   ) {
  1723.     // Update the active descendant on the search input
  1724.     this.nodes.input.setAttribute(
  1725.       'aria-activedescendant',
  1726.       currentSelectedElement.id
  1727.     );
  1728.  
  1729.     // Unmark the previousSelected elemented as selected
  1730.     if (previousSelectedElement) {
  1731.       previousSelectedElement.removeAttribute('aria-selected');
  1732.     }
  1733.  
  1734.     // Mark the element as selected
  1735.     currentSelectedElement.setAttribute('aria-selected', true);
  1736.   };
  1737.  
  1738.   PredictiveSearchComponent.prototype._clearAriaActiveDescendant = function() {
  1739.     this.nodes.input.setAttribute('aria-activedescendant', '');
  1740.   };
  1741.  
  1742.   PredictiveSearchComponent.prototype._announceNumberOfResultsFound = function(
  1743.     results
  1744.   ) {
  1745.     var currentAnnouncedMessage = this._accessibilityAnnouncerDiv.innerHTML;
  1746.     var newMessage = this.numberOfResultsTemplateFct(results);
  1747.  
  1748.     // If the messages are the same, they won't get announced
  1749.     // add white space so it gets announced
  1750.     if (currentAnnouncedMessage === newMessage) {
  1751.       newMessage = newMessage + '&nbsp;';
  1752.     }
  1753.  
  1754.     this._accessibilityAnnouncerDiv.innerHTML = newMessage;
  1755.   };
  1756.  
  1757.   PredictiveSearchComponent.prototype._announceLoadingState = function() {
  1758.     this._accessibilityAnnouncerDiv.innerHTML = this.loadingResultsMessageTemplateFct();
  1759.   };
  1760.  
  1761.   PredictiveSearchComponent.prototype._handleInputKeyup = function(evt) {
  1762.     var UP_ARROW_KEY_CODE = 38;
  1763.     var DOWN_ARROW_KEY_CODE = 40;
  1764.     var RETURN_KEY_CODE = 13;
  1765.     var ESCAPE_KEY_CODE = 27;
  1766.  
  1767.     if (isFunction(this.callbacks.onInputKeyup)) {
  1768.       var returnedValue = this.callbacks.onInputKeyup(this.nodes);
  1769.       if (isBoolean(returnedValue) && !returnedValue) {
  1770.        return false;
  1771.       }
  1772.     }
  1773.  
  1774.     this._toggleClearButtonVisibility();
  1775.  
  1776.     if (this.isResultVisible && this.nodes !== null) {
  1777.      if (evt.keyCode === UP_ARROW_KEY_CODE) {
  1778.        this._navigateOption(evt, 'UP');
  1779.         return true;
  1780.       }
  1781.  
  1782.       if (evt.keyCode === DOWN_ARROW_KEY_CODE) {
  1783.         this._navigateOption(evt, 'DOWN');
  1784.         return true;
  1785.       }
  1786.  
  1787.       if (evt.keyCode === RETURN_KEY_CODE) {
  1788.         this._selectOption();
  1789.         return true;
  1790.       }
  1791.  
  1792.       if (evt.keyCode === ESCAPE_KEY_CODE) {
  1793.         this.close();
  1794.       }
  1795.     }
  1796.  
  1797.     if (evt.target.value.length <= 0) {
  1798.      this.close();
  1799.      this._setKeyword('');
  1800.    } else if (evt.target.value.length > 0) {
  1801.       this._search();
  1802.     }
  1803.  
  1804.     return true;
  1805.   };
  1806.  
  1807.   PredictiveSearchComponent.prototype._handleInputKeydown = function(evt) {
  1808.     var RETURN_KEY_CODE = 13;
  1809.     var UP_ARROW_KEY_CODE = 38;
  1810.     var DOWN_ARROW_KEY_CODE = 40;
  1811.  
  1812.     // Prevent the form default submission if there is a selected option
  1813.     if (evt.keyCode === RETURN_KEY_CODE && this._getSelectedOption() !== null) {
  1814.      evt.preventDefault();
  1815.     }
  1816.  
  1817.     // Prevent the cursor from moving in the input when using the up and down arrow keys
  1818.     if (
  1819.       evt.keyCode === UP_ARROW_KEY_CODE ||
  1820.       evt.keyCode === DOWN_ARROW_KEY_CODE
  1821.     ) {
  1822.       evt.preventDefault();
  1823.     }
  1824.   };
  1825.  
  1826.   PredictiveSearchComponent.prototype._handleInputReset = function(evt) {
  1827.     evt.preventDefault();
  1828.  
  1829.     if (isFunction(this.callbacks.onInputReset)) {
  1830.       var returnedValue = this.callbacks.onInputReset(this.nodes);
  1831.       if (isBoolean(returnedValue) && !returnedValue) {
  1832.        return false;
  1833.       }
  1834.     }
  1835.  
  1836.     this.nodes.input.value = '';
  1837.     this.nodes.input.focus();
  1838.     this._toggleClearButtonVisibility();
  1839.     this.close();
  1840.  
  1841.     return true;
  1842.   };
  1843.  
  1844.   PredictiveSearchComponent.prototype._navigateOption = function(
  1845.     evt,
  1846.     direction
  1847.   ) {
  1848.     var currentOption = this._getSelectedOption();
  1849.  
  1850.     if (!currentOption) {
  1851.       var firstOption = this.nodes.result.querySelector(
  1852.         this.selectors.searchResult
  1853.       );
  1854.       firstOption.classList.add(this.classes.itemSelected);
  1855.       this._updateAccessibilityAttributesAfterSelectingElement(
  1856.         null,
  1857.         firstOption
  1858.       );
  1859.     } else {
  1860.       if (direction === 'DOWN') {
  1861.         var nextOption = currentOption.nextElementSibling;
  1862.         if (nextOption) {
  1863.           currentOption.classList.remove(this.classes.itemSelected);
  1864.           nextOption.classList.add(this.classes.itemSelected);
  1865.           this._updateAccessibilityAttributesAfterSelectingElement(
  1866.             currentOption,
  1867.             nextOption
  1868.           );
  1869.         }
  1870.       } else {
  1871.         var previousOption = currentOption.previousElementSibling;
  1872.         if (previousOption) {
  1873.           currentOption.classList.remove(this.classes.itemSelected);
  1874.           previousOption.classList.add(this.classes.itemSelected);
  1875.           this._updateAccessibilityAttributesAfterSelectingElement(
  1876.             currentOption,
  1877.             previousOption
  1878.           );
  1879.         }
  1880.       }
  1881.     }
  1882.   };
  1883.  
  1884.   PredictiveSearchComponent.prototype._getSelectedOption = function() {
  1885.     return this.nodes.result.querySelector('.' + this.classes.itemSelected);
  1886.   };
  1887.  
  1888.   PredictiveSearchComponent.prototype._selectOption = function() {
  1889.     var selectedOption = this._getSelectedOption();
  1890.  
  1891.     if (selectedOption) {
  1892.       selectedOption.querySelector('a, button').click();
  1893.     }
  1894.   };
  1895.  
  1896.   PredictiveSearchComponent.prototype._search = function() {
  1897.     var newSearchKeyword = this.nodes.input.value;
  1898.  
  1899.     if (this._searchKeyword === newSearchKeyword) {
  1900.       return;
  1901.     }
  1902.  
  1903.     clearTimeout(this._latencyTimer);
  1904.     this._latencyTimer = setTimeout(
  1905.       function() {
  1906.         this.results.isLoading = true;
  1907.  
  1908.         // Annonuce that we're loading the results
  1909.         this._announceLoadingState();
  1910.  
  1911.         this.nodes.result.classList.add(this.classes.visibleVariant);
  1912.         // NOTE: We could benifit in using DOMPurify.
  1913.         // https://github.com/cure53/DOMPurify
  1914.         this.nodes.result.innerHTML = this.resultTemplateFct(this.results);
  1915.       }.bind(this),
  1916.       500
  1917.     );
  1918.  
  1919.     this.predictiveSearch.query(newSearchKeyword);
  1920.     this._setKeyword(newSearchKeyword);
  1921.   };
  1922.  
  1923.   PredictiveSearchComponent.prototype._handlePredictiveSearchSuccess = function(
  1924.     json
  1925.   ) {
  1926.     clearTimeout(this._latencyTimer);
  1927.     this.results = json.resources.results;
  1928.  
  1929.     this.results.isLoading = false;
  1930.     this.results.products = this.results.products.slice(
  1931.       0,
  1932.       this.numberOfResults
  1933.     );
  1934.     this.results.canLoadMore =
  1935.       this.numberOfResults <= this.results.products.length;
  1936.    this.results.searchQuery = this.nodes.input.value;
  1937.  
  1938.    if (this.results.products.length > 0 || this.results.searchQuery) {
  1939.       this.nodes.result.innerHTML = this.resultTemplateFct(this.results);
  1940.       this._announceNumberOfResultsFound(this.results);
  1941.       this.open();
  1942.     } else {
  1943.       this.nodes.result.innerHTML = '';
  1944.  
  1945.       this._closeOnNoResults();
  1946.     }
  1947.   };
  1948.  
  1949.   PredictiveSearchComponent.prototype._handlePredictiveSearchError = function() {
  1950.     clearTimeout(this._latencyTimer);
  1951.     this.nodes.result.innerHTML = '';
  1952.  
  1953.     this._closeOnNoResults();
  1954.   };
  1955.  
  1956.   PredictiveSearchComponent.prototype._closeOnNoResults = function() {
  1957.     if (this.nodes) {
  1958.       this.nodes.result.classList.remove(this.classes.visibleVariant);
  1959.     }
  1960.  
  1961.     this.isResultVisible = false;
  1962.   };
  1963.  
  1964.   PredictiveSearchComponent.prototype._setKeyword = function(keyword) {
  1965.     this._searchKeyword = keyword;
  1966.   };
  1967.  
  1968.   PredictiveSearchComponent.prototype._toggleClearButtonVisibility = function() {
  1969.     if (!this.nodes.reset) {
  1970.       return;
  1971.     }
  1972.  
  1973.     if (this.nodes.input.value.length > 0) {
  1974.       this.nodes.reset.classList.add(this.classes.clearButtonVisible);
  1975.     } else {
  1976.       this.nodes.reset.classList.remove(this.classes.clearButtonVisible);
  1977.     }
  1978.   };
  1979.  
  1980.   /**
  1981.    * Public methods
  1982.    */
  1983.   PredictiveSearchComponent.prototype.open = function() {
  1984.     if (this.isResultVisible) {
  1985.       return;
  1986.     }
  1987.  
  1988.     if (isFunction(this.callbacks.onBeforeOpen)) {
  1989.       var returnedValue = this.callbacks.onBeforeOpen(this.nodes);
  1990.       if (isBoolean(returnedValue) && !returnedValue) {
  1991.        return false;
  1992.       }
  1993.     }
  1994.  
  1995.     this.nodes.result.classList.add(this.classes.visibleVariant);
  1996.     this.nodes.input.setAttribute('aria-expanded', true);
  1997.     this.isResultVisible = true;
  1998.  
  1999.     if (isFunction(this.callbacks.onOpen)) {
  2000.       return this.callbacks.onOpen(this.nodes) || true;
  2001.     }
  2002.  
  2003.     return true;
  2004.   };
  2005.  
  2006.   PredictiveSearchComponent.prototype.close = function() {
  2007.     if (!this.isResultVisible) {
  2008.       return true;
  2009.     }
  2010.  
  2011.     if (isFunction(this.callbacks.onBeforeClose)) {
  2012.       var returnedValue = this.callbacks.onBeforeClose(this.nodes);
  2013.       if (isBoolean(returnedValue) && !returnedValue) {
  2014.        return false;
  2015.       }
  2016.     }
  2017.  
  2018.     if (this.nodes) {
  2019.       this.nodes.result.classList.remove(this.classes.visibleVariant);
  2020.     }
  2021.  
  2022.     this.nodes.input.setAttribute('aria-expanded', false);
  2023.     this._clearAriaActiveDescendant();
  2024.     this._setKeyword('');
  2025.  
  2026.     if (isFunction(this.callbacks.onClose)) {
  2027.       this.callbacks.onClose(this.nodes);
  2028.     }
  2029.  
  2030.     this.isResultVisible = false;
  2031.     this.results = {};
  2032.  
  2033.     return true;
  2034.   };
  2035.  
  2036.   PredictiveSearchComponent.prototype.destroy = function() {
  2037.     this.close();
  2038.  
  2039.     if (isFunction(this.callbacks.onBeforeDestroy)) {
  2040.       var returnedValue = this.callbacks.onBeforeDestroy(this.nodes);
  2041.       if (isBoolean(returnedValue) && !returnedValue) {
  2042.        return false;
  2043.       }
  2044.     }
  2045.  
  2046.     this.nodes.result.classList.remove(this.classes.visibleVariant);
  2047.     removeInputAttributes(this.nodes.input);
  2048.     this._removeInputEventListeners();
  2049.     this._removeBodyEventListener();
  2050.     this._removeAccessibilityAnnouncer();
  2051.     this._removeClearButtonEventListener();
  2052.  
  2053.     if (isFunction(this.callbacks.onDestroy)) {
  2054.       this.callbacks.onDestroy(this.nodes);
  2055.     }
  2056.  
  2057.     return true;
  2058.   };
  2059.  
  2060.   PredictiveSearchComponent.prototype.clearAndClose = function() {
  2061.     this.nodes.input.value = '';
  2062.     this.close();
  2063.   };
  2064.  
  2065.   /**
  2066.    * Utilities
  2067.    */
  2068.   function getTypeOf(value) {
  2069.     return Object.prototype.toString.call(value);
  2070.   }
  2071.  
  2072.   function isString(value) {
  2073.     return getTypeOf(value) === '[object String]';
  2074.   }
  2075.  
  2076.   function isBoolean(value) {
  2077.     return getTypeOf(value) === '[object Boolean]';
  2078.   }
  2079.  
  2080.   function isFunction(value) {
  2081.     return getTypeOf(value) === '[object Function]';
  2082.   }
  2083.  
  2084.   return PredictiveSearchComponent;
  2085. })(Shopify.theme.PredictiveSearch);
  2086.  
  2087. window.theme = window.theme || {};
  2088.  
  2089. theme.TouchEvents = function TouchEvents(element, options) {
  2090.   this.axis;
  2091.   this.checkEvents = [];
  2092.   this.eventHandlers = {};
  2093.   this.eventModel = {};
  2094.   this.events = [
  2095.     ['touchstart', 'touchmove', 'touchend', 'touchcancel'],
  2096.     ['pointerdown', 'pointermove', 'pointerup', 'pointercancel'],
  2097.     ['mousedown', 'mousemove', 'mouseup']
  2098.   ];
  2099.   this.eventType;
  2100.   this.difference = {};
  2101.   this.direction;
  2102.   this.start = {};
  2103.  
  2104.   this.element = element;
  2105.   this.options = Object.assign(
  2106.     {},
  2107.     {
  2108.       dragThreshold: 10,
  2109.       start: function() {}, // eslint-disable-line
  2110.       move: function() {}, // eslint-disable-line
  2111.       end: function() {} // eslint-disable-line
  2112.     },
  2113.     options
  2114.   );
  2115.  
  2116.   this.checkEvents = this._getCheckEvents();
  2117.   this.eventModel = this._getEventModel();
  2118.  
  2119.   this._setupEventHandlers();
  2120. };
  2121.  
  2122. theme.TouchEvents.prototype = Object.assign({}, theme.TouchEvents.prototype, {
  2123.   destroy: function() {
  2124.     this.element.removeEventListener(
  2125.       'dragstart',
  2126.       this.eventHandlers.preventDefault
  2127.     );
  2128.  
  2129.     this.element.removeEventListener(
  2130.       this.events[this.eventModel][0],
  2131.       this.eventHandlers.touchStart
  2132.     );
  2133.  
  2134.     if (!this.eventModel) {
  2135.       this.element.removeEventListener(
  2136.         this.events[2][0],
  2137.         this.eventHandlers.touchStart
  2138.       );
  2139.     }
  2140.  
  2141.     this.element.removeEventListener('click', this.eventHandlers.preventClick);
  2142.   },
  2143.  
  2144.   _setupEventHandlers: function() {
  2145.     this.eventHandlers.preventDefault = this._preventDefault.bind(this);
  2146.     this.eventHandlers.preventClick = this._preventClick.bind(this);
  2147.     this.eventHandlers.touchStart = this._touchStart.bind(this);
  2148.     this.eventHandlers.touchMove = this._touchMove.bind(this);
  2149.     this.eventHandlers.touchEnd = this._touchEnd.bind(this);
  2150.  
  2151.     // Prevent element from dragging when using mouse
  2152.     this.element.addEventListener(
  2153.       'dragstart',
  2154.       this.eventHandlers.preventDefault
  2155.     );
  2156.  
  2157.     // Bind the touchstart/pointerdown event
  2158.     this.element.addEventListener(
  2159.       this.events[this.eventModel][0],
  2160.       this.eventHandlers.touchStart
  2161.     );
  2162.  
  2163.     // Bind mousedown if necessary
  2164.     if (!this.eventModel) {
  2165.       this.element.addEventListener(
  2166.         this.events[2][0],
  2167.         this.eventHandlers.touchStart
  2168.       );
  2169.     }
  2170.  
  2171.     // No clicking during touch
  2172.     this.element.addEventListener('click', this.eventHandlers.preventClick);
  2173.   },
  2174.  
  2175.   _touchStart: function(event) {
  2176.     this.eventType = this.eventModel;
  2177.  
  2178.     if (event.type === 'mousedown' && !this.eventModel) {
  2179.      this.eventType = 2;
  2180.     }
  2181.  
  2182.     if (this.checkEvents[this.eventType](event)) return;
  2183.     if (this.eventType) this._preventDefault(event);
  2184.  
  2185.     document.addEventListener(
  2186.       this.events[this.eventType][1],
  2187.       this.eventHandlers.touchMove
  2188.     );
  2189.  
  2190.     document.addEventListener(
  2191.       this.events[this.eventType][2],
  2192.       this.eventHandlers.touchEnd
  2193.     );
  2194.  
  2195.     if (this.eventType < 2) {
  2196.      document.addEventListener(
  2197.        this.events[this.eventType][3],
  2198.        this.eventHandlers.touchEnd
  2199.      );
  2200.    }
  2201.  
  2202.    this.start = {
  2203.      xPosition: this.eventType ? event.clientX : event.touches[0].clientX,
  2204.      yPosition: this.eventType ? event.clientY : event.touches[0].clientY,
  2205.      time: new Date().getTime()
  2206.    };
  2207.  
  2208.    // Ensure we empty out the this.difference object
  2209.    Object.keys(this.difference).forEach(
  2210.      function(key) {
  2211.        delete this.difference[key];
  2212.      }.bind(this)
  2213.    );
  2214.  
  2215.    this.options.start(event);
  2216.  },
  2217.  
  2218.  _touchMove: function(event) {
  2219.    this.difference = this._getDifference(event);
  2220.  
  2221.    // Prevent document from scrolling during swipe gesture
  2222.    document['on' + this.events[this.eventType][1]] = function(event) {
  2223.      this._preventDefault(event);
  2224.    }.bind(this);
  2225.  
  2226.    // Get the direction user is dragging
  2227.    if (!this.axis) {
  2228.      if (this.options.dragThreshold < Math.abs(this.difference.xPosition)) {
  2229.        this.axis = 'xPosition';
  2230.      } else if (
  2231.        this.options.dragThreshold < Math.abs(this.difference.yPosition)
  2232.      ) {
  2233.        this.axis = 'yPosition';
  2234.      } else {
  2235.        this.axis = false;
  2236.      }
  2237.    } else if (this.axis === 'xPosition') {
  2238.      this.direction = this.difference.xPosition < 0 ? 'left' : 'right';
  2239.    } else if (this.axis === 'yPosition') {
  2240.      this.direction = this.difference.yPosition < 0 ? 'up' : 'down';
  2241.    }
  2242.  
  2243.    this.options.move(event, this.direction, this.difference);
  2244.  },
  2245.  
  2246.  _touchEnd: function(event) {
  2247.    document.removeEventListener(
  2248.      this.events[this.eventType][1],
  2249.      this.eventHandlers.touchMove
  2250.    );
  2251.  
  2252.    document.removeEventListener(
  2253.      this.events[this.eventType][2],
  2254.      this.eventHandlers.touchEnd
  2255.    );
  2256.  
  2257.    if (this.eventType < 2) {
  2258.      document.removeEventListener(
  2259.        this.events[this.eventType][3],
  2260.        this.eventHandlers.touchEnd
  2261.      );
  2262.    }
  2263.  
  2264.    // Re-enable document scrolling
  2265.    document['on' + this.events[this.eventType][1]] = function() {
  2266.      return true;
  2267.    };
  2268.  
  2269.    this.options.end(event, this.direction, this.difference);
  2270.    this.axis = false;
  2271.  },
  2272.  
  2273.  _getDifference: function(event) {
  2274.    return {
  2275.      xPosition:
  2276.        (this.eventType ? event.clientX : event.touches[0].clientX) -
  2277.        this.start.xPosition,
  2278.      yPosition:
  2279.        (this.eventType ? event.clientY : event.touches[0].clientY) -
  2280.        this.start.yPosition,
  2281.      time: new Date().getTime() - this.start.time
  2282.    };
  2283.  },
  2284.  
  2285.  _getCheckEvents: function() {
  2286.    return [
  2287.      // Touch events
  2288.      function(event) {
  2289.        // Skip the event if it's a multi-touch or pinch move
  2290.        return (
  2291.          (event.touches && event.touches.length > 1) ||
  2292.           (event.scale && event.scale !== 1)
  2293.        );
  2294.       },
  2295.       // Pointer events
  2296.       function(event) {
  2297.         // Skip it, if:
  2298.         // 1. The event is not primary (other pointers during multi-touch),
  2299.         // 2. Left mouse button is not pressed,
  2300.         // 3. Event is not a touch event
  2301.         return (
  2302.           !event.isPrimary ||
  2303.           (event.buttons && event.buttons !== 1) ||
  2304.          (event.pointerType !== 'touch' && event.pointerType !== 'pen')
  2305.        );
  2306.       },
  2307.       // Mouse events
  2308.       function(event) {
  2309.         // Skip the event if left mouse button is not pressed
  2310.         return event.buttons && event.buttons !== 1;
  2311.       }
  2312.     ];
  2313.   },
  2314.  
  2315.   _getEventModel: function() {
  2316.     return window.navigator.pointerEnabled ? 1 : 0;
  2317.   },
  2318.  
  2319.   _preventDefault: function(event) {
  2320.     event.preventDefault ? event.preventDefault() : (event.returnValue = false);
  2321.   },
  2322.  
  2323.   _preventClick: function(event) {
  2324.     if (Math.abs(this.difference.xPosition) > this.options.dragThreshold) {
  2325.       this._preventDefault(event);
  2326.     }
  2327.   }
  2328. });
  2329.  
  2330.  
  2331. /* ================ GLOBAL ================ */
  2332. /*============================================================================
  2333.   Drawer modules
  2334. ==============================================================================*/
  2335. theme.Drawers = (function() {
  2336.   function Drawer(id, position, options) {
  2337.     var DEFAULT_OPEN_CLASS = 'js-drawer-open';
  2338.     var DEFAULT_CLOSE_CLASS = 'js-drawer-close';
  2339.  
  2340.     var defaults = {
  2341.       selectors: {
  2342.         openVariant: '.' + DEFAULT_OPEN_CLASS + '-' + position,
  2343.         close: '.' + DEFAULT_CLOSE_CLASS
  2344.       },
  2345.       classes: {
  2346.         open: DEFAULT_OPEN_CLASS,
  2347.         openVariant: DEFAULT_OPEN_CLASS + '-' + position
  2348.       },
  2349.       withPredictiveSearch: false
  2350.     };
  2351.  
  2352.     this.nodes = {
  2353.       parents: [document.documentElement, document.body],
  2354.       page: document.getElementById('PageContainer')
  2355.     };
  2356.  
  2357.     this.eventHandlers = {};
  2358.  
  2359.     this.config = Object.assign({}, defaults, options);
  2360.     this.position = position;
  2361.     this.drawer = document.getElementById(id);
  2362.  
  2363.     if (!this.drawer) {
  2364.       return false;
  2365.     }
  2366.  
  2367.     this.drawerIsOpen = false;
  2368.     this.init();
  2369.   }
  2370.  
  2371.   Drawer.prototype.init = function() {
  2372.     document
  2373.       .querySelector(this.config.selectors.openVariant)
  2374.       .addEventListener('click', this.open.bind(this));
  2375.     this.drawer
  2376.       .querySelector(this.config.selectors.close)
  2377.       .addEventListener('click', this.close.bind(this));
  2378.   };
  2379.  
  2380.   Drawer.prototype.open = function(evt) {
  2381.     // Keep track if drawer was opened from a click, or called by another function
  2382.     var externalCall = false;
  2383.  
  2384.     // Prevent following href if link is clicked
  2385.     if (evt) {
  2386.       evt.preventDefault();
  2387.     } else {
  2388.       externalCall = true;
  2389.     }
  2390.  
  2391.     // Without this, the drawer opens, the click event bubbles up to nodes.page
  2392.     // which closes the drawer.
  2393.     if (evt && evt.stopPropagation) {
  2394.      evt.stopPropagation();
  2395.       // save the source of the click, we'll focus to this on close
  2396.       this.activeSource = evt.currentTarget;
  2397.     }
  2398.  
  2399.     if (this.drawerIsOpen && !externalCall) {
  2400.      return this.close();
  2401.     }
  2402.  
  2403.     // Add is-transitioning class to moved elements on open so drawer can have
  2404.     // transition for close animation
  2405.     if (!this.config.withPredictiveSearch) {
  2406.       theme.Helpers.prepareTransition(this.drawer);
  2407.     }
  2408.  
  2409.     this.nodes.parents.forEach(
  2410.       function(parent) {
  2411.         parent.classList.add(
  2412.           this.config.classes.open,
  2413.           this.config.classes.openVariant
  2414.         );
  2415.       }.bind(this)
  2416.     );
  2417.  
  2418.     this.drawerIsOpen = true;
  2419.  
  2420.     // Run function when draw opens if set
  2421.     if (
  2422.       this.config.onDrawerOpen &&
  2423.      typeof this.config.onDrawerOpen === 'function'
  2424.    ) {
  2425.      if (!externalCall) {
  2426.        this.config.onDrawerOpen();
  2427.       }
  2428.     }
  2429.  
  2430.     if (this.activeSource && this.activeSource.hasAttribute('aria-expanded')) {
  2431.      this.activeSource.setAttribute('aria-expanded', 'true');
  2432.     }
  2433.  
  2434.     // Set focus on drawer
  2435.     var trapFocusConfig = {
  2436.       container: this.drawer
  2437.     };
  2438.  
  2439.     if (this.config.elementToFocusOnOpen) {
  2440.       trapFocusConfig.elementToFocus = this.config.elementToFocusOnOpen;
  2441.     }
  2442.  
  2443.     slate.a11y.trapFocus(trapFocusConfig);
  2444.  
  2445.     this.bindEvents();
  2446.  
  2447.     return this;
  2448.   };
  2449.  
  2450.   Drawer.prototype.close = function() {
  2451.     if (!this.drawerIsOpen) {
  2452.       // don't close a closed drawer
  2453.       return;
  2454.     }
  2455.  
  2456.     // deselect any focused form elements
  2457.     document.activeElement.dispatchEvent(
  2458.       new CustomEvent('blur', { bubbles: true, cancelable: true })
  2459.     );
  2460.  
  2461.     // Ensure closing transition is applied to moved elements, like the nav
  2462.     if (!this.config.withPredictiveSearch) {
  2463.       theme.Helpers.prepareTransition(this.drawer);
  2464.     }
  2465.  
  2466.     this.nodes.parents.forEach(
  2467.       function(parent) {
  2468.         parent.classList.remove(
  2469.           this.config.classes.open,
  2470.           this.config.classes.openVariant
  2471.         );
  2472.       }.bind(this)
  2473.     );
  2474.  
  2475.     if (this.activeSource && this.activeSource.hasAttribute('aria-expanded')) {
  2476.      this.activeSource.setAttribute('aria-expanded', 'false');
  2477.     }
  2478.  
  2479.     this.drawerIsOpen = false;
  2480.  
  2481.     // Remove focus on drawer
  2482.     slate.a11y.removeTrapFocus({
  2483.       container: this.drawer
  2484.     });
  2485.  
  2486.     this.unbindEvents();
  2487.  
  2488.     // Run function when draw closes if set
  2489.     if (
  2490.       this.config.onDrawerClose &&
  2491.      typeof this.config.onDrawerClose === 'function'
  2492.    ) {
  2493.      this.config.onDrawerClose();
  2494.     }
  2495.   };
  2496.  
  2497.   Drawer.prototype.bindEvents = function() {
  2498.     this.eventHandlers.drawerKeyupHandler = function(evt) {
  2499.       // close on 'esc' keypress
  2500.       if (evt.keyCode === 27) {
  2501.         this.close();
  2502.         return false;
  2503.       } else {
  2504.         return true;
  2505.       }
  2506.     }.bind(this);
  2507.  
  2508.     this.eventHandlers.drawerTouchmoveHandler = function() {
  2509.       return false;
  2510.     };
  2511.  
  2512.     this.eventHandlers.drawerClickHandler = function() {
  2513.       this.close();
  2514.       return false;
  2515.     }.bind(this);
  2516.  
  2517.     // Add event listener to document body
  2518.     document.body.addEventListener(
  2519.       'keyup',
  2520.       this.eventHandlers.drawerKeyupHandler
  2521.     );
  2522.  
  2523.     // Lock scrolling on mobile
  2524.     this.nodes.page.addEventListener(
  2525.       'touchmove',
  2526.       this.eventHandlers.drawerTouchmoveHandler
  2527.     );
  2528.  
  2529.     this.nodes.page.addEventListener(
  2530.       'click',
  2531.       this.eventHandlers.drawerClickHandler
  2532.     );
  2533.   };
  2534.  
  2535.   Drawer.prototype.unbindEvents = function() {
  2536.     this.nodes.page.removeEventListener(
  2537.       'touchmove',
  2538.       this.eventHandlers.drawerTouchmoveHandler
  2539.     );
  2540.     this.nodes.page.removeEventListener(
  2541.       'click',
  2542.       this.eventHandlers.drawerClickHandler
  2543.     );
  2544.     document.body.removeEventListener(
  2545.       'keyup',
  2546.       this.eventHandlers.drawerKeyupHandler
  2547.     );
  2548.   };
  2549.  
  2550.   return Drawer;
  2551. })();
  2552.  
  2553. theme.Helpers = (function() {
  2554.   var touchDevice = false;
  2555.  
  2556.   var classes = {
  2557.     preventScrolling: 'prevent-scrolling'
  2558.   };
  2559.  
  2560.   var scrollPosition = window.pageYOffset;
  2561.  
  2562.   function setTouch() {
  2563.     touchDevice = true;
  2564.   }
  2565.  
  2566.   function isTouch() {
  2567.     return touchDevice;
  2568.   }
  2569.  
  2570.   function enableScrollLock() {
  2571.     scrollPosition = window.pageYOffset;
  2572.     document.body.style.top = '-' + scrollPosition + 'px';
  2573.     document.body.classList.add(classes.preventScrolling);
  2574.   }
  2575.  
  2576.   function disableScrollLock() {
  2577.     document.body.classList.remove(classes.preventScrolling);
  2578.     document.body.style.removeProperty('top');
  2579.     window.scrollTo(0, scrollPosition);
  2580.   }
  2581.  
  2582.   function debounce(func, wait, immediate) {
  2583.     var timeout;
  2584.  
  2585.     return function() {
  2586.       var context = this,
  2587.         args = arguments;
  2588.  
  2589.       var later = function() {
  2590.         timeout = null;
  2591.         if (!immediate) func.apply(context, args);
  2592.       };
  2593.  
  2594.       var callNow = immediate && !timeout;
  2595.       clearTimeout(timeout);
  2596.       timeout = setTimeout(later, wait);
  2597.       if (callNow) func.apply(context, args);
  2598.     };
  2599.   }
  2600.  
  2601.   function getScript(source, beforeEl) {
  2602.     return new Promise(function(resolve, reject) {
  2603.       var script = document.createElement('script');
  2604.       var prior = beforeEl || document.getElementsByTagName('script')[0];
  2605.  
  2606.       script.async = true;
  2607.       script.defer = true;
  2608.  
  2609.       // eslint-disable-next-line shopify/prefer-early-return
  2610.       function onloadHander(_, isAbort) {
  2611.         if (
  2612.           isAbort ||
  2613.           !script.readyState ||
  2614.           /loaded|complete/.test(script.readyState)
  2615.         ) {
  2616.           script.onload = null;
  2617.           script.onreadystatechange = null;
  2618.           script = undefined;
  2619.  
  2620.           if (isAbort) {
  2621.             reject();
  2622.           } else {
  2623.             resolve();
  2624.           }
  2625.         }
  2626.       }
  2627.  
  2628.       script.onload = onloadHander;
  2629.       script.onreadystatechange = onloadHander;
  2630.  
  2631.       script.src = source;
  2632.       prior.parentNode.insertBefore(script, prior);
  2633.     });
  2634.   }
  2635.  
  2636.   /* Based on the prepareTransition by Jonathan Snook */
  2637.   /* Jonathan Snook - MIT License - https://github.com/snookca/prepareTransition */
  2638.   function prepareTransition(element) {
  2639.     element.addEventListener(
  2640.       'transitionend',
  2641.       function(event) {
  2642.         event.currentTarget.classList.remove('is-transitioning');
  2643.       },
  2644.       { once: true }
  2645.     );
  2646.  
  2647.     var properties = [
  2648.       'transition-duration',
  2649.       '-moz-transition-duration',
  2650.       '-webkit-transition-duration',
  2651.       '-o-transition-duration'
  2652.     ];
  2653.  
  2654.     var duration = 0;
  2655.  
  2656.     properties.forEach(function(property) {
  2657.       var computedValue = getComputedStyle(element)[property];
  2658.  
  2659.       if (computedValue) {
  2660.         computedValue.replace(/\D/g, '');
  2661.         duration || (duration = parseFloat(computedValue));
  2662.       }
  2663.     });
  2664.  
  2665.     if (duration !== 0) {
  2666.       element.classList.add('is-transitioning');
  2667.       element.offsetWidth;
  2668.     }
  2669.   }
  2670.  
  2671.   /*!
  2672.    * Serialize all form data into a SearchParams string
  2673.    * (c) 2020 Chris Ferdinandi, MIT License, https://gomakethings.com
  2674.    * @param  {Node}   form The form to serialize
  2675.    * @return {String}      The serialized form data
  2676.    */
  2677.   function serialize(form) {
  2678.     var arr = [];
  2679.     Array.prototype.slice.call(form.elements).forEach(function(field) {
  2680.       if (
  2681.         !field.name ||
  2682.         field.disabled ||
  2683.         ['file', 'reset', 'submit', 'button'].indexOf(field.type) > -1
  2684.       )
  2685.         return;
  2686.       if (field.type === 'select-multiple') {
  2687.         Array.prototype.slice.call(field.options).forEach(function(option) {
  2688.           if (!option.selected) return;
  2689.           arr.push(
  2690.             encodeURIComponent(field.name) +
  2691.               '=' +
  2692.               encodeURIComponent(option.value)
  2693.           );
  2694.         });
  2695.         return;
  2696.       }
  2697.       if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked)
  2698.        return;
  2699.       arr.push(
  2700.         encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value)
  2701.       );
  2702.     });
  2703.     return arr.join('&');
  2704.   }
  2705.   function cookiesEnabled() {
  2706.     var cookieEnabled = navigator.cookieEnabled;
  2707.  
  2708.     if (!cookieEnabled) {
  2709.       document.cookie = 'testcookie';
  2710.       cookieEnabled = document.cookie.indexOf('testcookie') !== -1;
  2711.     }
  2712.  
  2713.     return cookieEnabled;
  2714.   }
  2715.  
  2716.   function promiseStylesheet(stylesheet) {
  2717.     var stylesheetUrl = stylesheet || theme.stylesheet;
  2718.  
  2719.     if (typeof this.stylesheetPromise === 'undefined') {
  2720.       this.stylesheetPromise = new Promise(function(resolve) {
  2721.         var link = document.querySelector('link[href="' + stylesheetUrl + '"]');
  2722.  
  2723.         if (link.loaded) resolve();
  2724.  
  2725.         link.addEventListener('load', function() {
  2726.           setTimeout(resolve, 0);
  2727.         });
  2728.       });
  2729.     }
  2730.  
  2731.     return this.stylesheetPromise;
  2732.   }
  2733.  
  2734.   return {
  2735.     setTouch: setTouch,
  2736.     isTouch: isTouch,
  2737.     enableScrollLock: enableScrollLock,
  2738.     disableScrollLock: disableScrollLock,
  2739.     debounce: debounce,
  2740.     getScript: getScript,
  2741.     prepareTransition: prepareTransition,
  2742.     serialize: serialize,
  2743.     cookiesEnabled: cookiesEnabled,
  2744.     promiseStylesheet: promiseStylesheet
  2745.   };
  2746. })();
  2747.  
  2748. theme.LibraryLoader = (function() {
  2749.   var types = {
  2750.     link: 'link',
  2751.     script: 'script'
  2752.   };
  2753.  
  2754.   var status = {
  2755.     requested: 'requested',
  2756.     loaded: 'loaded'
  2757.   };
  2758.  
  2759.   var cloudCdn = 'https://cdn.shopify.com/shopifycloud/';
  2760.  
  2761.   var libraries = {
  2762.     youtubeSdk: {
  2763.       tagId: 'youtube-sdk',
  2764.       src: 'https://www.youtube.com/iframe_api',
  2765.       type: types.script
  2766.     },
  2767.     plyrShopifyStyles: {
  2768.       tagId: 'plyr-shopify-styles',
  2769.       src: cloudCdn + 'shopify-plyr/v1.0/shopify-plyr.css',
  2770.       type: types.link
  2771.     },
  2772.     modelViewerUiStyles: {
  2773.       tagId: 'shopify-model-viewer-ui-styles',
  2774.       src: cloudCdn + 'model-viewer-ui/assets/v1.0/model-viewer-ui.css',
  2775.       type: types.link
  2776.     }
  2777.   };
  2778.  
  2779.   function load(libraryName, callback) {
  2780.     var library = libraries[libraryName];
  2781.  
  2782.     if (!library) return;
  2783.     if (library.status === status.requested) return;
  2784.  
  2785.     callback = callback || function() {};
  2786.     if (library.status === status.loaded) {
  2787.       callback();
  2788.       return;
  2789.     }
  2790.  
  2791.     library.status = status.requested;
  2792.  
  2793.     var tag;
  2794.  
  2795.     switch (library.type) {
  2796.       case types.script:
  2797.         tag = createScriptTag(library, callback);
  2798.         break;
  2799.       case types.link:
  2800.         tag = createLinkTag(library, callback);
  2801.         break;
  2802.     }
  2803.  
  2804.     tag.id = library.tagId;
  2805.     library.element = tag;
  2806.  
  2807.     var firstScriptTag = document.getElementsByTagName(library.type)[0];
  2808.     firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  2809.   }
  2810.  
  2811.   function createScriptTag(library, callback) {
  2812.     var tag = document.createElement('script');
  2813.     tag.src = library.src;
  2814.     tag.addEventListener('load', function() {
  2815.       library.status = status.loaded;
  2816.       callback();
  2817.     });
  2818.     return tag;
  2819.   }
  2820.  
  2821.   function createLinkTag(library, callback) {
  2822.     var tag = document.createElement('link');
  2823.     tag.href = library.src;
  2824.     tag.rel = 'stylesheet';
  2825.     tag.type = 'text/css';
  2826.     tag.addEventListener('load', function() {
  2827.       library.status = status.loaded;
  2828.       callback();
  2829.     });
  2830.     return tag;
  2831.   }
  2832.  
  2833.   return {
  2834.     load: load
  2835.   };
  2836. })();
  2837.  
  2838.  
  2839. /* ================ MODULES ================ */
  2840. window.theme = window.theme || {};
  2841.  
  2842. theme.Header = (function() {
  2843.   var selectors = {
  2844.     body: 'body',
  2845.     navigation: '#AccessibleNav',
  2846.     siteNavHasDropdown: '[data-has-dropdowns]',
  2847.     siteNavChildLinks: '.site-nav__child-link',
  2848.     siteNavActiveDropdown: '.site-nav--active-dropdown',
  2849.     siteNavHasCenteredDropdown: '.site-nav--has-centered-dropdown',
  2850.     siteNavCenteredDropdown: '.site-nav__dropdown--centered',
  2851.     siteNavLinkMain: '.site-nav__link--main',
  2852.     siteNavChildLink: '.site-nav__link--last',
  2853.     siteNavDropdown: '.site-nav__dropdown',
  2854.     siteHeader: '.site-header'
  2855.   };
  2856.  
  2857.   var config = {
  2858.     activeClass: 'site-nav--active-dropdown',
  2859.     childLinkClass: 'site-nav__child-link',
  2860.     rightDropdownClass: 'site-nav__dropdown--right',
  2861.     leftDropdownClass: 'site-nav__dropdown--left'
  2862.   };
  2863.  
  2864.   var cache = {};
  2865.  
  2866.   function init() {
  2867.     cacheSelectors();
  2868.     styleDropdowns(document.querySelectorAll(selectors.siteNavHasDropdown));
  2869.     positionFullWidthDropdowns();
  2870.  
  2871.     cache.parents.forEach(function(element) {
  2872.       element.addEventListener('click', submenuParentClickHandler);
  2873.     });
  2874.  
  2875.     // check when we're leaving a dropdown and close the active dropdown
  2876.     cache.siteNavChildLink.forEach(function(element) {
  2877.       element.addEventListener('focusout', submenuFocusoutHandler);
  2878.     });
  2879.  
  2880.     cache.topLevel.forEach(function(element) {
  2881.       element.addEventListener('focus', hideDropdown);
  2882.     });
  2883.  
  2884.     cache.subMenuLinks.forEach(function(element) {
  2885.       element.addEventListener('click', stopImmediatePropagation);
  2886.     });
  2887.  
  2888.     window.addEventListener('resize', resizeHandler);
  2889.   }
  2890.  
  2891.   function stopImmediatePropagation(event) {
  2892.     event.stopImmediatePropagation();
  2893.   }
  2894.  
  2895.   function cacheSelectors() {
  2896.     var navigation = document.querySelector(selectors.navigation);
  2897.  
  2898.     cache = {
  2899.       nav: navigation,
  2900.       topLevel: document.querySelectorAll(selectors.siteNavLinkMain),
  2901.       parents: navigation.querySelectorAll(selectors.siteNavHasDropdown),
  2902.       subMenuLinks: document.querySelectorAll(selectors.siteNavChildLinks),
  2903.       activeDropdown: document.querySelector(selectors.siteNavActiveDropdown),
  2904.       siteHeader: document.querySelector(selectors.siteHeader),
  2905.       siteNavChildLink: document.querySelectorAll(selectors.siteNavChildLink)
  2906.     };
  2907.   }
  2908.  
  2909.   function showDropdown(element) {
  2910.     element.classList.add(config.activeClass);
  2911.  
  2912.     if (cache.activeDropdown) hideDropdown();
  2913.  
  2914.     cache.activeDropdown = element;
  2915.  
  2916.     element
  2917.       .querySelector(selectors.siteNavLinkMain)
  2918.       .setAttribute('aria-expanded', 'true');
  2919.  
  2920.     setTimeout(function() {
  2921.       window.addEventListener('keyup', keyUpHandler);
  2922.       document.body.addEventListener('click', hideDropdown);
  2923.     }, 250);
  2924.   }
  2925.  
  2926.   function hideDropdown() {
  2927.     if (!cache.activeDropdown) return;
  2928.  
  2929.     cache.activeDropdown
  2930.       .querySelector(selectors.siteNavLinkMain)
  2931.       .setAttribute('aria-expanded', 'false');
  2932.     cache.activeDropdown.classList.remove(config.activeClass);
  2933.  
  2934.     cache.activeDropdown = document.querySelector(
  2935.       selectors.siteNavActiveDropdown
  2936.     );
  2937.  
  2938.     window.removeEventListener('keyup', keyUpHandler);
  2939.     document.body.removeEventListener('click', hideDropdown);
  2940.   }
  2941.  
  2942.   function styleDropdowns(dropdownListItems) {
  2943.     dropdownListItems.forEach(function(item) {
  2944.       var dropdownLi = item.querySelector(selectors.siteNavDropdown);
  2945.  
  2946.       if (!dropdownLi) return;
  2947.  
  2948.       if (isRightOfLogo(item)) {
  2949.         dropdownLi.classList.remove(config.leftDropdownClass);
  2950.         dropdownLi.classList.add(config.rightDropdownClass);
  2951.       } else {
  2952.         dropdownLi.classList.remove(config.rightDropdownClass);
  2953.         dropdownLi.classList.add(config.leftDropdownClass);
  2954.       }
  2955.     });
  2956.   }
  2957.  
  2958.   function isRightOfLogo(item) {
  2959.     var rect = item.getBoundingClientRect();
  2960.     var win = item.ownerDocument.defaultView;
  2961.     var leftOffset = rect.left + win.pageXOffset;
  2962.  
  2963.     var headerWidth = Math.floor(cache.siteHeader.offsetWidth) / 2;
  2964.     return leftOffset > headerWidth;
  2965.   }
  2966.  
  2967.   function positionFullWidthDropdowns() {
  2968.     document
  2969.       .querySelectorAll(selectors.siteNavHasCenteredDropdown)
  2970.       .forEach(function(el) {
  2971.         var fullWidthDropdown = el.querySelector(
  2972.           selectors.siteNavCenteredDropdown
  2973.         );
  2974.  
  2975.         var fullWidthDropdownOffset = el.offsetTop + 41;
  2976.         fullWidthDropdown.style.top = fullWidthDropdownOffset + 'px';
  2977.       });
  2978.   }
  2979.  
  2980.   function keyUpHandler(event) {
  2981.     if (event.keyCode === 27) hideDropdown();
  2982.   }
  2983.  
  2984.   function resizeHandler() {
  2985.     adjustStyleAndPosition();
  2986.   }
  2987.  
  2988.   function submenuParentClickHandler(event) {
  2989.     var element = event.currentTarget;
  2990.  
  2991.     element.classList.contains(config.activeClass)
  2992.       ? hideDropdown()
  2993.       : showDropdown(element);
  2994.   }
  2995.  
  2996.   function submenuFocusoutHandler() {
  2997.     setTimeout(function() {
  2998.       if (
  2999.         document.activeElement.classList.contains(config.childLinkClass) ||
  3000.         !cache.activeDropdown
  3001.       ) {
  3002.         return;
  3003.       }
  3004.  
  3005.       hideDropdown();
  3006.     });
  3007.   }
  3008.  
  3009.   var adjustStyleAndPosition = theme.Helpers.debounce(function() {
  3010.     styleDropdowns(document.querySelectorAll(selectors.siteNavHasDropdown));
  3011.     positionFullWidthDropdowns();
  3012.   }, 50);
  3013.  
  3014.   function unload() {
  3015.     cache.topLevel.forEach(function(element) {
  3016.       element.removeEventListener('focus', hideDropdown);
  3017.     });
  3018.  
  3019.     cache.subMenuLinks.forEach(function(element) {
  3020.       element.removeEventListener('click', stopImmediatePropagation);
  3021.     });
  3022.  
  3023.     cache.parents.forEach(function(element) {
  3024.       element.removeEventListener('click', submenuParentClickHandler);
  3025.     });
  3026.  
  3027.     cache.siteNavChildLink.forEach(function(element) {
  3028.       element.removeEventListener('focusout', submenuFocusoutHandler);
  3029.     });
  3030.  
  3031.     window.removeEventListener('resize', resizeHandler);
  3032.     window.removeEventListener('keyup', keyUpHandler);
  3033.     document.body.removeEventListener('click', hideDropdown);
  3034.   }
  3035.  
  3036.   return {
  3037.     init: init,
  3038.     unload: unload
  3039.   };
  3040. })();
  3041.  
  3042. window.theme = window.theme || {};
  3043.  
  3044. theme.MobileNav = (function() {
  3045.   var classes = {
  3046.     mobileNavOpenIcon: 'mobile-nav--open',
  3047.     mobileNavCloseIcon: 'mobile-nav--close',
  3048.     navLinkWrapper: 'mobile-nav__item',
  3049.     navLink: 'mobile-nav__link',
  3050.     subNavLink: 'mobile-nav__sublist-link',
  3051.     return: 'mobile-nav__return-btn',
  3052.     subNavActive: 'is-active',
  3053.     subNavClosing: 'is-closing',
  3054.     navOpen: 'js-menu--is-open',
  3055.     subNavShowing: 'sub-nav--is-open',
  3056.     thirdNavShowing: 'third-nav--is-open',
  3057.     subNavToggleBtn: 'js-toggle-submenu'
  3058.   };
  3059.  
  3060.   var cache = {};
  3061.   var isTransitioning;
  3062.   var activeSubNav;
  3063.   var activeTrigger;
  3064.   var menuLevel = 1;
  3065.   var mediumUpQuery = '(min-width: ' + theme.breakpoints.medium + 'px)';
  3066.   var mql = window.matchMedia(mediumUpQuery);
  3067.  
  3068.   function init() {
  3069.     cacheSelectors();
  3070.  
  3071.     if (cache.mobileNavToggle) {
  3072.       cache.mobileNavToggle.addEventListener('click', toggleMobileNav);
  3073.     }
  3074.  
  3075.     cache.subNavToggleBtns.forEach(function(element) {
  3076.       element.addEventListener('click', toggleSubNav);
  3077.     });
  3078.  
  3079.     mql.addListener(initBreakpoints);
  3080.   }
  3081.  
  3082.   function initBreakpoints() {
  3083.     if (
  3084.       mql.matches &&
  3085.      cache.mobileNavContainer.classList.contains(classes.navOpen)
  3086.    ) {
  3087.      closeMobileNav();
  3088.     }
  3089.   }
  3090.  
  3091.   function toggleMobileNav() {
  3092.     var mobileNavIsOpen = cache.mobileNavToggle.classList.contains(
  3093.       classes.mobileNavCloseIcon
  3094.     );
  3095.  
  3096.     if (mobileNavIsOpen) {
  3097.       closeMobileNav();
  3098.     } else {
  3099.       openMobileNav();
  3100.     }
  3101.   }
  3102.  
  3103.   function cacheSelectors() {
  3104.     cache = {
  3105.       pageContainer: document.querySelector('#PageContainer'),
  3106.       siteHeader: document.querySelector('.site-header'),
  3107.       mobileNavToggle: document.querySelector('.js-mobile-nav-toggle'),
  3108.       mobileNavContainer: document.querySelector('.mobile-nav-wrapper'),
  3109.       mobileNav: document.querySelector('#MobileNav'),
  3110.       sectionHeader: document.querySelector('#shopify-section-header'),
  3111.       subNavToggleBtns: document.querySelectorAll('.' + classes.subNavToggleBtn)
  3112.     };
  3113.   }
  3114.  
  3115.   function openMobileNav() {
  3116.     var translateHeaderHeight = cache.siteHeader.offsetHeight;
  3117.  
  3118.     theme.Helpers.prepareTransition(cache.mobileNavContainer);
  3119.     cache.mobileNavContainer.classList.add(classes.navOpen);
  3120.  
  3121.     cache.mobileNavContainer.style.transform =
  3122.       'translateY(' + translateHeaderHeight + 'px)';
  3123.  
  3124.     cache.pageContainer.style.transform =
  3125.       'translate3d(0, ' + cache.mobileNavContainer.scrollHeight + 'px, 0)';
  3126.  
  3127.     slate.a11y.trapFocus({
  3128.       container: cache.sectionHeader,
  3129.       elementToFocus: cache.mobileNavToggle
  3130.     });
  3131.  
  3132.     cache.mobileNavToggle.classList.add(classes.mobileNavCloseIcon);
  3133.     cache.mobileNavToggle.classList.remove(classes.mobileNavOpenIcon);
  3134.     cache.mobileNavToggle.setAttribute('aria-expanded', true);
  3135.  
  3136.     window.addEventListener('keyup', keyUpHandler);
  3137.   }
  3138.  
  3139.   function keyUpHandler(event) {
  3140.     if (event.which === 27) {
  3141.       closeMobileNav();
  3142.     }
  3143.   }
  3144.  
  3145.   function closeMobileNav() {
  3146.     theme.Helpers.prepareTransition(cache.mobileNavContainer);
  3147.     cache.mobileNavContainer.classList.remove(classes.navOpen);
  3148.     cache.mobileNavContainer.style.transform = 'translateY(-100%)';
  3149.     cache.pageContainer.setAttribute('style', '');
  3150.  
  3151.     slate.a11y.trapFocus({
  3152.       container: document.querySelector('html'),
  3153.       elementToFocus: document.body
  3154.     });
  3155.  
  3156.     cache.mobileNavContainer.addEventListener(
  3157.       'transitionend',
  3158.       mobileNavRemoveTrapFocus,
  3159.       { once: true }
  3160.     );
  3161.  
  3162.     cache.mobileNavToggle.classList.add(classes.mobileNavOpenIcon);
  3163.     cache.mobileNavToggle.classList.remove(classes.mobileNavCloseIcon);
  3164.     cache.mobileNavToggle.setAttribute('aria-expanded', false);
  3165.     cache.mobileNavToggle.focus();
  3166.  
  3167.     window.removeEventListener('keyup', keyUpHandler);
  3168.     window.scrollTo(0, 0);
  3169.   }
  3170.  
  3171.   function mobileNavRemoveTrapFocus() {
  3172.     slate.a11y.removeTrapFocus({
  3173.       container: cache.mobileNav
  3174.     });
  3175.   }
  3176.  
  3177.   function toggleSubNav(event) {
  3178.     if (isTransitioning) return;
  3179.  
  3180.     var toggleBtn = event.currentTarget;
  3181.     var isReturn = toggleBtn.classList.contains(classes.return);
  3182.  
  3183.     isTransitioning = true;
  3184.  
  3185.     if (isReturn) {
  3186.       var subNavToggleBtn = document.querySelectorAll(
  3187.         '.' + classes.subNavToggleBtn + "[data-level='" + (menuLevel - 1) + "']"
  3188.       );
  3189.  
  3190.       subNavToggleBtn.forEach(function(element) {
  3191.         element.classList.remove(classes.subNavActive);
  3192.       });
  3193.  
  3194.       if (activeTrigger) {
  3195.         activeTrigger.classList.remove(classes.subNavActive);
  3196.       }
  3197.     } else {
  3198.       toggleBtn.classList.add(classes.subNavActive);
  3199.     }
  3200.  
  3201.     activeTrigger = toggleBtn;
  3202.  
  3203.     goToSubnav(toggleBtn.getAttribute('data-target'));
  3204.   }
  3205.  
  3206.   function goToSubnav(target) {
  3207.     var targetMenu = target
  3208.       ? document.querySelector(
  3209.           '.mobile-nav__dropdown[data-parent="' + target + '"]'
  3210.         )
  3211.       : cache.mobileNav;
  3212.  
  3213.     menuLevel = targetMenu.dataset.level ? Number(targetMenu.dataset.level) : 1;
  3214.  
  3215.     if (activeSubNav) {
  3216.       theme.Helpers.prepareTransition(activeSubNav);
  3217.       activeSubNav.classList.add(classes.subNavClosing);
  3218.     }
  3219.  
  3220.     activeSubNav = targetMenu;
  3221.  
  3222.     var translateMenuHeight = targetMenu.offsetHeight;
  3223.  
  3224.     var openNavClass =
  3225.       menuLevel > 2 ? classes.thirdNavShowing : classes.subNavShowing;
  3226.  
  3227.     cache.mobileNavContainer.style.height = translateMenuHeight + 'px';
  3228.     cache.mobileNavContainer.classList.remove(classes.thirdNavShowing);
  3229.     cache.mobileNavContainer.classList.add(openNavClass);
  3230.  
  3231.     if (!target) {
  3232.       cache.mobileNavContainer.classList.remove(
  3233.         classes.thirdNavShowing,
  3234.         classes.subNavShowing
  3235.       );
  3236.     }
  3237.  
  3238.     /* if going back to first subnav, focus is on whole header */
  3239.     var container = menuLevel === 1 ? cache.sectionHeader : targetMenu;
  3240.  
  3241.     cache.mobileNavContainer.addEventListener(
  3242.       'transitionend',
  3243.       trapMobileNavFocus,
  3244.       { once: true }
  3245.     );
  3246.  
  3247.     function trapMobileNavFocus() {
  3248.       slate.a11y.trapFocus({
  3249.         container: container
  3250.       });
  3251.  
  3252.       cache.mobileNavContainer.removeEventListener(
  3253.         'transitionend',
  3254.         trapMobileNavFocus
  3255.       );
  3256.  
  3257.       isTransitioning = false;
  3258.     }
  3259.  
  3260.     // Match height of subnav
  3261.     cache.pageContainer.style.transform =
  3262.       'translateY(' + translateMenuHeight + 'px)';
  3263.  
  3264.     activeSubNav.classList.remove(classes.subNavClosing);
  3265.   }
  3266.  
  3267.   function unload() {
  3268.     mql.removeListener(initBreakpoints);
  3269.   }
  3270.  
  3271.   return {
  3272.     init: init,
  3273.     unload: unload,
  3274.     closeMobileNav: closeMobileNav
  3275.   };
  3276. })();
  3277.  
  3278. window.Modals = (function() {
  3279.   function Modal(id, name, options) {
  3280.     var defaults = {
  3281.       close: '.js-modal-close',
  3282.       open: '.js-modal-open-' + name,
  3283.       openClass: 'modal--is-active',
  3284.       closeModalOnClick: false
  3285.     };
  3286.  
  3287.     this.modal = document.getElementById(id);
  3288.  
  3289.     if (!this.modal) return false;
  3290.  
  3291.     this.nodes = {
  3292.       parents: [document.querySelector('html'), document.body]
  3293.     };
  3294.  
  3295.     this.config = Object.assign(defaults, options);
  3296.  
  3297.     this.modalIsOpen = false;
  3298.  
  3299.     this.focusOnOpen = this.config.focusOnOpen
  3300.       ? document.getElementById(this.config.focusOnOpen)
  3301.       : this.modal;
  3302.  
  3303.     this.openElement = document.querySelector(this.config.open);
  3304.     this.init();
  3305.   }
  3306.  
  3307.   Modal.prototype.init = function() {
  3308.     this.openElement.addEventListener('click', this.open.bind(this));
  3309.  
  3310.     this.modal
  3311.       .querySelector(this.config.close)
  3312.       .addEventListener('click', this.closeModal.bind(this));
  3313.   };
  3314.  
  3315.   Modal.prototype.open = function(evt) {
  3316.     var self = this;
  3317.     // Keep track if modal was opened from a click, or called by another function
  3318.     var externalCall = false;
  3319.  
  3320.     if (this.modalIsOpen) return;
  3321.  
  3322.     // Prevent following href if link is clicked
  3323.     if (evt) {
  3324.       evt.preventDefault();
  3325.     } else {
  3326.       externalCall = true;
  3327.     }
  3328.  
  3329.     // Without this, the modal opens, the click event bubbles up
  3330.     // which closes the modal.
  3331.     if (evt && evt.stopPropagation) {
  3332.      evt.stopPropagation();
  3333.     }
  3334.  
  3335.     if (this.modalIsOpen && !externalCall) {
  3336.      this.closeModal();
  3337.     }
  3338.  
  3339.     this.modal.classList.add(this.config.openClass);
  3340.  
  3341.     this.nodes.parents.forEach(function(node) {
  3342.       node.classList.add(self.config.openClass);
  3343.     });
  3344.  
  3345.     this.modalIsOpen = true;
  3346.  
  3347.     slate.a11y.trapFocus({
  3348.       container: this.modal,
  3349.       elementToFocus: this.focusOnOpen
  3350.     });
  3351.  
  3352.     this.bindEvents();
  3353.   };
  3354.  
  3355.   Modal.prototype.closeModal = function() {
  3356.     if (!this.modalIsOpen) return;
  3357.  
  3358.     document.activeElement.blur();
  3359.  
  3360.     this.modal.classList.remove(this.config.openClass);
  3361.  
  3362.     var self = this;
  3363.  
  3364.     this.nodes.parents.forEach(function(node) {
  3365.       node.classList.remove(self.config.openClass);
  3366.     });
  3367.  
  3368.     this.modalIsOpen = false;
  3369.  
  3370.     slate.a11y.removeTrapFocus({
  3371.       container: this.modal
  3372.     });
  3373.  
  3374.     this.openElement.focus();
  3375.  
  3376.     this.unbindEvents();
  3377.   };
  3378.  
  3379.   Modal.prototype.bindEvents = function() {
  3380.     this.keyupHandler = this.keyupHandler.bind(this);
  3381.     this.clickHandler = this.clickHandler.bind(this);
  3382.     document.body.addEventListener('keyup', this.keyupHandler);
  3383.     document.body.addEventListener('click', this.clickHandler);
  3384.   };
  3385.  
  3386.   Modal.prototype.unbindEvents = function() {
  3387.     document.body.removeEventListener('keyup', this.keyupHandler);
  3388.     document.body.removeEventListener('click', this.clickHandler);
  3389.   };
  3390.  
  3391.   Modal.prototype.keyupHandler = function(event) {
  3392.     if (event.keyCode === 27) {
  3393.       this.closeModal();
  3394.     }
  3395.   };
  3396.  
  3397.   Modal.prototype.clickHandler = function(event) {
  3398.     if (this.config.closeModalOnClick && !this.modal.contains(event.target)) {
  3399.      this.closeModal();
  3400.     }
  3401.   };
  3402.  
  3403.   return Modal;
  3404. })();
  3405.  
  3406. (function() {
  3407.   var selectors = {
  3408.     backButton: '.return-link'
  3409.   };
  3410.  
  3411.   var backButton = document.querySelector(selectors.backButton);
  3412.  
  3413.   if (!document.referrer || !backButton || !window.history.length) {
  3414.     return;
  3415.   }
  3416.  
  3417.   backButton.addEventListener(
  3418.     'click',
  3419.     function(evt) {
  3420.       evt.preventDefault();
  3421.  
  3422.       var referrerDomain = urlDomain(document.referrer);
  3423.       var shopDomain = urlDomain(window.location.href);
  3424.  
  3425.       if (shopDomain === referrerDomain) {
  3426.         history.back();
  3427.       }
  3428.  
  3429.       return false;
  3430.     },
  3431.     { once: true }
  3432.   );
  3433.  
  3434.   function urlDomain(url) {
  3435.     var anchor = document.createElement('a');
  3436.     anchor.ref = url;
  3437.  
  3438.     return anchor.hostname;
  3439.   }
  3440. })();
  3441.  
  3442. theme.Slideshow = (function() {
  3443.   var selectors = {
  3444.     button: '[data-slider-button]',
  3445.     indicator: '[data-slider-indicator]',
  3446.     indicators: '[data-slider-indicators]',
  3447.     pause: '[data-slider-pause]',
  3448.     slider: '[data-slider]',
  3449.     sliderItem: '[data-slider-item]',
  3450.     sliderItemLink: '[data-slider-item-link]',
  3451.     sliderTrack: '[data-slider-track]',
  3452.     sliderContainer: '[data-slider-container]'
  3453.   };
  3454.  
  3455.   var classes = {
  3456.     isPaused: 'slideshow__pause--is-paused',
  3457.     indicator: 'slider-indicators__item',
  3458.     indicatorActive: 'slick-active',
  3459.     sliderInitialized: 'slick-initialized',
  3460.     slideActive: 'slideshow__slide--active',
  3461.     slideClone: 'slick-cloned'
  3462.   };
  3463.  
  3464.   var attributes = {
  3465.     buttonNext: 'data-slider-button-next'
  3466.   };
  3467.  
  3468.   function Slideshow(container, options) {
  3469.     this.container = container;
  3470.     this.slider = this.container.querySelector(selectors.slider);
  3471.  
  3472.     if (!this.slider) return;
  3473.  
  3474.     this.eventHandlers = {};
  3475.     this.lastSlide = 0;
  3476.     this.slideIndex = 0;
  3477.     this.sliderContainer = null;
  3478.     this.slides = [];
  3479.     this.options = Object.assign(
  3480.       {},
  3481.       {
  3482.         autoplay: false,
  3483.         canUseKeyboardArrows: true,
  3484.         canUseTouchEvents: false,
  3485.         slideActiveClass: classes.slideActive,
  3486.         slideInterval: 0,
  3487.         slidesToShow: 0,
  3488.         slidesToScroll: 1,
  3489.         type: 'fade'
  3490.       },
  3491.       options
  3492.     );
  3493.  
  3494.     this.sliderContainer = this.slider.querySelector(selectors.sliderContainer);
  3495.     this.adaptHeight =
  3496.       this.sliderContainer.getAttribute('data-adapt-height') === 'true';
  3497.     this.slides = Array.from(
  3498.       this.sliderContainer.querySelectorAll(selectors.sliderItem)
  3499.     );
  3500.     // adding -1 to accomodate Array order
  3501.     this.lastSlide = this.slides.length - 1;
  3502.     this.buttons = this.container.querySelectorAll(selectors.button);
  3503.     this.pause = this.container.querySelector(selectors.pause);
  3504.     this.indicators = this.container.querySelectorAll(selectors.indicators);
  3505.  
  3506.     if (this.slides.length <= 1) return;
  3507.  
  3508.    this.timeout = 250;
  3509.  
  3510.    if (this.options.autoplay) {
  3511.      this.startAutoplay();
  3512.    }
  3513.  
  3514.    if (this.adaptHeight) {
  3515.      this.setSlideshowHeight();
  3516.    }
  3517.  
  3518.    if (this.options.type === 'slide') {
  3519.      this.isFirstSlide = false;
  3520.      this.isLastSlide = false;
  3521.      this.sliderItemWidthTotal = 0;
  3522.      this.sliderTrack = this.slider.querySelector(selectors.sliderTrack);
  3523.      // added setTimeout due to matchMedia calling too early
  3524.      // which result wrong value when getting dimension from an element
  3525.      this.sliderItemWidthTotal = 0;
  3526.      theme.Helpers.promiseStylesheet().then(
  3527.        function() {
  3528.          this._setupSlideType();
  3529.        }.bind(this)
  3530.      );
  3531.    } else {
  3532.      this.setupSlider(0);
  3533.    }
  3534.  
  3535.    this._setupEventHandlers();
  3536.  }
  3537.  
  3538.  Slideshow.prototype = Object.assign({}, Slideshow.prototype, {
  3539.    /**
  3540.     * Moves to the previous slide
  3541.     */
  3542.    previousSlide: function() {
  3543.      this._move();
  3544.    },
  3545.  
  3546.    /**
  3547.     * Moves to the next slide
  3548.     */
  3549.    nextSlide: function() {
  3550.      this._move('next');
  3551.    },
  3552.  
  3553.    /**
  3554.     * Moves to the specified slide
  3555.     * @param {Number} index - The index of the slide to move to
  3556.     */
  3557.    setSlide: function(index) {
  3558.      this._setPosition(Number(index));
  3559.    },
  3560.  
  3561.    /**
  3562.     * Starts autoplaying the slider if autoplay is enabled
  3563.     */
  3564.    startAutoplay: function() {
  3565.      this.isAutoPlaying = true;
  3566.  
  3567.      window.clearTimeout(this.autoTimeOut);
  3568.  
  3569.      this.autoTimeOut = window.setTimeout(
  3570.        function() {
  3571.          var nextSlideIndex = this._getNextSlideIndex('next');
  3572.          this._setPosition(nextSlideIndex);
  3573.        }.bind(this),
  3574.        this.options.slideInterval
  3575.      );
  3576.    },
  3577.  
  3578.    /**
  3579.     * Stops autoplaying the slider if autoplay is enabled
  3580.     */
  3581.    stopAutoplay: function() {
  3582.      this.isAutoPlaying = false;
  3583.  
  3584.      window.clearTimeout(this.autoTimeOut);
  3585.    },
  3586.  
  3587.    /**
  3588.     * Set active states for sliders and indicators
  3589.     * @param {index} integer - Slide index to set up slider from
  3590.     */
  3591.    setupSlider: function(index) {
  3592.      this.slideIndex = index;
  3593.  
  3594.      if (this.indicators.length) {
  3595.        this._setActiveIndicator(index);
  3596.      }
  3597.  
  3598.      this._setupActiveSlide(index);
  3599.    },
  3600.  
  3601.    /**
  3602.     * Removes event listeners, among other things when wanting to destroy the
  3603.     * slider instance. This method needs to be called manually and will most
  3604.     * likely be included in a section's onUnload() method.
  3605.     */
  3606.    destroy: function() {
  3607.      if (this.adaptHeight) {
  3608.        window.removeEventListener('resize', this.eventHandlers.debounceResize);
  3609.      }
  3610.      this.container.removeEventListener(
  3611.        'focus',
  3612.        this.eventHandlers.focus,
  3613.        true
  3614.      );
  3615.      this.slider.removeEventListener(
  3616.        'focusin',
  3617.        this.eventHandlers.focusIn,
  3618.        true
  3619.      );
  3620.      this.slider.removeEventListener(
  3621.        'focusout',
  3622.        this.eventHandlers.focusOut,
  3623.        true
  3624.      );
  3625.      this.container.removeEventListener('blur', this.eventHandlers.blur, true);
  3626.      if (this.buttons) {
  3627.        this.buttons.forEach(
  3628.          function(button) {
  3629.            button.removeEventListener('click', this.eventHandlers.clickButton);
  3630.          }.bind(this)
  3631.        );
  3632.      }
  3633.      this.indicators.forEach(function(indicatorWrapper) {
  3634.        indicatorWrapper.childNodes.forEach(function(indicator) {
  3635.          indicator.firstElementChild.removeEventListener(
  3636.            'click',
  3637.            this.eventHandlers.onClickIndicator
  3638.          );
  3639.          indicator.firstElementChild.removeEventListener(
  3640.            'keydown',
  3641.            this.eventHandlers.onKeydownIndicator
  3642.          );
  3643.        }, this);
  3644.      }, this);
  3645.      if (this.options.type === 'slide') {
  3646.        window.removeEventListener(
  3647.          'resize',
  3648.          this.eventHandlers.debounceResizeSlideIn
  3649.        );
  3650.        if (this.touchEvents && this.options.canUseTouchEvents) {
  3651.          this.touchEvents.destroy();
  3652.          this.touchEvents = null;
  3653.        }
  3654.      }
  3655.    },
  3656.    _setupEventHandlers: function() {
  3657.      this.eventHandlers.focus = this._onFocus.bind(this);
  3658.      this.eventHandlers.focusIn = this._onFocusIn.bind(this);
  3659.      this.eventHandlers.focusOut = this._onFocusOut.bind(this);
  3660.      this.eventHandlers.blur = this._onBlur.bind(this);
  3661.      this.eventHandlers.keyUp = this._onKeyUp.bind(this);
  3662.      this.eventHandlers.clickButton = this._onClickButton.bind(this);
  3663.      this.eventHandlers.onClickIndicator = this._onClickIndicator.bind(this);
  3664.      this.eventHandlers.onKeydownIndicator = this._onKeydownIndicator.bind(
  3665.        this
  3666.      );
  3667.      this.eventHandlers.onClickPause = this._onClickPause.bind(this);
  3668.      if (this.adaptHeight) {
  3669.        this.eventHandlers.debounceResize = theme.Helpers.debounce(
  3670.          function() {
  3671.            this.setSlideshowHeight();
  3672.          }.bind(this),
  3673.          50
  3674.        );
  3675.        window.addEventListener('resize', this.eventHandlers.debounceResize);
  3676.      }
  3677.      this.container.addEventListener('focus', this.eventHandlers.focus, true);
  3678.      this.slider.addEventListener('focusin', this.eventHandlers.focusIn, true);
  3679.      this.slider.addEventListener(
  3680.        'focusout',
  3681.        this.eventHandlers.focusOut,
  3682.        true
  3683.      );
  3684.      this.container.addEventListener('blur', this.eventHandlers.blur, true);
  3685.      if (this.buttons) {
  3686.        this.buttons.forEach(
  3687.          function(button) {
  3688.            button.addEventListener('click', this.eventHandlers.clickButton);
  3689.          }.bind(this)
  3690.        );
  3691.      }
  3692.      if (this.pause) {
  3693.        this.pause.addEventListener('click', this.eventHandlers.onClickPause);
  3694.      }
  3695.      this.indicators.forEach(function(indicatorWrapper) {
  3696.        indicatorWrapper.childNodes.forEach(function(indicator) {
  3697.          indicator.firstElementChild.addEventListener(
  3698.            'click',
  3699.            this.eventHandlers.onClickIndicator
  3700.          );
  3701.          indicator.firstElementChild.addEventListener(
  3702.            'keydown',
  3703.            this.eventHandlers.onKeydownIndicator
  3704.          );
  3705.        }, this);
  3706.      }, this);
  3707.      if (this.options.type === 'slide') {
  3708.        this.eventHandlers.debounceResizeSlideIn = theme.Helpers.debounce(
  3709.          function() {
  3710.            this.sliderItemWidthTotal = 0;
  3711.            this._setupSlideType(true);
  3712.          }.bind(this),
  3713.          50
  3714.        );
  3715.        window.addEventListener(
  3716.          'resize',
  3717.          this.eventHandlers.debounceResizeSlideIn
  3718.        );
  3719.        if (
  3720.          this.options.canUseTouchEvents &&
  3721.          this.options.slidesToScroll < this.slides.length
  3722.        ) {
  3723.          this._setupTouchEvents();
  3724.        }
  3725.      }
  3726.    },
  3727.    _setupTouchEvents: function() {
  3728.      this.touchEvents = new theme.TouchEvents(this.sliderTrack, {
  3729.        start: function() {
  3730.          this._onTouchStart();
  3731.        }.bind(this),
  3732.        move: function(event, direction, difference) {
  3733.          this._onTouchMove(event, direction, difference);
  3734.        }.bind(this),
  3735.        end: function(event, direction, difference) {
  3736.          this._onTouchEnd(event, direction, difference);
  3737.        }.bind(this)
  3738.      });
  3739.    },
  3740.    /**
  3741.     * Set slideshop for "slide-in" effect
  3742.     * @param {Boolean} onResize if function call came from resize event
  3743.     */
  3744.    _setupSlideType: function(onResize) {
  3745.      this.sliderItemWidth = Math.floor(
  3746.        this.sliderContainer.offsetWidth / this.options.slidesToShow
  3747.      );
  3748.      this.sliderTranslateXMove =
  3749.        this.sliderItemWidth * this.options.slidesToScroll;
  3750.      if (!onResize) {
  3751.        this.sliderContainer.classList.add(classes.sliderInitialized);
  3752.      }
  3753.      // Loop through all slider items
  3754.      // Set width according to the number of items to show in 1 slide
  3755.      // Set container width to accomodate all items
  3756.      this.slides.forEach(function(sliderItem, index) {
  3757.        var sliderItemLink = sliderItem.querySelector(selectors.sliderItemLink);
  3758.        sliderItem.style.width = this.sliderItemWidth + 'px';
  3759.        sliderItem.setAttribute('aria-hidden', true);
  3760.        sliderItem.setAttribute('tabindex', -1);
  3761.        this.sliderItemWidthTotal =
  3762.          this.sliderItemWidthTotal + sliderItem.offsetWidth;
  3763.        if (sliderItemLink) {
  3764.          sliderItemLink.setAttribute('tabindex', -1);
  3765.        }
  3766.        if (index < this.options.slidesToShow) {
  3767.          sliderItem.setAttribute('aria-hidden', false);
  3768.          sliderItem.classList.add(this.options.slideActiveClass);
  3769.          if (sliderItemLink) {
  3770.            sliderItemLink.setAttribute('tabindex', 0);
  3771.          }
  3772.        }
  3773.      }, this);
  3774.      this.sliderTrack.style.width =
  3775.        Math.floor(this.sliderItemWidthTotal) + 'px';
  3776.      this.sliderTrack.style.transform = 'translateX(-0px)';
  3777.      // set disabled attribute on Previous button
  3778.      if (this.buttons.length) {
  3779.        this.buttons[0].setAttribute('aria-disabled', true);
  3780.        this.buttons[1].removeAttribute('aria-disabled');
  3781.      }
  3782.      if (this.indicators.length) {
  3783.        this._setActiveIndicator(0);
  3784.      }
  3785.    },
  3786.    _onTouchStart: function() {
  3787.      this.touchStartPosition = this._getTranslateXPosition();
  3788.    },
  3789.    _onTouchMove: function(event, direction, difference) {
  3790.      // Fix touch events cause unexpected behaviour
  3791.      // when the dragging motion goes beyond the theme editor preview.
  3792.      var threshold = 80;
  3793.      if (
  3794.        Shopify.designMode &&
  3795.        (event.clientX <= threshold ||
  3796.          event.clientX >= window.innerWidth - threshold)
  3797.       ) {
  3798.         event.target.dispatchEvent(
  3799.           new MouseEvent('mouseup', {
  3800.             bubbles: true,
  3801.             cancelable: true
  3802.           })
  3803.         );
  3804.         return;
  3805.       }
  3806.  
  3807.       if (direction !== 'left' && direction !== 'right') return;
  3808.  
  3809.       this.touchMovePosition = this.touchStartPosition + difference.xPosition;
  3810.  
  3811.       this.sliderTrack.style.transform =
  3812.         'translateX(' + this.touchMovePosition + 'px';
  3813.     },
  3814.  
  3815.     _onTouchEnd: function(event, direction, difference) {
  3816.       var nextTranslateXPosition = 0;
  3817.  
  3818.       if (Object.keys(difference).length === 0) return;
  3819.  
  3820.       var slideDirection = direction === 'left' ? 'next' : '';
  3821.  
  3822.       if (direction === 'left') {
  3823.         if (this._isNextTranslateXLast(this.touchStartPosition)) {
  3824.           nextTranslateXPosition = this.touchStartPosition;
  3825.         } else {
  3826.           nextTranslateXPosition =
  3827.             this.touchStartPosition - this.sliderTranslateXMove;
  3828.         }
  3829.       } else {
  3830.         nextTranslateXPosition =
  3831.           this.touchStartPosition + this.sliderTranslateXMove;
  3832.         if (this._isNextTranslateXFirst(this.touchStartPosition)) {
  3833.           nextTranslateXPosition = 0;
  3834.         }
  3835.       }
  3836.  
  3837.       this.slideIndex = this._getNextSlideIndex(slideDirection);
  3838.  
  3839.       this.sliderTrack.style.transition = 'transform 500ms ease 0s';
  3840.       this.sliderTrack.style.transform =
  3841.         'translateX(' + nextTranslateXPosition + 'px';
  3842.  
  3843.       window.setTimeout(
  3844.         function() {
  3845.           this.sliderTrack.style.transition = '';
  3846.         }.bind(this),
  3847.         500
  3848.       );
  3849.  
  3850.       this._verifyFirstLastSlideTranslateX(nextTranslateXPosition);
  3851.  
  3852.       this._postTransitionEnd();
  3853.     },
  3854.  
  3855.     /**
  3856.      * Events handlers for next and previous button
  3857.      * @param {Object} event event handler
  3858.      */
  3859.     _onClickButton: function(event) {
  3860.       // prevent multiple clicks
  3861.       if (event.detail > 1) return;
  3862.  
  3863.       var button = event.currentTarget;
  3864.       var nextButton = button.hasAttribute(attributes.buttonNext);
  3865.  
  3866.       if (
  3867.         this.options.type === 'slide' &&
  3868.        button.getAttribute('aria-disabled') === 'true'
  3869.      ) {
  3870.        return;
  3871.       }
  3872.  
  3873.       if (this.options.autoplay && this.isAutoPlaying) {
  3874.        this.stopAutoplay();
  3875.       }
  3876.  
  3877.       if (nextButton) {
  3878.         this.nextSlide();
  3879.       } else {
  3880.         this.previousSlide();
  3881.       }
  3882.     },
  3883.  
  3884.     _onClickIndicator: function(event) {
  3885.       event.preventDefault();
  3886.  
  3887.       if (event.target.classList.contains(classes.indicatorActive)) return;
  3888.  
  3889.       if (this.options.autoplay && this.isAutoPlaying) {
  3890.        this.stopAutoplay();
  3891.       }
  3892.  
  3893.       this.slideIndex = Number(event.target.dataset.slideNumber);
  3894.       this.goToSlideByIndex(this.slideIndex);
  3895.     },
  3896.  
  3897.     goToSlideByIndex: function(index) {
  3898.       this._setPosition(index);
  3899.  
  3900.       if (this.options.type === 'slide' && this.sliderTrack) {
  3901.        this.sliderTrack.style.transition = 'transform 500ms ease 0s';
  3902.         var newPosition = index * this.slides[0].offsetWidth;
  3903.  
  3904.         this.sliderTrack.style.transform = 'translateX(-' + newPosition + 'px)';
  3905.  
  3906.         if (this.options.slidesToShow > 1) {
  3907.           this._verifyFirstLastSlideTranslateX(newPosition);
  3908.  
  3909.           if (this.buttons.length) {
  3910.             this._disableArrows();
  3911.           }
  3912.  
  3913.           this._setupMultipleActiveSlide(
  3914.             index,
  3915.             index + (this.options.slidesToShow - 1)
  3916.           );
  3917.         }
  3918.       }
  3919.     },
  3920.  
  3921.     _onKeydownIndicator: function(event) {
  3922.       if (event.keyCode !== slate.utils.keyboardKeys.ENTER) return;
  3923.  
  3924.       this._onClickIndicator(event);
  3925.  
  3926.       this.slider.focus();
  3927.     },
  3928.  
  3929.     _onClickPause: function(event) {
  3930.       if (!event.currentTarget.classList.contains(classes.isPaused)) {
  3931.         event.currentTarget.classList.add(classes.isPaused);
  3932.         this.stopAutoplay();
  3933.       } else {
  3934.         event.currentTarget.classList.remove(classes.isPaused);
  3935.         this.startAutoplay();
  3936.       }
  3937.     },
  3938.  
  3939.     _onFocus: function() {
  3940.       this.container.addEventListener('keyup', this.eventHandlers.keyUp);
  3941.     },
  3942.  
  3943.     _onFocusIn: function() {
  3944.       if (this.slider.hasAttribute('aria-live')) return;
  3945.  
  3946.       if (this.options.autoplay && this.isAutoPlaying) {
  3947.        this.stopAutoplay();
  3948.       }
  3949.  
  3950.       this.slider.setAttribute('aria-live', 'polite');
  3951.     },
  3952.  
  3953.     _onBlur: function() {
  3954.       this.container.removeEventListener('keyup', this.eventHandlers.keyUp);
  3955.     },
  3956.  
  3957.     _onFocusOut: function() {
  3958.       this.slider.removeAttribute('aria-live');
  3959.  
  3960.       // Adding a setTimeout because everytime we focus out
  3961.       // It automatically goes to <body>
  3962.       // We want to resume autoplay when focus is outside of the slideshow container
  3963.       setTimeout(
  3964.         function() {
  3965.           if (
  3966.             !document.activeElement.closest(
  3967.               '#' + this.slider.getAttribute('id')
  3968.             )
  3969.           ) {
  3970.             if (
  3971.               this.options.autoplay &&
  3972.              !this.isAutoPlaying &&
  3973.              !this.pause.classList.contains(classes.isPaused)
  3974.            ) {
  3975.              this.startAutoplay();
  3976.             }
  3977.           }
  3978.         }.bind(this),
  3979.         this.timeout
  3980.       );
  3981.     },
  3982.  
  3983.     _onKeyUp: function(event) {
  3984.       switch (event.keyCode) {
  3985.         case slate.utils.keyboardKeys.LEFTARROW:
  3986.           if (!this.options.canUseKeyboardArrows) return;
  3987.  
  3988.           if (this.options.type === 'slide' && this.isFirstSlide) {
  3989.            return;
  3990.           }
  3991.  
  3992.           this.previousSlide();
  3993.  
  3994.           break;
  3995.         case slate.utils.keyboardKeys.RIGHTARROW:
  3996.           if (!this.options.canUseKeyboardArrows) return;
  3997.  
  3998.           if (this.options.type === 'slide' && this.isLastSlide) {
  3999.            return;
  4000.           }
  4001.  
  4002.           this.nextSlide();
  4003.  
  4004.           break;
  4005.         case slate.utils.keyboardKeys.ESCAPE:
  4006.           this.slider.blur();
  4007.           break;
  4008.       }
  4009.     },
  4010.  
  4011.     _move: function(direction) {
  4012.       if (this.options.type === 'slide') {
  4013.         this.slideIndex = this._getNextSlideIndex(direction);
  4014.         this._moveSlideshow(direction);
  4015.       } else {
  4016.         var nextSlideIndex = this._getNextSlideIndex(direction);
  4017.         this._setPosition(nextSlideIndex);
  4018.       }
  4019.     },
  4020.  
  4021.     _moveSlideshow: function(direction) {
  4022.       this.direction = direction;
  4023.       var valueXToMove = 0;
  4024.  
  4025.       // Get current position of translateX
  4026.       var currentTranslateXPosition = this._getTranslateXPosition();
  4027.       var currentActiveSlidesIndex = this._getActiveSlidesIndex();
  4028.  
  4029.       // In the future, we'll use ES6 deconstructure
  4030.       // Math.min(...currentActiveSlidesIndex);
  4031.       var currentActiveSlidesMinIndex = Math.min.apply(
  4032.         Math,
  4033.         currentActiveSlidesIndex
  4034.       );
  4035.       var currentActiveSlidesMaxIndex = Math.max.apply(
  4036.         Math,
  4037.         currentActiveSlidesIndex
  4038.       );
  4039.  
  4040.       // Set the next active state depending on the direction
  4041.       // We bump up the index depending on the "slidesToShow" option
  4042.       this.nextMinIndex =
  4043.         direction === 'next'
  4044.           ? currentActiveSlidesMinIndex + this.options.slidesToShow
  4045.           : currentActiveSlidesMinIndex - this.options.slidesToShow;
  4046.       this.nextMaxIndex =
  4047.         direction === 'next'
  4048.           ? currentActiveSlidesMaxIndex + this.options.slidesToShow
  4049.           : currentActiveSlidesMinIndex - 1;
  4050.  
  4051.       this.sliderTrack.style.transition = 'transform 500ms ease 0s';
  4052.  
  4053.       if (direction === 'next') {
  4054.         valueXToMove = currentTranslateXPosition - this.sliderTranslateXMove;
  4055.         this.sliderTrack.style.transform = 'translateX(' + valueXToMove + 'px)';
  4056.       } else {
  4057.         valueXToMove = currentTranslateXPosition + this.sliderTranslateXMove;
  4058.         this.sliderTrack.style.transform = 'translateX(' + valueXToMove + 'px)';
  4059.       }
  4060.  
  4061.       this._verifyFirstLastSlideTranslateX(valueXToMove);
  4062.  
  4063.       this._postTransitionEnd();
  4064.  
  4065.       this._setupMultipleActiveSlide(this.nextMinIndex, this.nextMaxIndex);
  4066.     },
  4067.  
  4068.     _setPosition: function(nextSlideIndex) {
  4069.       this.slideIndex = nextSlideIndex;
  4070.  
  4071.       if (this.indicators.length) {
  4072.         this._setActiveIndicator(nextSlideIndex);
  4073.       }
  4074.  
  4075.       this._setupActiveSlide(nextSlideIndex);
  4076.  
  4077.       if (this.options.autoplay && this.isAutoPlaying) {
  4078.        this.startAutoplay();
  4079.       }
  4080.  
  4081.       this.container.dispatchEvent(
  4082.         new CustomEvent('slider_slide_changed', {
  4083.           detail: nextSlideIndex
  4084.         })
  4085.       );
  4086.     },
  4087.  
  4088.     _setupActiveSlide: function(index) {
  4089.       this.slides.forEach(function(slide) {
  4090.         slide.setAttribute('aria-hidden', true);
  4091.         slide.classList.remove(this.options.slideActiveClass);
  4092.       }, this);
  4093.  
  4094.       this.slides[index].setAttribute('aria-hidden', false);
  4095.       this.slides[index].classList.add(this.options.slideActiveClass);
  4096.     },
  4097.  
  4098.     /**
  4099.      * Loops through all slide items
  4100.      * Set the active state depending the direction and slide indexes
  4101.      * Because slide-in effect can have multiple items in 1 slide, we need to target multiple active elements
  4102.      * @param {String} direction "next" for next slides or empty string for previous
  4103.      * @param {*} minIndex the current active minimum index
  4104.      * @param {*} maxIndex the current active maximum index
  4105.      */
  4106.     _setupMultipleActiveSlide: function(minIndex, maxIndex) {
  4107.       this.slides.forEach(function(slide) {
  4108.         var sliderIndex = Number(slide.getAttribute('data-slider-slide-index'));
  4109.         var sliderItemLink = slide.querySelector(selectors.sliderItemLink);
  4110.  
  4111.         slide.setAttribute('aria-hidden', true);
  4112.         slide.classList.remove(this.options.slideActiveClass);
  4113.         if (sliderItemLink) {
  4114.           sliderItemLink.setAttribute('tabindex', -1);
  4115.         }
  4116.  
  4117.         if (sliderIndex >= minIndex && sliderIndex <= maxIndex) {
  4118.          slide.setAttribute('aria-hidden', false);
  4119.           slide.classList.add(this.options.slideActiveClass);
  4120.  
  4121.           if (sliderItemLink) {
  4122.             sliderItemLink.setAttribute('tabindex', 0);
  4123.           }
  4124.         }
  4125.       }, this);
  4126.     },
  4127.  
  4128.     _setActiveIndicator: function(index) {
  4129.       this.indicators.forEach(function(indicatorWrapper) {
  4130.         var activeIndicator = indicatorWrapper.querySelector(
  4131.           '.' + classes.indicatorActive
  4132.         );
  4133.  
  4134.         var nextIndicator = indicatorWrapper.childNodes[index];
  4135.  
  4136.         if (activeIndicator) {
  4137.           activeIndicator.setAttribute('aria-selected', false);
  4138.           activeIndicator.classList.remove(classes.indicatorActive);
  4139.           activeIndicator.firstElementChild.removeAttribute('aria-current');
  4140.         }
  4141.  
  4142.         nextIndicator.classList.add(classes.indicatorActive);
  4143.         nextIndicator.setAttribute('aria-selected', true);
  4144.         nextIndicator.firstElementChild.setAttribute('aria-current', true);
  4145.       }, this);
  4146.     },
  4147.  
  4148.     setSlideshowHeight: function() {
  4149.       var minAspectRatio = this.sliderContainer.getAttribute(
  4150.         'data-min-aspect-ratio'
  4151.       );
  4152.       this.sliderContainer.style.height =
  4153.         document.documentElement.offsetWidth / minAspectRatio + 'px';
  4154.     },
  4155.  
  4156.     /**
  4157.      * Increase or decrease index position of the slideshow
  4158.      * Automatically auto-rotate
  4159.      * - Last slide goes to first slide when clicking "next"
  4160.      * - First slide goes to last slide when clicking "previous"
  4161.      * @param {String} direction "next" as a String, other empty string is previous slide
  4162.      */
  4163.     _getNextSlideIndex: function(direction) {
  4164.       var counter = direction === 'next' ? 1 : -1;
  4165.  
  4166.       if (direction === 'next') {
  4167.         if (this.slideIndex === this.lastSlide) {
  4168.           return this.options.type === 'slide' ? this.lastSlide : 0;
  4169.         }
  4170.       } else if (!this.slideIndex) {
  4171.         return this.options.type === 'slide' ? 0 : this.lastSlide;
  4172.       }
  4173.  
  4174.       return this.slideIndex + counter;
  4175.     },
  4176.  
  4177.     /**
  4178.      * In "slide-in" type, multiple items are active in 1 slide
  4179.      * This will return an array containing their indexes
  4180.      */
  4181.     _getActiveSlidesIndex: function() {
  4182.       var currentActiveSlides = this.slides.filter(function(sliderItem) {
  4183.         if (sliderItem.classList.contains(this.options.slideActiveClass)) {
  4184.           return sliderItem;
  4185.         }
  4186.       }, this);
  4187.       var currentActiveSlidesIndex = currentActiveSlides.map(function(
  4188.         sliderItem
  4189.       ) {
  4190.         return Number(sliderItem.getAttribute('data-slider-slide-index'));
  4191.       });
  4192.  
  4193.       return currentActiveSlidesIndex;
  4194.     },
  4195.  
  4196.     /**
  4197.      * This checks the next "translateX" value and verifies
  4198.      * If it's at the last slide or beginning of the slide
  4199.      * So we can disable the arrow buttons
  4200.      */
  4201.     _disableArrows: function() {
  4202.       if (this.buttons.length === 0) return;
  4203.  
  4204.       var previousButton = this.buttons[0];
  4205.       var nextButton = this.buttons[1];
  4206.  
  4207.       // first slide
  4208.       if (this.isFirstSlide) {
  4209.         previousButton.setAttribute('aria-disabled', true);
  4210.       } else {
  4211.         previousButton.removeAttribute('aria-disabled');
  4212.       }
  4213.  
  4214.       // last slide
  4215.       if (this.isLastSlide) {
  4216.         nextButton.setAttribute('aria-disabled', true);
  4217.       } else {
  4218.         nextButton.removeAttribute('aria-disabled');
  4219.       }
  4220.     },
  4221.  
  4222.     /**
  4223.      * Verify if translateX reaches at first or last slide
  4224.      * @param {Number} translateXValue
  4225.      */
  4226.     _verifyFirstLastSlideTranslateX: function(translateXValue) {
  4227.       // first slide
  4228.       if (this._isNextTranslateXFirst(translateXValue)) {
  4229.         this.isFirstSlide = true;
  4230.       } else {
  4231.         this.isFirstSlide = false;
  4232.       }
  4233.  
  4234.       // last slide
  4235.       if (this._isNextTranslateXLast(translateXValue)) {
  4236.         this.isLastSlide = true;
  4237.       } else {
  4238.         this.isLastSlide = false;
  4239.       }
  4240.     },
  4241.  
  4242.     _getTranslateXPosition: function() {
  4243.       return Number(this.sliderTrack.style.transform.match(/(-?[0-9]+)/g)[0]);
  4244.     },
  4245.  
  4246.     _isNextTranslateXFirst: function(translateXValue) {
  4247.       return translateXValue === 0;
  4248.     },
  4249.  
  4250.     _isNextTranslateXLast: function(translateXValue) {
  4251.       // because translateX values are using negative, I'm converting into positive value
  4252.       var translateXValueAbsolute = Math.abs(translateXValue);
  4253.       var nextTranslateXValue =
  4254.         translateXValueAbsolute + this.sliderTranslateXMove;
  4255.  
  4256.       return nextTranslateXValue >= this.sliderItemWidthTotal;
  4257.     },
  4258.  
  4259.     _postTransitionEnd: function() {
  4260.       if (this.buttons.length) {
  4261.         this._disableArrows();
  4262.       }
  4263.  
  4264.       if (this.indicators.length) {
  4265.         this._setActiveIndicator(this.slideIndex);
  4266.       }
  4267.     }
  4268.   });
  4269.  
  4270.   return Slideshow;
  4271. })();
  4272.  
  4273. theme.Video = (function() {
  4274.   var autoplayCheckComplete = false;
  4275.   var playOnClickChecked = false;
  4276.   var playOnClick = false;
  4277.   var youtubeLoaded = false;
  4278.   var videos = {};
  4279.   var videoPlayers = [];
  4280.   var videoOptions = {
  4281.     ratio: 16 / 9,
  4282.     scrollAnimationDuration: 400,
  4283.     playerVars: {
  4284.       // eslint-disable-next-line camelcase
  4285.       iv_load_policy: 3,
  4286.       modestbranding: 1,
  4287.       autoplay: 0,
  4288.       controls: 0,
  4289.       wmode: 'opaque',
  4290.       branding: 0,
  4291.       autohide: 0,
  4292.       rel: 0
  4293.     },
  4294.     events: {
  4295.       onReady: onPlayerReady,
  4296.       onStateChange: onPlayerChange
  4297.     }
  4298.   };
  4299.   var classes = {
  4300.     playing: 'video-is-playing',
  4301.     paused: 'video-is-paused',
  4302.     loading: 'video-is-loading',
  4303.     loaded: 'video-is-loaded',
  4304.     backgroundVideoWrapper: 'video-background-wrapper',
  4305.     videoWithImage: 'video--image_with_play',
  4306.     backgroundVideo: 'video--background',
  4307.     userPaused: 'is-paused',
  4308.     supportsAutoplay: 'autoplay',
  4309.     supportsNoAutoplay: 'no-autoplay',
  4310.     wrapperMinHeight: 'video-section-wrapper--min-height'
  4311.   };
  4312.  
  4313.   var selectors = {
  4314.     section: '.video-section',
  4315.     videoWrapper: '.video-section-wrapper',
  4316.     playVideoBtn: '.video-control__play',
  4317.     closeVideoBtn: '.video-control__close-wrapper',
  4318.     pauseVideoBtn: '.video__pause',
  4319.     pauseVideoStop: '.video__pause-stop',
  4320.     pauseVideoResume: '.video__pause-resume',
  4321.     fallbackText: '.icon__fallback-text'
  4322.   };
  4323.  
  4324.   /**
  4325.    * Public functions
  4326.    */
  4327.   function init(video) {
  4328.     if (!video) return;
  4329.  
  4330.     videos[video.id] = {
  4331.       id: video.id,
  4332.       videoId: video.dataset.id,
  4333.       type: video.dataset.type,
  4334.       status:
  4335.         video.dataset.type === 'image_with_play' ? 'closed' : 'background', // closed, open, background
  4336.       video: video,
  4337.       videoWrapper: video.closest(selectors.videoWrapper),
  4338.       section: video.closest(selectors.section),
  4339.       controls: video.dataset.type === 'background' ? 0 : 1
  4340.     };
  4341.  
  4342.     if (!youtubeLoaded) {
  4343.       // This code loads the IFrame Player API code asynchronously.
  4344.       var tag = document.createElement('script');
  4345.       tag.src = 'https://www.youtube.com/iframe_api';
  4346.       var firstScriptTag = document.getElementsByTagName('script')[0];
  4347.       firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  4348.     }
  4349.  
  4350.     playOnClickCheck();
  4351.   }
  4352.  
  4353.   function customPlayVideo(playerId) {
  4354.     // Make sure we have carried out the playOnClick check first
  4355.     if (!playOnClickChecked && !playOnClick) {
  4356.      return;
  4357.     }
  4358.  
  4359.     if (playerId && typeof videoPlayers[playerId].playVideo === 'function') {
  4360.      privatePlayVideo(playerId);
  4361.     }
  4362.   }
  4363.  
  4364.   function pauseVideo(playerId) {
  4365.     if (
  4366.       videoPlayers[playerId] &&
  4367.      typeof videoPlayers[playerId].pauseVideo === 'function'
  4368.    ) {
  4369.      videoPlayers[playerId].pauseVideo();
  4370.     }
  4371.   }
  4372.  
  4373.   function loadVideos() {
  4374.     for (var key in videos) {
  4375.       if (videos.hasOwnProperty(key)) {
  4376.         createPlayer(key);
  4377.       }
  4378.     }
  4379.  
  4380.     initEvents();
  4381.     youtubeLoaded = true;
  4382.   }
  4383.  
  4384.   function editorLoadVideo(key) {
  4385.     if (!youtubeLoaded) {
  4386.       return;
  4387.     }
  4388.     createPlayer(key);
  4389.  
  4390.     initEvents();
  4391.   }
  4392.  
  4393.   /**
  4394.    * Private functions
  4395.    */
  4396.  
  4397.   function privatePlayVideo(id, clicked) {
  4398.     var videoData = videos[id];
  4399.     var player = videoPlayers[id];
  4400.     var videoWrapper = videoData.videoWrapper;
  4401.  
  4402.     if (playOnClick) {
  4403.       // playOnClick means we are probably on mobile (no autoplay).
  4404.       // setAsPlaying will show the iframe, requiring another click
  4405.       // to play the video.
  4406.       setAsPlaying(videoData);
  4407.     } else if (clicked || autoplayCheckComplete) {
  4408.       // Play if autoplay is available or clicked to play
  4409.       videoWrapper.classList.remove(classes.loading);
  4410.       setAsPlaying(videoData);
  4411.       player.playVideo();
  4412.       return;
  4413.     } else {
  4414.       player.playVideo();
  4415.     }
  4416.   }
  4417.  
  4418.   function setAutoplaySupport(supported) {
  4419.     var supportClass = supported
  4420.       ? classes.supportsAutoplay
  4421.       : classes.supportsNoAutoplay;
  4422.     document.documentElement.classList.remove(
  4423.       classes.supportsAutoplay,
  4424.       classes.supportsNoAutoplay
  4425.     );
  4426.     document.documentElement.classList.add(supportClass);
  4427.  
  4428.     if (!supported) {
  4429.       playOnClick = true;
  4430.     }
  4431.  
  4432.     autoplayCheckComplete = true;
  4433.   }
  4434.  
  4435.   function playOnClickCheck() {
  4436.     if (playOnClickChecked) {
  4437.       return;
  4438.     }
  4439.  
  4440.     if (isMobile()) {
  4441.       playOnClick = true;
  4442.     }
  4443.  
  4444.     if (playOnClick) {
  4445.       // No need to also do the autoplay check
  4446.       setAutoplaySupport(false);
  4447.     }
  4448.  
  4449.     playOnClickChecked = true;
  4450.   }
  4451.  
  4452.   // The API will call this function when each video player is ready
  4453.   function onPlayerReady(evt) {
  4454.     evt.target.setPlaybackQuality('hd1080');
  4455.     var videoData = getVideoOptions(evt);
  4456.     var videoTitle = evt.target.getVideoData().title;
  4457.     playOnClickCheck();
  4458.  
  4459.     // Prevent tabbing through YouTube player controls until visible
  4460.     document.getElementById(videoData.id).setAttribute('tabindex', '-1');
  4461.  
  4462.     sizeBackgroundVideos();
  4463.     setButtonLabels(videoData.videoWrapper, videoTitle);
  4464.  
  4465.     // Customize based on options from the video ID
  4466.     if (videoData.type === 'background') {
  4467.       evt.target.mute();
  4468.       privatePlayVideo(videoData.id);
  4469.     }
  4470.  
  4471.     videoData.videoWrapper.classList.add(classes.loaded);
  4472.   }
  4473.  
  4474.   function onPlayerChange(evt) {
  4475.     var videoData = getVideoOptions(evt);
  4476.     if (
  4477.       videoData.status === 'background' &&
  4478.      !isMobile() &&
  4479.      !autoplayCheckComplete &&
  4480.      (evt.data === YT.PlayerState.PLAYING ||
  4481.        evt.data === YT.PlayerState.BUFFERING)
  4482.    ) {
  4483.      setAutoplaySupport(true);
  4484.       autoplayCheckComplete = true;
  4485.       videoData.videoWrapper.classList.remove(classes.loading);
  4486.     }
  4487.     switch (evt.data) {
  4488.       case YT.PlayerState.ENDED:
  4489.         setAsFinished(videoData);
  4490.         break;
  4491.       case YT.PlayerState.PAUSED:
  4492.         // Seeking on a YouTube video also fires a PAUSED state change,
  4493.         // checking the state after a delay prevents us pausing the video when
  4494.         // the user is seeking instead of pausing
  4495.         setTimeout(function() {
  4496.           if (evt.target.getPlayerState() === YT.PlayerState.PAUSED) {
  4497.             setAsPaused(videoData);
  4498.           }
  4499.         }, 200);
  4500.         break;
  4501.     }
  4502.   }
  4503.  
  4504.   function setAsFinished(videoData) {
  4505.     switch (videoData.type) {
  4506.       case 'background':
  4507.         videoPlayers[videoData.id].seekTo(0);
  4508.         break;
  4509.       case 'image_with_play':
  4510.         closeVideo(videoData.id);
  4511.         toggleExpandVideo(videoData.id, false);
  4512.         break;
  4513.     }
  4514.   }
  4515.  
  4516.   function setAsPlaying(videoData) {
  4517.     var videoWrapper = videoData.videoWrapper;
  4518.     var pauseButton = videoWrapper.querySelector(selectors.pauseVideoBtn);
  4519.  
  4520.     videoWrapper.classList.remove(classes.loading);
  4521.  
  4522.     if (pauseButton.classList.contains(classes.userPaused)) {
  4523.       pauseButton.classList.remove(classes.userPaused);
  4524.     }
  4525.  
  4526.     // Do not change element visibility if it is a background video
  4527.     if (videoData.status === 'background') {
  4528.       return;
  4529.     }
  4530.  
  4531.     document.getElementById(videoData.id).setAttribute('tabindex', '0');
  4532.  
  4533.     if (videoData.type === 'image_with_play') {
  4534.       videoWrapper.classList.remove(classes.paused);
  4535.       videoWrapper.classList.add(classes.playing);
  4536.     }
  4537.  
  4538.     // Update focus to the close button so we stay within the video wrapper,
  4539.     // allowing time for the scroll animation
  4540.     setTimeout(function() {
  4541.       videoWrapper.querySelector(selectors.closeVideoBtn).focus();
  4542.     }, videoOptions.scrollAnimationDuration);
  4543.   }
  4544.  
  4545.   function setAsPaused(videoData) {
  4546.     var videoWrapper = videoData.videoWrapper;
  4547.  
  4548.     // YT's events fire after our click event. This status flag ensures
  4549.     // we don't interact with a closed or background video.
  4550.     if (videoData.type === 'image_with_play') {
  4551.       if (videoData.status === 'closed') {
  4552.         videoWrapper.classList.remove(classes.paused);
  4553.       } else {
  4554.         videoWrapper.classList.add(classes.paused);
  4555.       }
  4556.     }
  4557.  
  4558.     videoWrapper.classList.remove(classes.playing);
  4559.   }
  4560.  
  4561.   function closeVideo(playerId) {
  4562.     var videoData = videos[playerId];
  4563.     var videoWrapper = videoData.videoWrapper;
  4564.  
  4565.     document.getElementById(videoData.id).setAttribute('tabindex', '-1');
  4566.  
  4567.     videoData.status = 'closed';
  4568.  
  4569.     switch (videoData.type) {
  4570.       case 'image_with_play':
  4571.         videoPlayers[playerId].stopVideo();
  4572.         setAsPaused(videoData); // in case the video is already paused
  4573.         break;
  4574.       case 'background':
  4575.         videoPlayers[playerId].mute();
  4576.         setBackgroundVideo(playerId);
  4577.         break;
  4578.     }
  4579.  
  4580.     videoWrapper.classList.remove(classes.paused, classes.playing);
  4581.   }
  4582.  
  4583.   function getVideoOptions(evt) {
  4584.     var id = evt.target.getIframe().id;
  4585.     return videos[id];
  4586.   }
  4587.  
  4588.   function toggleExpandVideo(playerId, expand) {
  4589.     var video = videos[playerId];
  4590.     var elementTop =
  4591.       video.videoWrapper.getBoundingClientRect().top + window.pageYOffset;
  4592.     var playButton = video.videoWrapper.querySelector(selectors.playVideoBtn);
  4593.     var offset = 0;
  4594.     var newHeight = 0;
  4595.  
  4596.     if (isMobile()) {
  4597.       video.videoWrapper.parentElement.classList.toggle('page-width', !expand);
  4598.     }
  4599.  
  4600.     if (expand) {
  4601.       if (isMobile()) {
  4602.         newHeight = window.innerWidth / videoOptions.ratio;
  4603.       } else {
  4604.         newHeight = video.videoWrapper.offsetWidth / videoOptions.ratio;
  4605.       }
  4606.       offset = (window.innerHeight - newHeight) / 2;
  4607.  
  4608.       video.videoWrapper.style.height =
  4609.         video.videoWrapper.getBoundingClientRect().height + 'px';
  4610.       video.videoWrapper.classList.remove(classes.wrapperMinHeight);
  4611.       video.videoWrapper.style.height = newHeight + 'px';
  4612.  
  4613.       // Animate doesn't work in mobile editor, so we don't use it
  4614.       if (!(isMobile() && Shopify.designMode)) {
  4615.        var scrollBehavior = document.documentElement.style.scrollBehavior;
  4616.         document.documentElement.style.scrollBehavior = 'smooth';
  4617.         window.scrollTo({ top: elementTop - offset });
  4618.         document.documentElement.style.scrollBehavior = scrollBehavior;
  4619.       }
  4620.     } else {
  4621.       if (isMobile()) {
  4622.         newHeight = video.videoWrapper.dataset.mobileHeight;
  4623.       } else {
  4624.         newHeight = video.videoWrapper.dataset.desktopHeight;
  4625.       }
  4626.  
  4627.       video.videoWrapper.style.height = newHeight + 'px';
  4628.  
  4629.       setTimeout(function() {
  4630.         video.videoWrapper.classList.add(classes.wrapperMinHeight);
  4631.       }, 600);
  4632.       // Set focus on play button, but don't scroll page
  4633.       var x = window.scrollX;
  4634.       var y = window.scrollY;
  4635.       playButton.focus();
  4636.       window.scrollTo(x, y);
  4637.     }
  4638.   }
  4639.  
  4640.   function togglePause(playerId) {
  4641.     var pauseButton = videos[playerId].videoWrapper.querySelector(
  4642.       selectors.pauseVideoBtn
  4643.     );
  4644.     var paused = pauseButton.classList.contains(classes.userPaused);
  4645.     if (paused) {
  4646.       pauseButton.classList.remove(classes.userPaused);
  4647.       customPlayVideo(playerId);
  4648.     } else {
  4649.       pauseButton.classList.add(classes.userPaused);
  4650.       pauseVideo(playerId);
  4651.     }
  4652.     pauseButton.setAttribute('aria-pressed', !paused);
  4653.   }
  4654.  
  4655.   function startVideoOnClick(playerId) {
  4656.     var video = videos[playerId];
  4657.  
  4658.     // add loading class to wrapper
  4659.     video.videoWrapper.classList.add(classes.loading);
  4660.  
  4661.     // Explicity set the video wrapper height (needed for height transition)
  4662.     video.videoWrapper.style.height = video.videoWrapper.offsetHeight + 'px';
  4663.  
  4664.     video.status = 'open';
  4665.  
  4666.     switch (video.type) {
  4667.       case 'image_with_play':
  4668.         privatePlayVideo(playerId, true);
  4669.         break;
  4670.       case 'background':
  4671.         unsetBackgroundVideo(playerId, video);
  4672.         videoPlayers[playerId].unMute();
  4673.         privatePlayVideo(playerId, true);
  4674.         break;
  4675.     }
  4676.  
  4677.     toggleExpandVideo(playerId, true);
  4678.  
  4679.     // esc to close video player
  4680.     document.addEventListener('keydown', handleVideoPlayerKeydown);
  4681.   }
  4682.  
  4683.   var handleVideoPlayerKeydown = function(evt) {
  4684.     var playerId = document.activeElement.dataset.controls;
  4685.     if (evt.keyCode !== slate.utils.keyboardKeys.ESCAPE || !playerId) {
  4686.       return;
  4687.     }
  4688.  
  4689.     closeVideo(playerId);
  4690.     toggleExpandVideo(playerId, false);
  4691.   };
  4692.  
  4693.   function sizeBackgroundVideos() {
  4694.     var backgroundVideos = document.querySelectorAll(
  4695.       '.' + classes.backgroundVideo
  4696.     );
  4697.     backgroundVideos.forEach(function(el) {
  4698.       sizeBackgroundVideo(el);
  4699.     });
  4700.   }
  4701.  
  4702.   function sizeBackgroundVideo(videoPlayer) {
  4703.     if (!youtubeLoaded) {
  4704.       return;
  4705.     }
  4706.  
  4707.     if (isMobile()) {
  4708.       videoPlayer.style.cssText = null;
  4709.     } else {
  4710.       var videoWrapper = videoPlayer.closest(selectors.videoWrapper);
  4711.       var videoWidth = videoWrapper.clientWidth;
  4712.       var playerWidth = videoPlayer.clientWidth;
  4713.       var desktopHeight = videoWrapper.dataset.desktopHeight;
  4714.  
  4715.       // when screen aspect ratio differs from video, video must center and underlay one dimension
  4716.       if (videoWidth / videoOptions.ratio < desktopHeight) {
  4717.        playerWidth = Math.ceil(desktopHeight * videoOptions.ratio); // get new player width
  4718.        var styles =
  4719.          'width: ' +
  4720.          playerWidth +
  4721.          'px; height: ' +
  4722.          desktopHeight +
  4723.          'px; left: ' +
  4724.          (videoWidth - playerWidth) / 2 +
  4725.          'px; top: 0;';
  4726.        videoPlayer.style.cssText = styles;
  4727.      } else {
  4728.        // new video width < window width (gap to right)
  4729.        desktopHeight = Math.ceil(videoWidth / videoOptions.ratio); // get new player height
  4730.        var styles2 =
  4731.          'width: ' +
  4732.          videoWidth +
  4733.          'px; height: ' +
  4734.          desktopHeight +
  4735.          'px; top: ' +
  4736.          (desktopHeight - desktopHeight) / 2 +
  4737.          'px; left: 0;'; // player height is greater, offset top; reset left
  4738.        videoPlayer.style.cssText = styles2;
  4739.      }
  4740.  
  4741.      theme.Helpers.prepareTransition(videoPlayer);
  4742.      videoWrapper.classList.add(classes.loaded);
  4743.    }
  4744.  }
  4745.  
  4746.  function unsetBackgroundVideo(playerId) {
  4747.    // Switch the background video to a chrome-only player once played
  4748.    var player = document.getElementById(playerId);
  4749.    player.classList.remove(classes.backgroundVideo);
  4750.    player.classList.add(classes.videoWithImage);
  4751.  
  4752.    setTimeout(function() {
  4753.      document.getElementById(playerId).style.cssText = null;
  4754.    }, 600);
  4755.  
  4756.    videos[playerId].videoWrapper.classList.remove(
  4757.      classes.backgroundVideoWrapper
  4758.    );
  4759.    videos[playerId].videoWrapper.classList.add(classes.playing);
  4760.  
  4761.    videos[playerId].status = 'open';
  4762.  }
  4763.  
  4764.  function setBackgroundVideo(playerId) {
  4765.    var player = document.getElementById(playerId);
  4766.    player.classList.remove(classes.videoWithImage);
  4767.    player.classList.add(classes.backgroundVideo);
  4768.  
  4769.    videos[playerId].videoWrapper.classList.add(classes.backgroundVideoWrapper);
  4770.  
  4771.    videos[playerId].status = 'background';
  4772.    sizeBackgroundVideo(player);
  4773.  }
  4774.  
  4775.  function isMobile() {
  4776.    return window.innerWidth < theme.breakpoints.medium;
  4777.  }
  4778.  
  4779.  var handleWindowResize = theme.Helpers.debounce(function() {
  4780.    if (!youtubeLoaded) return;
  4781.    var key;
  4782.    var fullscreen = window.innerHeight === screen.height;
  4783.    sizeBackgroundVideos();
  4784.  
  4785.    if (isMobile()) {
  4786.      for (key in videos) {
  4787.        if (videos.hasOwnProperty(key)) {
  4788.          if (videos[key].videoWrapper.classList.contains(classes.playing)) {
  4789.            if (!fullscreen) {
  4790.              pauseVideo(key);
  4791.              setAsPaused(videos[key]);
  4792.            }
  4793.          }
  4794.          videos[key].videoWrapper.style.height =
  4795.            document.documentElement.clientWidth / videoOptions.ratio + 'px';
  4796.        }
  4797.      }
  4798.      setAutoplaySupport(false);
  4799.    } else {
  4800.      setAutoplaySupport(true);
  4801.      for (key in videos) {
  4802.        var videosWithImage = videos[key].videoWrapper.querySelectorAll(
  4803.          '.' + classes.videoWithImage
  4804.        );
  4805.        if (videosWithImage.length) {
  4806.          continue;
  4807.        }
  4808.        videoPlayers[key].playVideo();
  4809.        setAsPlaying(videos[key]);
  4810.      }
  4811.    }
  4812.  }, 200);
  4813.  
  4814.  var handleWindowScroll = theme.Helpers.debounce(function() {
  4815.    if (!youtubeLoaded) return;
  4816.  
  4817.    for (var key in videos) {
  4818.      if (videos.hasOwnProperty(key)) {
  4819.        var videoWrapper = videos[key].videoWrapper;
  4820.        var condition =
  4821.          videoWrapper.getBoundingClientRect().top +
  4822.            window.pageYOffset +
  4823.            videoWrapper.offsetHeight * 0.75 <
  4824.            window.pageYOffset ||
  4825.          videoWrapper.getBoundingClientRect().top +
  4826.            window.pageYOffset +
  4827.            videoWrapper.offsetHeight * 0.25 >
  4828.             window.pageYOffset + window.innerHeight;
  4829.  
  4830.         // Close the video if more than 75% of it is scrolled out of view
  4831.         if (videoWrapper.classList.contains(classes.playing)) {
  4832.           if (!condition) return;
  4833.           closeVideo(key);
  4834.           toggleExpandVideo(key, false);
  4835.         }
  4836.       }
  4837.     }
  4838.   }, 50);
  4839.  
  4840.   function initEvents() {
  4841.     var playVideoBtns = document.querySelectorAll(selectors.playVideoBtn);
  4842.     var closeVideoBtns = document.querySelectorAll(selectors.closeVideoBtn);
  4843.     var pauseVideoBtns = document.querySelectorAll(selectors.pauseVideoBtn);
  4844.  
  4845.     playVideoBtns.forEach(function(btn) {
  4846.       btn.addEventListener('click', function(evt) {
  4847.         var playerId = evt.currentTarget.dataset.controls;
  4848.         startVideoOnClick(playerId);
  4849.       });
  4850.     });
  4851.  
  4852.     closeVideoBtns.forEach(function(btn) {
  4853.       btn.addEventListener('click', function(evt) {
  4854.         var playerId = evt.currentTarget.dataset.controls;
  4855.  
  4856.         evt.currentTarget.blur();
  4857.         closeVideo(playerId);
  4858.         toggleExpandVideo(playerId, false);
  4859.       });
  4860.     });
  4861.  
  4862.     pauseVideoBtns.forEach(function(btn) {
  4863.       btn.addEventListener('click', function(evt) {
  4864.         var playerId = evt.currentTarget.dataset.controls;
  4865.         togglePause(playerId);
  4866.       });
  4867.     });
  4868.  
  4869.     // Listen to resize to keep a background-size:cover-like layout
  4870.     window.addEventListener('resize', handleWindowResize);
  4871.  
  4872.     window.addEventListener('scroll', handleWindowScroll);
  4873.   }
  4874.  
  4875.   function createPlayer(key) {
  4876.     var args = Object.assign(videoOptions, videos[key]);
  4877.  
  4878.     args.playerVars.controls = args.controls;
  4879.     videoPlayers[key] = new YT.Player(key, args);
  4880.   }
  4881.  
  4882.   function removeEvents() {
  4883.     document.removeEventListener('keydown', handleVideoPlayerKeydown);
  4884.     window.removeEventListener('resize', handleWindowResize);
  4885.     window.removeEventListener('scroll', handleWindowScroll);
  4886.   }
  4887.  
  4888.   function setButtonLabels(videoWrapper, title) {
  4889.     var playButtons = videoWrapper.querySelectorAll(selectors.playVideoBtn);
  4890.     var closeButton = videoWrapper.querySelector(selectors.closeVideoBtn);
  4891.     var pauseButton = videoWrapper.querySelector(selectors.pauseVideoBtn);
  4892.     var closeButtonText = closeButton.querySelector(selectors.fallbackText);
  4893.  
  4894.     var pauseButtonStop = pauseButton.querySelector(selectors.pauseVideoStop);
  4895.     var pauseButtonStopText = pauseButtonStop.querySelector(
  4896.       selectors.fallbackText
  4897.     );
  4898.  
  4899.     var pauseButtonResume = pauseButton.querySelector(
  4900.       selectors.pauseVideoResume
  4901.     );
  4902.     var pauseButtonResumeText = pauseButtonResume.querySelector(
  4903.       selectors.fallbackText
  4904.     );
  4905.  
  4906.     // Insert the video title retrieved from YouTube into the instructional text
  4907.     // for each button
  4908.     playButtons.forEach(function(playButton) {
  4909.       var playButtonText = playButton.querySelector(selectors.fallbackText);
  4910.  
  4911.       playButtonText.textContent = playButtonText.textContent.replace(
  4912.         '[video_title]',
  4913.         title
  4914.       );
  4915.     });
  4916.     closeButtonText.textContent = closeButtonText.textContent.replace(
  4917.       '[video_title]',
  4918.       title
  4919.     );
  4920.     pauseButtonStopText.textContent = pauseButtonStopText.textContent.replace(
  4921.       '[video_title]',
  4922.       title
  4923.     );
  4924.     pauseButtonResumeText.textContent = pauseButtonResumeText.textContent.replace(
  4925.       '[video_title]',
  4926.       title
  4927.     );
  4928.   }
  4929.  
  4930.   return {
  4931.     init: init,
  4932.     editorLoadVideo: editorLoadVideo,
  4933.     loadVideos: loadVideos,
  4934.     playVideo: customPlayVideo,
  4935.     pauseVideo: pauseVideo,
  4936.     removeEvents: removeEvents
  4937.   };
  4938. })();
  4939.  
  4940. theme.ProductVideo = (function() {
  4941.   var videos = {};
  4942.  
  4943.   var hosts = {
  4944.     html5: 'html5',
  4945.     youtube: 'youtube'
  4946.   };
  4947.  
  4948.   var selectors = {
  4949.     productMediaWrapper: '[data-product-single-media-wrapper]'
  4950.   };
  4951.  
  4952.   var attributes = {
  4953.     enableVideoLooping: 'enable-video-looping',
  4954.     videoId: 'video-id'
  4955.   };
  4956.  
  4957.   function init(videoContainer, sectionId) {
  4958.     if (!videoContainer) {
  4959.       return;
  4960.     }
  4961.  
  4962.     var videoElement = videoContainer.querySelector('iframe, video');
  4963.  
  4964.     if (!videoElement) {
  4965.       return;
  4966.     }
  4967.  
  4968.     var mediaId = videoContainer.getAttribute('data-media-id');
  4969.  
  4970.     videos[mediaId] = {
  4971.       mediaId: mediaId,
  4972.       sectionId: sectionId,
  4973.       host: hostFromVideoElement(videoElement),
  4974.       container: videoContainer,
  4975.       element: videoElement,
  4976.       ready: function() {
  4977.         createPlayer(this);
  4978.       }
  4979.     };
  4980.  
  4981.     var video = videos[mediaId];
  4982.  
  4983.     switch (video.host) {
  4984.       case hosts.html5:
  4985.         window.Shopify.loadFeatures([
  4986.           {
  4987.             name: 'video-ui',
  4988.             version: '1.0',
  4989.             onLoad: setupPlyrVideos
  4990.           }
  4991.         ]);
  4992.         theme.LibraryLoader.load('plyrShopifyStyles');
  4993.         break;
  4994.       case hosts.youtube:
  4995.         theme.LibraryLoader.load('youtubeSdk', setupYouTubeVideos);
  4996.         break;
  4997.     }
  4998.   }
  4999.  
  5000.   function setupPlyrVideos(errors) {
  5001.     if (errors) {
  5002.       fallbackToNativeVideo();
  5003.       return;
  5004.     }
  5005.  
  5006.     loadVideos(hosts.html5);
  5007.   }
  5008.  
  5009.   function setupYouTubeVideos() {
  5010.     if (!window.YT.Player) return;
  5011.  
  5012.     loadVideos(hosts.youtube);
  5013.   }
  5014.  
  5015.   function createPlayer(video) {
  5016.     if (video.player) {
  5017.       return;
  5018.     }
  5019.  
  5020.     var productMediaWrapper = video.container.closest(
  5021.       selectors.productMediaWrapper
  5022.     );
  5023.  
  5024.     var enableLooping = productMediaWrapper.getAttribute(
  5025.       'data-' + attributes.enableVideoLooping
  5026.     );
  5027.  
  5028.     switch (video.host) {
  5029.       case hosts.html5:
  5030.         // eslint-disable-next-line no-undef
  5031.         video.player = new Shopify.Plyr(video.element, {
  5032.           loop: { active: enableLooping }
  5033.         });
  5034.         break;
  5035.       case hosts.youtube:
  5036.         var videoId = productMediaWrapper.getAttribute(
  5037.           'data-' + attributes.videoId
  5038.         );
  5039.  
  5040.         video.player = new YT.Player(video.element, {
  5041.           videoId: videoId,
  5042.           events: {
  5043.             onStateChange: function(event) {
  5044.               if (event.data === 0 && enableLooping) event.target.seekTo(0);
  5045.             }
  5046.           }
  5047.         });
  5048.         break;
  5049.     }
  5050.  
  5051.     var pauseVideo = function() {
  5052.       if (!video.player) return;
  5053.  
  5054.       if (video.host === hosts.html5) {
  5055.         video.player.pause();
  5056.       }
  5057.  
  5058.       if (video.host === hosts.youtube && video.player.pauseVideo) {
  5059.        video.player.pauseVideo();
  5060.       }
  5061.     };
  5062.  
  5063.     productMediaWrapper.addEventListener('mediaHidden', pauseVideo);
  5064.     productMediaWrapper.addEventListener('xrLaunch', pauseVideo);
  5065.  
  5066.     productMediaWrapper.addEventListener('mediaVisible', function() {
  5067.       if (theme.Helpers.isTouch()) return;
  5068.       if (!video.player) return;
  5069.  
  5070.       if (video.host === hosts.html5) {
  5071.         video.player.play();
  5072.       }
  5073.  
  5074.       if (video.host === hosts.youtube && video.player.playVideo) {
  5075.        video.player.playVideo();
  5076.       }
  5077.     });
  5078.   }
  5079.  
  5080.   function hostFromVideoElement(video) {
  5081.     if (video.tagName === 'VIDEO') {
  5082.       return hosts.html5;
  5083.     }
  5084.  
  5085.     if (video.tagName === 'IFRAME') {
  5086.       if (
  5087.         /^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(
  5088.           video.src
  5089.         )
  5090.       ) {
  5091.         return hosts.youtube;
  5092.       }
  5093.     }
  5094.     return null;
  5095.   }
  5096.  
  5097.   function loadVideos(host) {
  5098.     for (var key in videos) {
  5099.       if (videos.hasOwnProperty(key)) {
  5100.         var video = videos[key];
  5101.         if (video.host === host) {
  5102.           video.ready();
  5103.         }
  5104.       }
  5105.     }
  5106.   }
  5107.  
  5108.   function fallbackToNativeVideo() {
  5109.     for (var key in videos) {
  5110.       if (videos.hasOwnProperty(key)) {
  5111.         var video = videos[key];
  5112.  
  5113.         if (video.nativeVideo) continue;
  5114.  
  5115.         if (video.host === hosts.html5) {
  5116.           video.element.setAttribute('controls', 'controls');
  5117.           video.nativeVideo = true;
  5118.         }
  5119.       }
  5120.     }
  5121.   }
  5122.  
  5123.   function removeSectionVideos(sectionId) {
  5124.     for (var key in videos) {
  5125.       if (videos.hasOwnProperty(key)) {
  5126.         var video = videos[key];
  5127.  
  5128.         if (video.sectionId === sectionId) {
  5129.           if (video.player) video.player.destroy();
  5130.           delete videos[key];
  5131.         }
  5132.       }
  5133.     }
  5134.   }
  5135.  
  5136.   return {
  5137.     init: init,
  5138.     hosts: hosts,
  5139.     loadVideos: loadVideos,
  5140.     removeSectionVideos: removeSectionVideos
  5141.   };
  5142. })();
  5143.  
  5144. theme.ProductModel = (function() {
  5145.   var modelJsonSections = {};
  5146.   var models = {};
  5147.   var xrButtons = {};
  5148.  
  5149.   var selectors = {
  5150.     mediaGroup: '[data-product-single-media-group]',
  5151.     xrButton: '[data-shopify-xr]'
  5152.   };
  5153.  
  5154.   function init(modelViewerContainers, sectionId) {
  5155.     modelJsonSections[sectionId] = {
  5156.       loaded: false
  5157.     };
  5158.  
  5159.     modelViewerContainers.forEach(function(modelViewerContainer, index) {
  5160.       var mediaId = modelViewerContainer.getAttribute('data-media-id');
  5161.       var modelViewerElement = modelViewerContainer.querySelector(
  5162.         'model-viewer'
  5163.       );
  5164.       var modelId = modelViewerElement.getAttribute('data-model-id');
  5165.  
  5166.       if (index === 0) {
  5167.         var mediaGroup = modelViewerContainer.closest(selectors.mediaGroup);
  5168.         var xrButton = mediaGroup.querySelector(selectors.xrButton);
  5169.         xrButtons[sectionId] = {
  5170.           element: xrButton,
  5171.           defaultId: modelId
  5172.         };
  5173.       }
  5174.  
  5175.       models[mediaId] = {
  5176.         modelId: modelId,
  5177.         sectionId: sectionId,
  5178.         container: modelViewerContainer,
  5179.         element: modelViewerElement
  5180.       };
  5181.     });
  5182.  
  5183.     window.Shopify.loadFeatures([
  5184.       {
  5185.         name: 'shopify-xr',
  5186.         version: '1.0',
  5187.         onLoad: setupShopifyXr
  5188.       },
  5189.       {
  5190.         name: 'model-viewer-ui',
  5191.         version: '1.0',
  5192.         onLoad: setupModelViewerUi
  5193.       }
  5194.     ]);
  5195.     theme.LibraryLoader.load('modelViewerUiStyles');
  5196.   }
  5197.  
  5198.   function setupShopifyXr(errors) {
  5199.     if (errors) return;
  5200.  
  5201.     if (!window.ShopifyXR) {
  5202.       document.addEventListener('shopify_xr_initialized', function() {
  5203.         setupShopifyXr();
  5204.       });
  5205.       return;
  5206.     }
  5207.  
  5208.     for (var sectionId in modelJsonSections) {
  5209.       if (modelJsonSections.hasOwnProperty(sectionId)) {
  5210.         var modelSection = modelJsonSections[sectionId];
  5211.  
  5212.         if (modelSection.loaded) continue;
  5213.         var modelJson = document.querySelector('#ModelJson-' + sectionId);
  5214.  
  5215.         window.ShopifyXR.addModels(JSON.parse(modelJson.innerHTML));
  5216.         modelSection.loaded = true;
  5217.       }
  5218.     }
  5219.     window.ShopifyXR.setupXRElements();
  5220.   }
  5221.  
  5222.   function setupModelViewerUi(errors) {
  5223.     if (errors) return;
  5224.  
  5225.     for (var key in models) {
  5226.       if (models.hasOwnProperty(key)) {
  5227.         var model = models[key];
  5228.         if (!model.modelViewerUi) {
  5229.           model.modelViewerUi = new Shopify.ModelViewerUI(model.element);
  5230.         }
  5231.         setupModelViewerListeners(model);
  5232.       }
  5233.     }
  5234.   }
  5235.  
  5236.   function setupModelViewerListeners(model) {
  5237.     var xrButton = xrButtons[model.sectionId];
  5238.  
  5239.     model.container.addEventListener('mediaVisible', function() {
  5240.       xrButton.element.setAttribute('data-shopify-model3d-id', model.modelId);
  5241.       if (theme.Helpers.isTouch()) return;
  5242.       model.modelViewerUi.play();
  5243.     });
  5244.  
  5245.     model.container.addEventListener('mediaHidden', function() {
  5246.       xrButton.element.setAttribute(
  5247.         'data-shopify-model3d-id',
  5248.         xrButton.defaultId
  5249.       );
  5250.       model.modelViewerUi.pause();
  5251.     });
  5252.  
  5253.     model.container.addEventListener('xrLaunch', function() {
  5254.       model.modelViewerUi.pause();
  5255.     });
  5256.   }
  5257.  
  5258.   function removeSectionModels(sectionId) {
  5259.     for (var key in models) {
  5260.       if (models.hasOwnProperty(key)) {
  5261.         var model = models[key];
  5262.         if (model.sectionId === sectionId) {
  5263.           models[key].modelViewerUi.destroy();
  5264.           delete models[key];
  5265.         }
  5266.       }
  5267.     }
  5268.     delete modelJsonSections[sectionId];
  5269.   }
  5270.  
  5271.   return {
  5272.     init: init,
  5273.     removeSectionModels: removeSectionModels
  5274.   };
  5275. })();
  5276.  
  5277. window.theme = window.theme || {};
  5278.  
  5279. theme.FormStatus = (function() {
  5280.   var selectors = {
  5281.     statusMessage: '[data-form-status]'
  5282.   };
  5283.  
  5284.   function init() {
  5285.     var statusMessages = document.querySelectorAll(selectors.statusMessage);
  5286.  
  5287.     statusMessages.forEach(function(statusMessage) {
  5288.       statusMessage.setAttribute('tabindex', -1);
  5289.       statusMessage.focus();
  5290.  
  5291.       statusMessage.addEventListener(
  5292.         'blur',
  5293.         function(evt) {
  5294.           evt.target.removeAttribute('tabindex');
  5295.         },
  5296.         { once: true }
  5297.       );
  5298.     });
  5299.   }
  5300.  
  5301.   return {
  5302.     init: init
  5303.   };
  5304. })();
  5305.  
  5306. theme.Hero = (function() {
  5307.   var classes = {
  5308.     indexSectionFlush: 'index-section--flush'
  5309.   };
  5310.  
  5311.   var selectors = {
  5312.     heroFixedWidthContent: '.hero-fixed-width__content',
  5313.     heroFixedWidthImage: '.hero-fixed-width__image'
  5314.   };
  5315.  
  5316.   function hero(el, sectionId) {
  5317.     var hero = document.querySelector(el);
  5318.     var layout = hero.getAttribute('data-layout');
  5319.     var parentSection = document.querySelector('#shopify-section-' + sectionId);
  5320.     var heroContent = parentSection.querySelector(
  5321.       selectors.heroFixedWidthContent
  5322.     );
  5323.     var heroImage = parentSection.querySelector(selectors.heroFixedWidthImage);
  5324.  
  5325.     if (layout !== 'fixed_width') {
  5326.       return;
  5327.     }
  5328.  
  5329.     parentSection.classList.remove(classes.indexSectionFlush);
  5330.     heroFixedHeight();
  5331.  
  5332.     window.addEventListener('resize', function() {
  5333.       theme.Helpers.debounce(function() {
  5334.         heroFixedHeight();
  5335.       }, 50);
  5336.     });
  5337.  
  5338.     function heroFixedHeight() {
  5339.       var contentHeight;
  5340.       var imageHeight;
  5341.  
  5342.       if (heroContent) {
  5343.         contentHeight = heroContent.offsetHeight + 50;
  5344.       }
  5345.  
  5346.       if (heroImage) {
  5347.         imageHeight = heroImage.offsetHeight;
  5348.       }
  5349.  
  5350.       if (contentHeight > imageHeight) {
  5351.         heroImage.style.minHeight = contentHeight + 'px';
  5352.       }
  5353.     }
  5354.   }
  5355.  
  5356.   return hero;
  5357. })();
  5358.  
  5359. // prettier-ignore
  5360. window.theme = window.theme || {};
  5361.  
  5362. theme.SearchResultsTemplate = (function() {
  5363.   function renderResults(products, isLoading, searchQuery) {
  5364.     return [
  5365.       '<div class="predictive-search">',
  5366.       renderHeader(products, isLoading),
  5367.       renderProducts(products, searchQuery),
  5368.       '</div>'
  5369.     ].join('');
  5370.   }
  5371.  
  5372.   function renderHeader(products, isLoading) {
  5373.     if (products.length === 0) {
  5374.       return '';
  5375.     }
  5376.  
  5377.     return [
  5378.       '<div class="predictive-search-title">',
  5379.       '<h3 id="predictive-search" class="predictive-search-title__content">' +
  5380.         theme.strings.products +
  5381.         '</h3>',
  5382.       '<span class="predictive-search-title__loading-spinner">' +
  5383.         (isLoading
  5384.           ? '<span class= "icon-predictive-search-spinner" ></span >'
  5385.           : '') +
  5386.         '</span>',
  5387.       '</div>'
  5388.     ].join('');
  5389.   }
  5390.  
  5391.   function loadingState() {
  5392.     return [
  5393.       '<div class="predictive-search">',
  5394.       '<div class="predictive-search-loading">',
  5395.       '<span class="visually-hidden">' + theme.strings.loading + '</span>',
  5396.       '<span class="predictive-search-loading__icon">',
  5397.       '<span class="icon-predictive-search-spinner"></span>',
  5398.       '</span>',
  5399.       '</div>',
  5400.       '</div>'
  5401.     ].join('');
  5402.   }
  5403.  
  5404.   function renderViewAll(searchQuery) {
  5405.     return [
  5406.       '<button type="submit" class="predictive-search-view-all__button" tabindex="-1">',
  5407.       theme.strings.searchFor +
  5408.         '<span class="predictive-search-view-all__query"> &ldquo;' +
  5409.         _htmlEscape(searchQuery) +
  5410.         '&rdquo;</span>',
  5411.       '</button>'
  5412.     ].join('');
  5413.   }
  5414.  
  5415.   function renderProducts(products, searchQuery) {
  5416.     var resultsCount = products.length;
  5417.  
  5418.     return [
  5419.       '<ul id="predictive-search-results" class="predictive-search__list" role="listbox" aria-labelledby="predictive-search">',
  5420.       products
  5421.         .map(function(product, index) {
  5422.           return renderProduct(normalizeProduct(product), index, resultsCount);
  5423.         })
  5424.         .join(''),
  5425.       '<li id="search-all" class="predictive-search-view-all" role="option" data-search-result>' +
  5426.         renderViewAll(searchQuery) +
  5427.         '</li>',
  5428.       '</ul>'
  5429.     ].join('');
  5430.   }
  5431.  
  5432.   function renderProduct(product, index, resultsCount) {
  5433.     return [
  5434.       '<li id="search-result-' +
  5435.        index +
  5436.        '" class="predictive-search-item" role="option" data-search-result>',
  5437.       '<a class="predictive-search-item__link" href="' +
  5438.        product.url +
  5439.        '" tabindex="-1">',
  5440.       '<div class="predictive-search__column predictive-search__column--image" data-image-loading-animation>',
  5441.       renderProductImage(product),
  5442.       '</div>',
  5443.       '<div class="predictive-search__column predictive-search__column--content ' +
  5444.        (getDetailsCount() ? '' : 'predictive-search__column--center') +
  5445.        '">',
  5446.       '<span class="predictive-search-item__title">',
  5447.       '<span class="predictive-search-item__title-text">' +
  5448.         product.title +
  5449.         '</span>',
  5450.       '</span>' + (getDetailsCount() ? renderProductDetails(product) : ''),
  5451.       '<span class="visually-hidden">, </span>',
  5452.       '<span class="visually-hidden">' +
  5453.         getNumberOfResultsString(index + 1, resultsCount) +
  5454.         '</span>',
  5455.       '</div>',
  5456.       '</a>',
  5457.       '</li>'
  5458.     ].join('');
  5459.   }
  5460.  
  5461.   function renderProductImage(product) {
  5462.     if (product.image === null) {
  5463.       return '';
  5464.     }
  5465.  
  5466.     return (
  5467.       '<img class="predictive-search-item__image lazyload" src="' +
  5468.      product.image.url +
  5469.      '" data-src="' +
  5470.      product.image.url +
  5471.      '" data-image alt="' +
  5472.      product.image.alt +
  5473.      '" />'
  5474.     );
  5475.   }
  5476.  
  5477.   function renderProductDetails(product) {
  5478.     return [
  5479.       '<dl class="predictive-search-item__details price' +
  5480.        (product.isOnSale ? ' price--on-sale' : '') +
  5481.        (!product.available ? ' price--sold-out' : '') +
  5482.        (!product.isPriceVaries && product.isCompareVaries
  5483.          ? ' price--compare-price-hidden'
  5484.          : '') +
  5485.        '">',
  5486.       '<div class="predictive-search-item__detail">',
  5487.       renderVendor(product),
  5488.       '</div>',
  5489.       '<div class="predictive-search-item__detail predictive-search-item__detail--inline">' +
  5490.         renderProductPrice(product),
  5491.       '</div>',
  5492.       '</dl>'
  5493.     ].join('');
  5494.   }
  5495.   function renderProductPrice(product) {
  5496.     if (!theme.settings.predictiveSearchShowPrice) {
  5497.       return '';
  5498.     }
  5499.  
  5500.     var accessibilityAnnounceComma = '<span class="visually-hidden">, </span>';
  5501.  
  5502.     var priceMarkup =
  5503.       '<div class="price__regular">' + renderPrice(product) + '</div>';
  5504.  
  5505.     var salePriceMarkup =
  5506.       '<div class="price__sale">' + renderSalePrice(product) + '</div>';
  5507.  
  5508.     return (
  5509.       accessibilityAnnounceComma +
  5510.       '<div class="price__pricing-group">' +
  5511.       (product.isOnSale ? salePriceMarkup : priceMarkup) +
  5512.       '</div>'
  5513.     );
  5514.   }
  5515.  
  5516.   function renderSalePrice(product) {
  5517.     return [
  5518.       '<dt>',
  5519.       '<span class="visually-hidden">' + theme.strings.salePrice + '</span>',
  5520.       '</dt>',
  5521.       '<dd>',
  5522.       '<span class="predictive-search-item__price predictive-search-item__price--sale">' +
  5523.         (product.isPriceVaries
  5524.           ? theme.strings.fromLowestPrice.replace('[price]', product.price)
  5525.           : product.price) +
  5526.         '</span>',
  5527.       '</dd>',
  5528.       '<div class="price__compare">' + renderCompareAtPrice(product) + '</div>'
  5529.     ].join('');
  5530.   }
  5531.  
  5532.   function renderCompareAtPrice(product) {
  5533.     return [
  5534.       '<dt>',
  5535.       '<span class="visually-hidden">' +
  5536.         theme.strings.regularPrice +
  5537.         '</span> ',
  5538.       '</dt>',
  5539.       '<dd>',
  5540.       '<span class="predictive-search-item__price predictive-search-item__price--compare">' +
  5541.         product.compareAtPrice +
  5542.         '</span>',
  5543.       '</dd>'
  5544.     ].join('');
  5545.   }
  5546.  
  5547.   function renderPrice(product) {
  5548.     return [
  5549.       '<dt>',
  5550.       '<span class="visually-hidden">' + theme.strings.regularPrice + '</span>',
  5551.       '</dt>',
  5552.       '<dd>',
  5553.       '<span class="predictive-search-item__price">' +
  5554.         (product.isPriceVaries
  5555.           ? theme.strings.fromLowestPrice.replace('[price]', product.price)
  5556.           : product.price) +
  5557.         '</span>',
  5558.       '</dd>'
  5559.     ].join('');
  5560.   }
  5561.  
  5562.   function renderVendor(product) {
  5563.     if (!theme.settings.predictiveSearchShowVendor || product.vendor === '') {
  5564.       return '';
  5565.     }
  5566.  
  5567.     return [
  5568.       '<dt>',
  5569.       '<span class="visually-hidden">' + theme.strings.vendor + '</span>',
  5570.       '</dt>',
  5571.       '<dd class="predictive-search-item__vendor">' + product.vendor + '</dd>'
  5572.     ].join('');
  5573.   }
  5574.  
  5575.   function normalizeProduct(product) {
  5576.     var productOrVariant =
  5577.       product.variants.length > 0 ? product.variants[0] : product;
  5578.  
  5579.     return {
  5580.       url: productOrVariant.url,
  5581.       image: getProductImage(product),
  5582.       title: product.title,
  5583.       vendor: product.vendor || '',
  5584.       price: theme.Currency.formatMoney(product.price_min, theme.moneyFormat),
  5585.       compareAtPrice: theme.Currency.formatMoney(
  5586.         product.compare_at_price_min,
  5587.         theme.moneyFormat
  5588.       ),
  5589.       available: product.available,
  5590.       isOnSale: isOnSale(product),
  5591.       isPriceVaries: isPriceVaries(product),
  5592.       isCompareVaries: isCompareVaries(product)
  5593.     };
  5594.   }
  5595.  
  5596.   function getProductImage(product) {
  5597.     var image;
  5598.     var featuredImage;
  5599.  
  5600.     if (product.variants.length > 0 && product.variants[0].image !== null) {
  5601.      featuredImage = product.variants[0].featured_image;
  5602.     } else if (product.image) {
  5603.       featuredImage = product.featured_image;
  5604.     } else {
  5605.       image = null;
  5606.     }
  5607.  
  5608.     if (image !== null) {
  5609.       image = {
  5610.         url: theme.Images.getSizedImageUrl(featuredImage.url, '100x'),
  5611.         alt: featuredImage.alt
  5612.       };
  5613.     }
  5614.  
  5615.     return image;
  5616.   }
  5617.  
  5618.   function isOnSale(product) {
  5619.     return (
  5620.       product.compare_at_price_min !== null &&
  5621.      parseInt(product.compare_at_price_min, 10) >
  5622.        parseInt(product.price_min, 10)
  5623.    );
  5624.   }
  5625.  
  5626.   function isPriceVaries(product) {
  5627.     return product.price_max !== product.price_min;
  5628.   }
  5629.  
  5630.   function isCompareVaries(product) {
  5631.     return product.compare_at_price_max !== product.compare_at_price_min;
  5632.   }
  5633.  
  5634.   // Returns the number of optional product details to be shown,
  5635.   // values of the detailsList need to be boolean.
  5636.   function getDetailsCount() {
  5637.     var detailsList = [
  5638.       theme.settings.predictiveSearchShowPrice,
  5639.       theme.settings.predictiveSearchShowVendor
  5640.     ];
  5641.  
  5642.     var detailsCount = detailsList.reduce(function(acc, detail) {
  5643.       return acc + (detail ? 1 : 0);
  5644.     }, 0);
  5645.  
  5646.     return detailsCount;
  5647.   }
  5648.  
  5649.   function getNumberOfResultsString(resultNumber, resultsCount) {
  5650.     return theme.strings.number_of_results
  5651.       .replace('[result_number]', resultNumber)
  5652.       .replace('[results_count]', resultsCount);
  5653.   }
  5654.  
  5655.   function _htmlEscape(input) {
  5656.     return input
  5657.       .replace(/&/g, '&amp;')
  5658.       .replace(/</g, '&lt;')
  5659.      .replace(/>/g, '&gt;')
  5660.       .replace(/"/g, '&quot;')
  5661.       .replace(/'/g, '&#39;');
  5662.   }
  5663.  
  5664.   return function(data) {
  5665.     var products = data.products || [];
  5666.     var isLoading = data.isLoading;
  5667.     var searchQuery = data.searchQuery || '';
  5668.  
  5669.     if (isLoading && products.length === 0) {
  5670.      return loadingState();
  5671.     }
  5672.  
  5673.     return renderResults(products, isLoading, searchQuery);
  5674.   };
  5675. })();
  5676.  
  5677. window.theme = window.theme || {};
  5678.  
  5679. (function() {
  5680.   // (a11y) This function will be used by the Predictive Search Component
  5681.   // to announce the number of search results
  5682.   function numberOfResultsTemplateFct(data) {
  5683.     if (data.products.length === 1) {
  5684.       return theme.strings.one_result_found;
  5685.     } else {
  5686.       return theme.strings.number_of_results_found.replace(
  5687.         '[results_count]',
  5688.         data.products.length
  5689.       );
  5690.     }
  5691.   }
  5692.  
  5693.   // (a11y) This function will be used by the Predictive Search Component
  5694.   // to announce that it's loading results
  5695.   function loadingResultsMessageTemplateFct() {
  5696.     return theme.strings.loading;
  5697.   }
  5698.  
  5699.   function isPredictiveSearchSupported() {
  5700.     var shopifyFeatures = JSON.parse(
  5701.       document.getElementById('shopify-features').textContent
  5702.     );
  5703.  
  5704.     return shopifyFeatures.predictiveSearch;
  5705.   }
  5706.  
  5707.   function isPredictiveSearchEnabled() {
  5708.     return window.theme.settings.predictiveSearchEnabled;
  5709.   }
  5710.  
  5711.   function canInitializePredictiveSearch() {
  5712.     return isPredictiveSearchSupported() && isPredictiveSearchEnabled();
  5713.   }
  5714.  
  5715.   // listen for search submits and validate query
  5716.   function validateSearchHandler(searchEl, submitEl) {
  5717.     submitEl.addEventListener(
  5718.       'click',
  5719.       validateSearchInput.bind(this, searchEl)
  5720.     );
  5721.   }
  5722.  
  5723.   // if there is nothing in the search field, prevent submit
  5724.   function validateSearchInput(searchEl, evt) {
  5725.     var isInputValueEmpty = searchEl.value.trim().length === 0;
  5726.     if (!isInputValueEmpty) {
  5727.       return;
  5728.     }
  5729.  
  5730.     if (typeof evt !== 'undefined') {
  5731.       evt.preventDefault();
  5732.     }
  5733.  
  5734.     searchEl.focus();
  5735.   }
  5736.  
  5737.   window.theme.SearchPage = (function() {
  5738.     var selectors = {
  5739.       searchReset: '[data-search-page-predictive-search-clear]',
  5740.       searchInput: '[data-search-page-predictive-search-input]',
  5741.       searchSubmit: '[data-search-page-predictive-search-submit]',
  5742.       searchResults: '[data-predictive-search-mount="default"]'
  5743.     };
  5744.  
  5745.     var componentInstance;
  5746.  
  5747.     function init(config) {
  5748.       var searchInput = document.querySelector(selectors.searchInput);
  5749.       var searchSubmit = document.querySelector(selectors.searchSubmit);
  5750.       var searchUrl = searchInput.dataset.baseUrl;
  5751.  
  5752.       componentInstance = new window.Shopify.theme.PredictiveSearchComponent({
  5753.         selectors: {
  5754.           input: selectors.searchInput,
  5755.           reset: selectors.searchReset,
  5756.           result: selectors.searchResults
  5757.         },
  5758.         searchUrl: searchUrl,
  5759.         resultTemplateFct: window.theme.SearchResultsTemplate,
  5760.         numberOfResultsTemplateFct: numberOfResultsTemplateFct,
  5761.         loadingResultsMessageTemplateFct: loadingResultsMessageTemplateFct,
  5762.         onOpen: function(nodes) {
  5763.           if (config.isTabletAndUp) {
  5764.             return;
  5765.           }
  5766.  
  5767.           var searchInputBoundingRect = searchInput.getBoundingClientRect();
  5768.           var bodyHeight = document.body.offsetHeight;
  5769.           var offset = 50;
  5770.           var resultsMaxHeight =
  5771.             bodyHeight - searchInputBoundingRect.bottom - offset;
  5772.  
  5773.           nodes.result.style.maxHeight = resultsMaxHeight + 'px';
  5774.         },
  5775.         onBeforeDestroy: function(nodes) {
  5776.           // If the viewport width changes from mobile to tablet
  5777.           // reset the top position of the results
  5778.           nodes.result.style.maxHeight = '';
  5779.         }
  5780.       });
  5781.  
  5782.       validateSearchHandler(searchInput, searchSubmit);
  5783.     }
  5784.  
  5785.     function unload() {
  5786.       if (!componentInstance) {
  5787.         return;
  5788.       }
  5789.       componentInstance.destroy();
  5790.       componentInstance = null;
  5791.     }
  5792.  
  5793.     return {
  5794.       init: init,
  5795.       unload: unload
  5796.     };
  5797.   })();
  5798.  
  5799.   window.theme.SearchHeader = (function() {
  5800.     var selectors = {
  5801.       searchInput: '[data-predictive-search-drawer-input]',
  5802.       searchResults: '[data-predictive-search-mount="drawer"]',
  5803.       searchFormContainer: '[data-search-form-container]',
  5804.       searchSubmit: '[data-search-form-submit]'
  5805.     };
  5806.  
  5807.     var componentInstance;
  5808.  
  5809.     function init(config) {
  5810.       var searchInput = document.querySelector(selectors.searchInput);
  5811.       var searchSubmit = document.querySelector(selectors.searchSubmit);
  5812.       var searchUrl = searchInput.dataset.baseUrl;
  5813.  
  5814.       componentInstance = new window.Shopify.theme.PredictiveSearchComponent({
  5815.         selectors: {
  5816.           input: selectors.searchInput,
  5817.           result: selectors.searchResults
  5818.         },
  5819.         searchUrl: searchUrl,
  5820.         resultTemplateFct: window.theme.SearchResultsTemplate,
  5821.         numberOfResultsTemplateFct: numberOfResultsTemplateFct,
  5822.         numberOfResults: config.numberOfResults,
  5823.         loadingResultsMessageTemplateFct: loadingResultsMessageTemplateFct,
  5824.         onInputBlur: function() {
  5825.           return false;
  5826.         },
  5827.         onOpen: function(nodes) {
  5828.           var searchInputBoundingRect = searchInput.getBoundingClientRect();
  5829.  
  5830.           // For tablet screens and up, stop the scroll area from extending past
  5831.           // the bottom of the screen because we're locking the body scroll
  5832.           var maxHeight =
  5833.             window.innerHeight -
  5834.             searchInputBoundingRect.bottom -
  5835.             (config.isTabletAndUp ? 20 : 0);
  5836.  
  5837.           nodes.result.style.top = config.isTabletAndUp
  5838.             ? ''
  5839.             : searchInputBoundingRect.bottom + 'px';
  5840.           nodes.result.style.maxHeight = maxHeight + 'px';
  5841.         },
  5842.         onClose: function(nodes) {
  5843.           nodes.result.style.maxHeight = '';
  5844.         },
  5845.         onBeforeDestroy: function(nodes) {
  5846.           // If the viewport width changes from mobile to tablet
  5847.           // reset the top position of the results
  5848.           nodes.result.style.top = '';
  5849.         }
  5850.       });
  5851.  
  5852.       validateSearchHandler(searchInput, searchSubmit);
  5853.     }
  5854.  
  5855.     function unload() {
  5856.       if (!componentInstance) {
  5857.         return;
  5858.       }
  5859.  
  5860.       componentInstance.destroy();
  5861.       componentInstance = null;
  5862.     }
  5863.  
  5864.     function clearAndClose() {
  5865.       if (!componentInstance) {
  5866.         return;
  5867.       }
  5868.  
  5869.       componentInstance.clearAndClose();
  5870.     }
  5871.  
  5872.     return {
  5873.       init: init,
  5874.       unload: unload,
  5875.       clearAndClose: clearAndClose
  5876.     };
  5877.   })();
  5878.  
  5879.   window.theme.Search = (function() {
  5880.     var classes = {
  5881.       searchTemplate: 'template-search'
  5882.     };
  5883.     var selectors = {
  5884.       siteHeader: '.site-header'
  5885.     };
  5886.     var mediaQueryList = {
  5887.       mobile: window.matchMedia('(max-width: 749px)'),
  5888.       tabletAndUp: window.matchMedia('(min-width: 750px)')
  5889.     };
  5890.  
  5891.     function init() {
  5892.       if (!document.querySelector(selectors.siteHeader)) {
  5893.         return;
  5894.       }
  5895.  
  5896.       if (!canInitializePredictiveSearch()) {
  5897.         return;
  5898.       }
  5899.  
  5900.       Object.keys(mediaQueryList).forEach(function(device) {
  5901.         mediaQueryList[device].addListener(initSearchAccordingToViewport);
  5902.       });
  5903.  
  5904.       initSearchAccordingToViewport();
  5905.     }
  5906.  
  5907.     function initSearchAccordingToViewport() {
  5908.       theme.SearchDrawer.close();
  5909.       theme.SearchHeader.unload();
  5910.       theme.SearchPage.unload();
  5911.  
  5912.       if (mediaQueryList.mobile.matches) {
  5913.         theme.SearchHeader.init({
  5914.           numberOfResults: 4,
  5915.           isTabletAndUp: false
  5916.         });
  5917.  
  5918.         if (isSearchPage()) {
  5919.           theme.SearchPage.init({ isTabletAndUp: false });
  5920.         }
  5921.       } else {
  5922.         // Tablet and up
  5923.         theme.SearchHeader.init({
  5924.           numberOfResults: 4,
  5925.           isTabletAndUp: true
  5926.         });
  5927.  
  5928.         if (isSearchPage()) {
  5929.           theme.SearchPage.init({ isTabletAndUp: true });
  5930.         }
  5931.       }
  5932.     }
  5933.  
  5934.     function isSearchPage() {
  5935.       return document.body.classList.contains(classes.searchTemplate);
  5936.     }
  5937.  
  5938.     function unload() {
  5939.       theme.SearchHeader.unload();
  5940.       theme.SearchPage.unload();
  5941.     }
  5942.  
  5943.     return {
  5944.       init: init,
  5945.       unload: unload
  5946.     };
  5947.   })();
  5948. })();
  5949.  
  5950. window.theme = window.theme || {};
  5951.  
  5952. theme.SearchDrawer = (function() {
  5953.   var selectors = {
  5954.     headerSection: '[data-header-section]',
  5955.     drawer: '[data-predictive-search-drawer]',
  5956.     drawerOpenButton: '[data-predictive-search-open-drawer]',
  5957.     headerSearchInput: '[data-predictive-search-drawer-input]',
  5958.     predictiveSearchWrapper: '[data-predictive-search-mount="drawer"]'
  5959.   };
  5960.  
  5961.   var drawerInstance;
  5962.  
  5963.   function init() {
  5964.     setAccessibilityProps();
  5965.  
  5966.     drawerInstance = new theme.Drawers('SearchDrawer', 'top', {
  5967.       onDrawerOpen: function() {
  5968.         setHeight();
  5969.         theme.MobileNav.closeMobileNav();
  5970.         lockBodyScroll();
  5971.       },
  5972.       onDrawerClose: function() {
  5973.         theme.SearchHeader.clearAndClose();
  5974.         var drawerOpenButton = document.querySelector(
  5975.           selectors.drawerOpenButton
  5976.         );
  5977.  
  5978.         if (drawerOpenButton) drawerOpenButton.focus();
  5979.  
  5980.         unlockBodyScroll();
  5981.       },
  5982.       withPredictiveSearch: true,
  5983.       elementToFocusOnOpen: document.querySelector(selectors.headerSearchInput)
  5984.     });
  5985.   }
  5986.  
  5987.   function setAccessibilityProps() {
  5988.     var drawerOpenButton = document.querySelector(selectors.drawerOpenButton);
  5989.  
  5990.     if (drawerOpenButton) {
  5991.       drawerOpenButton.setAttribute('aria-controls', 'SearchDrawer');
  5992.       drawerOpenButton.setAttribute('aria-expanded', 'false');
  5993.       drawerOpenButton.setAttribute('aria-controls', 'dialog');
  5994.     }
  5995.   }
  5996.  
  5997.   function setHeight() {
  5998.     var searchDrawer = document.querySelector(selectors.drawer);
  5999.     var headerHeight = document.querySelector(selectors.headerSection)
  6000.       .offsetHeight;
  6001.  
  6002.     searchDrawer.style.height = headerHeight + 'px';
  6003.   }
  6004.  
  6005.   function close() {
  6006.     drawerInstance.close();
  6007.   }
  6008.  
  6009.   function lockBodyScroll() {
  6010.     theme.Helpers.enableScrollLock();
  6011.   }
  6012.  
  6013.   function unlockBodyScroll() {
  6014.     theme.Helpers.disableScrollLock();
  6015.   }
  6016.  
  6017.   return {
  6018.     init: init,
  6019.     close: close
  6020.   };
  6021. })();
  6022.  
  6023. theme.Disclosure = (function() {
  6024.   var selectors = {
  6025.     disclosureForm: '[data-disclosure-form]',
  6026.     disclosureList: '[data-disclosure-list]',
  6027.     disclosureToggle: '[data-disclosure-toggle]',
  6028.     disclosureInput: '[data-disclosure-input]',
  6029.     disclosureOptions: '[data-disclosure-option]'
  6030.   };
  6031.  
  6032.   var classes = {
  6033.     listVisible: 'disclosure-list--visible'
  6034.   };
  6035.  
  6036.   function Disclosure(disclosure) {
  6037.     this.container = disclosure;
  6038.     this._cacheSelectors();
  6039.     this._setupListeners();
  6040.   }
  6041.  
  6042.   Disclosure.prototype = Object.assign({}, Disclosure.prototype, {
  6043.     _cacheSelectors: function() {
  6044.       this.cache = {
  6045.         disclosureForm: this.container.closest(selectors.disclosureForm),
  6046.         disclosureList: this.container.querySelector(selectors.disclosureList),
  6047.         disclosureToggle: this.container.querySelector(
  6048.           selectors.disclosureToggle
  6049.         ),
  6050.         disclosureInput: this.container.querySelector(
  6051.           selectors.disclosureInput
  6052.         ),
  6053.         disclosureOptions: this.container.querySelectorAll(
  6054.           selectors.disclosureOptions
  6055.         )
  6056.       };
  6057.     },
  6058.  
  6059.     _setupListeners: function() {
  6060.       this.eventHandlers = this._setupEventHandlers();
  6061.  
  6062.       this.cache.disclosureToggle.addEventListener(
  6063.         'click',
  6064.         this.eventHandlers.toggleList
  6065.       );
  6066.  
  6067.       this.cache.disclosureOptions.forEach(function(disclosureOption) {
  6068.         disclosureOption.addEventListener(
  6069.           'click',
  6070.           this.eventHandlers.connectOptions
  6071.         );
  6072.       }, this);
  6073.  
  6074.       this.container.addEventListener(
  6075.         'keyup',
  6076.         this.eventHandlers.onDisclosureKeyUp
  6077.       );
  6078.  
  6079.       this.cache.disclosureList.addEventListener(
  6080.         'focusout',
  6081.         this.eventHandlers.onDisclosureListFocusOut
  6082.       );
  6083.  
  6084.       this.cache.disclosureToggle.addEventListener(
  6085.         'focusout',
  6086.         this.eventHandlers.onDisclosureToggleFocusOut
  6087.       );
  6088.  
  6089.       document.body.addEventListener('click', this.eventHandlers.onBodyClick);
  6090.     },
  6091.  
  6092.     _setupEventHandlers: function() {
  6093.       return {
  6094.         connectOptions: this._connectOptions.bind(this),
  6095.         toggleList: this._toggleList.bind(this),
  6096.         onBodyClick: this._onBodyClick.bind(this),
  6097.         onDisclosureKeyUp: this._onDisclosureKeyUp.bind(this),
  6098.         onDisclosureListFocusOut: this._onDisclosureListFocusOut.bind(this),
  6099.         onDisclosureToggleFocusOut: this._onDisclosureToggleFocusOut.bind(this)
  6100.       };
  6101.     },
  6102.  
  6103.     _connectOptions: function(event) {
  6104.       event.preventDefault();
  6105.  
  6106.       this._submitForm(event.currentTarget.dataset.value);
  6107.     },
  6108.  
  6109.     _onDisclosureToggleFocusOut: function(event) {
  6110.       var disclosureLostFocus =
  6111.         this.container.contains(event.relatedTarget) === false;
  6112.  
  6113.       if (disclosureLostFocus) {
  6114.         this._hideList();
  6115.       }
  6116.     },
  6117.  
  6118.     _onDisclosureListFocusOut: function(event) {
  6119.       var childInFocus = event.currentTarget.contains(event.relatedTarget);
  6120.  
  6121.       var isVisible = this.cache.disclosureList.classList.contains(
  6122.         classes.listVisible
  6123.       );
  6124.  
  6125.       if (isVisible && !childInFocus) {
  6126.        this._hideList();
  6127.       }
  6128.     },
  6129.  
  6130.     _onDisclosureKeyUp: function(event) {
  6131.       if (event.which !== slate.utils.keyboardKeys.ESCAPE) return;
  6132.       this._hideList();
  6133.       this.cache.disclosureToggle.focus();
  6134.     },
  6135.  
  6136.     _onBodyClick: function(event) {
  6137.       var isOption = this.container.contains(event.target);
  6138.       var isVisible = this.cache.disclosureList.classList.contains(
  6139.         classes.listVisible
  6140.       );
  6141.  
  6142.       if (isVisible && !isOption) {
  6143.        this._hideList();
  6144.       }
  6145.     },
  6146.  
  6147.     _submitForm: function(value) {
  6148.       this.cache.disclosureInput.value = value;
  6149.       this.cache.disclosureForm.submit();
  6150.     },
  6151.  
  6152.     _hideList: function() {
  6153.       this.cache.disclosureList.classList.remove(classes.listVisible);
  6154.       this.cache.disclosureToggle.setAttribute('aria-expanded', false);
  6155.     },
  6156.  
  6157.     _toggleList: function() {
  6158.       var ariaExpanded =
  6159.         this.cache.disclosureToggle.getAttribute('aria-expanded') === 'true';
  6160.       this.cache.disclosureList.classList.toggle(classes.listVisible);
  6161.       this.cache.disclosureToggle.setAttribute('aria-expanded', !ariaExpanded);
  6162.     },
  6163.  
  6164.     destroy: function() {
  6165.       this.cache.disclosureToggle.removeEventListener(
  6166.         'click',
  6167.         this.eventHandlers.toggleList
  6168.       );
  6169.  
  6170.       this.cache.disclosureOptions.forEach(function(disclosureOption) {
  6171.         disclosureOption.removeEventListener(
  6172.           'click',
  6173.           this.eventHandlers.connectOptions
  6174.         );
  6175.       }, this);
  6176.  
  6177.       this.container.removeEventListener(
  6178.         'keyup',
  6179.         this.eventHandlers.onDisclosureKeyUp
  6180.       );
  6181.  
  6182.       this.cache.disclosureList.removeEventListener(
  6183.         'focusout',
  6184.         this.eventHandlers.onDisclosureListFocusOut
  6185.       );
  6186.  
  6187.       this.cache.disclosureToggle.removeEventListener(
  6188.         'focusout',
  6189.         this.eventHandlers.onDisclosureToggleFocusOut
  6190.       );
  6191.  
  6192.       document.body.removeEventListener(
  6193.         'click',
  6194.         this.eventHandlers.onBodyClick
  6195.       );
  6196.     }
  6197.   });
  6198.  
  6199.   return Disclosure;
  6200. })();
  6201.  
  6202. theme.Zoom = (function() {
  6203.   var selectors = {
  6204.     imageZoom: '[data-image-zoom]'
  6205.   };
  6206.  
  6207.   var classes = {
  6208.     zoomImg: 'zoomImg'
  6209.   };
  6210.  
  6211.   var attributes = {
  6212.     imageZoomTarget: 'data-image-zoom-target'
  6213.   };
  6214.  
  6215.   function Zoom(container) {
  6216.     this.container = container;
  6217.     this.cache = {};
  6218.     this.url = container.dataset.zoom;
  6219.  
  6220.     this._cacheSelectors();
  6221.  
  6222.     if (!this.cache.sourceImage) return;
  6223.  
  6224.     this._duplicateImage();
  6225.   }
  6226.  
  6227.   Zoom.prototype = Object.assign({}, Zoom.prototype, {
  6228.     _cacheSelectors: function() {
  6229.       this.cache = {
  6230.         sourceImage: this.container.querySelector(selectors.imageZoom)
  6231.       };
  6232.     },
  6233.  
  6234.     _init: function() {
  6235.       var targetWidth = this.cache.targetImage.width;
  6236.       var targetHeight = this.cache.targetImage.height;
  6237.  
  6238.       if (this.cache.sourceImage === this.cache.targetImage) {
  6239.         this.sourceWidth = targetWidth;
  6240.         this.sourceHeight = targetHeight;
  6241.       } else {
  6242.         this.sourceWidth = this.cache.sourceImage.width;
  6243.         this.sourceHeight = this.cache.sourceImage.height;
  6244.       }
  6245.  
  6246.       this.xRatio =
  6247.         (this.cache.sourceImage.width - targetWidth) / this.sourceWidth;
  6248.       this.yRatio =
  6249.         (this.cache.sourceImage.height - targetHeight) / this.sourceHeight;
  6250.     },
  6251.  
  6252.     _start: function(e) {
  6253.       this._init();
  6254.       this._move(e);
  6255.     },
  6256.  
  6257.     _stop: function() {
  6258.       this.cache.targetImage.style.opacity = 0;
  6259.     },
  6260.  
  6261.     /**
  6262.      * Sets the correct coordinates top and left position in px
  6263.      * It sets a limit within between 0 and the max height of the image
  6264.      * So when the mouse leaves the target image, it could
  6265.      * never go above or beyond the target image zone
  6266.      */
  6267.     _setTopLeftMaxValues: function(top, left) {
  6268.       return {
  6269.         left: Math.max(Math.min(left, this.sourceWidth), 0),
  6270.         top: Math.max(Math.min(top, this.sourceHeight), 0)
  6271.       };
  6272.     },
  6273.  
  6274.     _move: function(e) {
  6275.       // get left and top position within the "source image" zone
  6276.       var left =
  6277.         e.pageX -
  6278.         (this.cache.sourceImage.getBoundingClientRect().left + window.scrollX);
  6279.       var top =
  6280.         e.pageY -
  6281.         (this.cache.sourceImage.getBoundingClientRect().top + window.scrollY);
  6282.       // make sure the left and top position don't go
  6283.       // above or beyond the target image zone
  6284.       var position = this._setTopLeftMaxValues(top, left);
  6285.  
  6286.       top = position.top;
  6287.       left = position.left;
  6288.  
  6289.       this.cache.targetImage.style.left = -(left * -this.xRatio) + 'px';
  6290.       this.cache.targetImage.style.top = -(top * -this.yRatio) + 'px';
  6291.       this.cache.targetImage.style.opacity = 1;
  6292.     },
  6293.  
  6294.     /**
  6295.      * This loads a high resolution image
  6296.      * via the data attributes url
  6297.      * It adds all necessary CSS styles and adds to the container
  6298.      */
  6299.     _duplicateImage: function() {
  6300.       this._loadImage()
  6301.         .then(
  6302.           function(image) {
  6303.             this.cache.targetImage = image;
  6304.             image.style.width = image.width + 'px';
  6305.             image.style.height = image.height + 'px';
  6306.             image.style.position = 'absolute';
  6307.             image.style.maxWidth = 'none';
  6308.             image.style.maxHeight = 'none';
  6309.             image.style.opacity = 0;
  6310.             image.style.border = 'none';
  6311.             image.style.left = 0;
  6312.             image.style.top = 0;
  6313.  
  6314.             this.container.appendChild(image);
  6315.  
  6316.             this._init();
  6317.  
  6318.             this._start = this._start.bind(this);
  6319.             this._stop = this._stop.bind(this);
  6320.             this._move = this._move.bind(this);
  6321.  
  6322.             this.container.addEventListener('mouseenter', this._start);
  6323.             this.container.addEventListener('mouseleave', this._stop);
  6324.             this.container.addEventListener('mousemove', this._move);
  6325.  
  6326.             this.container.style.position = 'relative';
  6327.             this.container.style.overflow = 'hidden';
  6328.           }.bind(this)
  6329.         )
  6330.         .catch(function(error) {
  6331.           // eslint-disable-next-line no-console
  6332.           console.warn('Error fetching image', error);
  6333.         });
  6334.     },
  6335.  
  6336.     _loadImage: function() {
  6337.       // eslint-disable-next-line
  6338.       return new Promise(function(resolve, reject) {
  6339.           var image = new Image();
  6340.           image.setAttribute('role', 'presentation');
  6341.           image.setAttribute(attributes.imageZoomTarget, true);
  6342.           image.classList.add(classes.zoomImg);
  6343.           image.src = this.url;
  6344.  
  6345.           image.addEventListener('load', function() {
  6346.             resolve(image);
  6347.           });
  6348.  
  6349.           image.addEventListener('error', function(error) {
  6350.             reject(error);
  6351.           });
  6352.         }.bind(this)
  6353.       );
  6354.     },
  6355.  
  6356.     unload: function() {
  6357.       var targetImage = this.container.querySelector(
  6358.         '[' + attributes.imageZoomTarget + ']'
  6359.       );
  6360.       if (targetImage) {
  6361.         targetImage.remove();
  6362.       }
  6363.  
  6364.       this.container.removeEventListener('mouseenter', this._start);
  6365.       this.container.removeEventListener('mouseleave', this._stop);
  6366.       this.container.removeEventListener('mousemove', this._move);
  6367.     }
  6368.   });
  6369.  
  6370.   return Zoom;
  6371. })();
  6372.  
  6373.  
  6374. /* ================ TEMPLATES ================ */
  6375. (function() {
  6376.   var filterBys = document.querySelectorAll('[data-blog-tag-filter]');
  6377.  
  6378.   if (!filterBys.length) return;
  6379.  
  6380.   slate.utils.resizeSelects(filterBys);
  6381.  
  6382.   filterBys.forEach(function(filterBy) {
  6383.     filterBy.addEventListener('change', function(evt) {
  6384.       location.href = evt.target.value;
  6385.     });
  6386.   });
  6387. })();
  6388.  
  6389. window.theme = theme || {};
  6390.  
  6391. theme.customerTemplates = (function() {
  6392.   var selectors = {
  6393.     RecoverHeading: '#RecoverHeading',
  6394.     RecoverEmail: '#RecoverEmail',
  6395.     LoginHeading: '#LoginHeading'
  6396.   };
  6397.  
  6398.   function initEventListeners() {
  6399.     this.recoverHeading = document.querySelector(selectors.RecoverHeading);
  6400.     this.recoverEmail = document.querySelector(selectors.RecoverEmail);
  6401.     this.loginHeading = document.querySelector(selectors.LoginHeading);
  6402.     var recoverPassword = document.getElementById('RecoverPassword');
  6403.     var hideRecoverPasswordLink = document.getElementById(
  6404.       'HideRecoverPasswordLink'
  6405.     );
  6406.  
  6407.     // Show reset password form
  6408.     if (recoverPassword) {
  6409.       recoverPassword.addEventListener(
  6410.         'click',
  6411.         function(evt) {
  6412.           evt.preventDefault();
  6413.           showRecoverPasswordForm();
  6414.           this.recoverHeading.setAttribute('tabindex', '-1');
  6415.           this.recoverHeading.focus();
  6416.         }.bind(this)
  6417.       );
  6418.     }
  6419.  
  6420.     // Hide reset password form
  6421.     if (hideRecoverPasswordLink) {
  6422.       hideRecoverPasswordLink.addEventListener(
  6423.         'click',
  6424.         function(evt) {
  6425.           evt.preventDefault();
  6426.           hideRecoverPasswordForm();
  6427.           this.loginHeading.setAttribute('tabindex', '-1');
  6428.           this.loginHeading.focus();
  6429.         }.bind(this)
  6430.       );
  6431.     }
  6432.  
  6433.     if (this.recoverHeading) {
  6434.       this.recoverHeading.addEventListener('blur', function(evt) {
  6435.         evt.target.removeAttribute('tabindex');
  6436.       });
  6437.     }
  6438.  
  6439.     if (this.loginHeading) {
  6440.       this.loginHeading.addEventListener('blur', function(evt) {
  6441.         evt.target.removeAttribute('tabindex');
  6442.       });
  6443.     }
  6444.   }
  6445.  
  6446.   /**
  6447.    *
  6448.    *  Show/Hide recover password form
  6449.    *
  6450.    */
  6451.  
  6452.   function showRecoverPasswordForm() {
  6453.     document.getElementById('RecoverPasswordForm').classList.remove('hide');
  6454.     document.getElementById('CustomerLoginForm').classList.add('hide');
  6455.  
  6456.     if (this.recoverEmail.getAttribute('aria-invalid') === 'true') {
  6457.       this.recoverEmail.focus();
  6458.     }
  6459.   }
  6460.  
  6461.   function hideRecoverPasswordForm() {
  6462.     document.getElementById('RecoverPasswordForm').classList.add('hide');
  6463.     document.getElementById('CustomerLoginForm').classList.remove('hide');
  6464.   }
  6465.  
  6466.   /**
  6467.    *
  6468.    *  Show reset password success message
  6469.    *
  6470.    */
  6471.   function resetPasswordSuccess() {
  6472.     var formState = document.querySelector('.reset-password-success');
  6473.  
  6474.     // check if reset password form was successfully submited.
  6475.     if (!formState) {
  6476.       return;
  6477.     }
  6478.  
  6479.     // show success message
  6480.     var resetSuccess = document.getElementById('ResetSuccess');
  6481.     resetSuccess.classList.remove('hide');
  6482.     resetSuccess.focus();
  6483.   }
  6484.  
  6485.   /**
  6486.    *
  6487.    *  Show/hide customer address forms
  6488.    *
  6489.    */
  6490.   function customerAddressForm() {
  6491.     var newAddressForm = document.getElementById('AddressNewForm');
  6492.     var newAddressFormButton = document.getElementById('AddressNewButton');
  6493.  
  6494.     if (!newAddressForm) {
  6495.       return;
  6496.     }
  6497.  
  6498.     // Initialize observers on address selectors, defined in shopify_common.js
  6499.     if (Shopify) {
  6500.       // eslint-disable-next-line no-new
  6501.       new Shopify.CountryProvinceSelector(
  6502.         'AddressCountryNew',
  6503.         'AddressProvinceNew',
  6504.         {
  6505.           hideElement: 'AddressProvinceContainerNew'
  6506.         }
  6507.       );
  6508.     }
  6509.  
  6510.     // Initialize each edit form's country/province selector
  6511.     document
  6512.       .querySelectorAll('.address-country-option')
  6513.       .forEach(function(option) {
  6514.         var formId = option.dataset.formId;
  6515.         var countrySelector = 'AddressCountry_' + formId;
  6516.         var provinceSelector = 'AddressProvince_' + formId;
  6517.         var containerSelector = 'AddressProvinceContainer_' + formId;
  6518.  
  6519.         // eslint-disable-next-line no-new
  6520.         new Shopify.CountryProvinceSelector(countrySelector, provinceSelector, {
  6521.           hideElement: containerSelector
  6522.         });
  6523.       });
  6524.  
  6525.     // Toggle new/edit address forms
  6526.     document.querySelectorAll('.address-new-toggle').forEach(function(button) {
  6527.       button.addEventListener('click', function() {
  6528.         var isExpanded =
  6529.           newAddressFormButton.getAttribute('aria-expanded') === 'true';
  6530.  
  6531.         newAddressForm.classList.toggle('hide');
  6532.         newAddressFormButton.setAttribute('aria-expanded', !isExpanded);
  6533.         newAddressFormButton.focus();
  6534.       });
  6535.     });
  6536.  
  6537.     document.querySelectorAll('.address-edit-toggle').forEach(function(button) {
  6538.       button.addEventListener('click', function(evt) {
  6539.         var formId = evt.target.dataset.formId;
  6540.         var editButton = document.getElementById('EditFormButton_' + formId);
  6541.         var editAddress = document.getElementById('EditAddress_' + formId);
  6542.         var isExpanded = editButton.getAttribute('aria-expanded') === 'true';
  6543.  
  6544.         editAddress.classList.toggle('hide');
  6545.         editButton.setAttribute('aria-expanded', !isExpanded);
  6546.         editButton.focus();
  6547.       });
  6548.     });
  6549.  
  6550.     document.querySelectorAll('.address-delete').forEach(function(button) {
  6551.       button.addEventListener('click', function(evt) {
  6552.         var target = evt.target.dataset.target;
  6553.         var confirmMessage = evt.target.dataset.confirmMessage;
  6554.  
  6555.         // eslint-disable-next-line no-alert
  6556.         if (
  6557.           confirm(
  6558.             confirmMessage || 'Are you sure you wish to delete this address?'
  6559.           )
  6560.         ) {
  6561.           Shopify.postLink(target, {
  6562.             parameters: { _method: 'delete' }
  6563.           });
  6564.         }
  6565.       });
  6566.     });
  6567.   }
  6568.  
  6569.   /**
  6570.    *
  6571.    *  Check URL for reset password hash
  6572.    *
  6573.    */
  6574.   function checkUrlHash() {
  6575.     var hash = window.location.hash;
  6576.  
  6577.     // Allow deep linking to recover password form
  6578.     if (hash === '#recover') {
  6579.       showRecoverPasswordForm.bind(this)();
  6580.     }
  6581.   }
  6582.  
  6583.   return {
  6584.     init: function() {
  6585.       initEventListeners();
  6586.       checkUrlHash();
  6587.       resetPasswordSuccess();
  6588.       customerAddressForm();
  6589.     }
  6590.   };
  6591. })();
  6592.  
  6593.  
  6594. /*================ SECTIONS ================*/
  6595. window.theme = window.theme || {};
  6596.  
  6597. theme.Cart = (function() {
  6598.   var selectors = {
  6599.     cartCount: '[data-cart-count]',
  6600.     cartCountBubble: '[data-cart-count-bubble]',
  6601.     cartDiscount: '[data-cart-discount]',
  6602.     cartDiscountTitle: '[data-cart-discount-title]',
  6603.     cartDiscountAmount: '[data-cart-discount-amount]',
  6604.     cartDiscountWrapper: '[data-cart-discount-wrapper]',
  6605.     cartErrorMessage: '[data-cart-error-message]',
  6606.     cartErrorMessageWrapper: '[data-cart-error-message-wrapper]',
  6607.     cartItem: '[data-cart-item]',
  6608.     cartItemDetails: '[data-cart-item-details]',
  6609.     cartItemDiscount: '[data-cart-item-discount]',
  6610.     cartItemDiscountedPriceGroup: '[data-cart-item-discounted-price-group]',
  6611.     cartItemDiscountTitle: '[data-cart-item-discount-title]',
  6612.     cartItemDiscountAmount: '[data-cart-item-discount-amount]',
  6613.     cartItemDiscountList: '[data-cart-item-discount-list]',
  6614.     cartItemFinalPrice: '[data-cart-item-final-price]',
  6615.     cartItemImage: '[data-cart-item-image]',
  6616.     cartItemLinePrice: '[data-cart-item-line-price]',
  6617.     cartItemOriginalPrice: '[data-cart-item-original-price]',
  6618.     cartItemPrice: '[data-cart-item-price]',
  6619.     cartItemPriceList: '[data-cart-item-price-list]',
  6620.     cartItemProperty: '[data-cart-item-property]',
  6621.     cartItemPropertyName: '[data-cart-item-property-name]',
  6622.     cartItemPropertyValue: '[data-cart-item-property-value]',
  6623.     cartItemRegularPriceGroup: '[data-cart-item-regular-price-group]',
  6624.     cartItemRegularPrice: '[data-cart-item-regular-price]',
  6625.     cartItemTitle: '[data-cart-item-title]',
  6626.     cartItemOption: '[data-cart-item-option]',
  6627.     cartItemSellingPlanName: '[data-cart-item-selling-plan-name]',
  6628.     cartLineItems: '[data-cart-line-items]',
  6629.     cartNote: '[data-cart-notes]',
  6630.     cartQuantityErrorMessage: '[data-cart-quantity-error-message]',
  6631.     cartQuantityErrorMessageWrapper:
  6632.       '[data-cart-quantity-error-message-wrapper]',
  6633.     cartRemove: '[data-cart-remove]',
  6634.     cartStatus: '[data-cart-status]',
  6635.     cartSubtotal: '[data-cart-subtotal]',
  6636.     cartTableCell: '[data-cart-table-cell]',
  6637.     cartWrapper: '[data-cart-wrapper]',
  6638.     emptyPageContent: '[data-empty-page-content]',
  6639.     quantityInput: '[data-quantity-input]',
  6640.     quantityInputMobile: '[data-quantity-input-mobile]',
  6641.     quantityInputDesktop: '[data-quantity-input-desktop]',
  6642.     quantityLabelMobile: '[data-quantity-label-mobile]',
  6643.     quantityLabelDesktop: '[data-quantity-label-desktop]',
  6644.     inputQty: '[data-quantity-input]',
  6645.     thumbnails: '.cart__image',
  6646.     unitPrice: '[data-unit-price]',
  6647.     unitPriceBaseUnit: '[data-unit-price-base-unit]',
  6648.     unitPriceGroup: '[data-unit-price-group]'
  6649.   };
  6650.  
  6651.   var classes = {
  6652.     cartNoCookies: 'cart--no-cookies',
  6653.     cartRemovedProduct: 'cart__removed-product',
  6654.     thumbnails: 'cart__image',
  6655.     hide: 'hide',
  6656.     inputError: 'input--error'
  6657.   };
  6658.  
  6659.   var attributes = {
  6660.     cartItemIndex: 'data-cart-item-index',
  6661.     cartItemKey: 'data-cart-item-key',
  6662.     cartItemQuantity: 'data-cart-item-quantity',
  6663.     cartItemTitle: 'data-cart-item-title',
  6664.     cartItemUrl: 'data-cart-item-url',
  6665.     quantityItem: 'data-quantity-item'
  6666.   };
  6667.  
  6668.   var mediumUpQuery = '(min-width: ' + theme.breakpoints.medium + 'px)';
  6669.  
  6670.   function Cart(container) {
  6671.     this.container = container;
  6672.     this.thumbnails = this.container.querySelectorAll(selectors.thumbnails);
  6673.     this.quantityInputs = this.container.querySelectorAll(selectors.inputQty);
  6674.     this.ajaxEnabled =
  6675.       this.container.getAttribute('data-ajax-enabled') === 'true';
  6676.  
  6677.     this._handleInputQty = theme.Helpers.debounce(
  6678.       this._handleInputQty.bind(this),
  6679.       500
  6680.     );
  6681.     this.setQuantityFormControllers = this.setQuantityFormControllers.bind(
  6682.       this
  6683.     );
  6684.     this._onNoteChange = this._onNoteChange.bind(this);
  6685.     this._onRemoveItem = this._onRemoveItem.bind(this);
  6686.  
  6687.     if (!theme.Helpers.cookiesEnabled()) {
  6688.       this.container.classList.add(classes.cartNoCookies);
  6689.     }
  6690.  
  6691.     this.thumbnails.forEach(function(element) {
  6692.       element.style.cursor = 'pointer';
  6693.     });
  6694.  
  6695.     this.container.addEventListener('click', this._handleThumbnailClick);
  6696.     this.container.addEventListener('change', this._handleInputQty);
  6697.  
  6698.     this.mql = window.matchMedia(mediumUpQuery);
  6699.     this.mql.addListener(this.setQuantityFormControllers);
  6700.  
  6701.     this.setQuantityFormControllers();
  6702.  
  6703.     if (this.ajaxEnabled) {
  6704.       /**
  6705.        * Because the entire cart is recreated when a cart item is updated,
  6706.        * we cannot cache the elements in the cart. Instead, we add the event
  6707.        * listeners on the cart's container to allow us to retain the event
  6708.        * listeners after rebuilding the cart when an item is updated.
  6709.        */
  6710.       this.container.addEventListener('click', this._onRemoveItem);
  6711.       this.container.addEventListener('change', this._onNoteChange);
  6712.  
  6713.       this._setupCartTemplates();
  6714.     }
  6715.   }
  6716.  
  6717.   Cart.prototype = Object.assign({}, Cart.prototype, {
  6718.     _setupCartTemplates: function() {
  6719.       var cartItem = this.container.querySelector(selectors.cartItem);
  6720.       if (!cartItem) return;
  6721.  
  6722.       this.itemTemplate = this.container
  6723.         .querySelector(selectors.cartItem)
  6724.         .cloneNode(true);
  6725.  
  6726.       this.itemDiscountTemplate = this.itemTemplate
  6727.         .querySelector(selectors.cartItemDiscount)
  6728.         .cloneNode(true);
  6729.  
  6730.       this.cartDiscountTemplate = this.container
  6731.         .querySelector(selectors.cartDiscount)
  6732.         .cloneNode(true);
  6733.  
  6734.       this.itemPriceListTemplate = this.itemTemplate
  6735.         .querySelector(selectors.cartItemPriceList)
  6736.         .cloneNode(true);
  6737.  
  6738.       this.itemOptionTemplate = this.itemTemplate
  6739.         .querySelector(selectors.cartItemOption)
  6740.         .cloneNode(true);
  6741.  
  6742.       this.itemPropertyTemplate = this.itemTemplate
  6743.         .querySelector(selectors.cartItemProperty)
  6744.         .cloneNode(true);
  6745.  
  6746.       this.itemSellingPlanNameTemplate = this.itemTemplate
  6747.         .querySelector(selectors.cartItemSellingPlanName)
  6748.         .cloneNode(true);
  6749.     },
  6750.  
  6751.     _handleInputQty: function(evt) {
  6752.       if (!evt.target.hasAttribute('data-quantity-input')) return;
  6753.  
  6754.       var input = evt.target;
  6755.       var itemElement = input.closest(selectors.cartItem);
  6756.  
  6757.       var itemIndex = Number(input.getAttribute('data-quantity-item'));
  6758.  
  6759.       var itemQtyInputs = this.container.querySelectorAll(
  6760.         "[data-quantity-item='" + itemIndex + "']"
  6761.       );
  6762.  
  6763.       var value = parseInt(input.value);
  6764.  
  6765.       var isValidValue = !(value < 0 || isNaN(value));
  6766.  
  6767.      itemQtyInputs.forEach(function(element) {
  6768.        element.value = value;
  6769.      });
  6770.  
  6771.      this._hideCartError();
  6772.      this._hideQuantityErrorMessage();
  6773.  
  6774.      if (!isValidValue) {
  6775.        this._showQuantityErrorMessages(itemElement);
  6776.        return;
  6777.      }
  6778.  
  6779.      if (isValidValue && this.ajaxEnabled) {
  6780.        this._updateItemQuantity(itemIndex, itemElement, itemQtyInputs, value);
  6781.      }
  6782.    },
  6783.  
  6784.    _updateItemQuantity: function(
  6785.      itemIndex,
  6786.      itemElement,
  6787.      itemQtyInputs,
  6788.      value
  6789.    ) {
  6790.      var key = itemElement.getAttribute(attributes.cartItemKey);
  6791.      var index = Number(itemElement.getAttribute(attributes.cartItemIndex));
  6792.  
  6793.      var request = {
  6794.        method: 'POST',
  6795.        headers: {
  6796.          'Content-Type': 'application/json;'
  6797.        },
  6798.        body: JSON.stringify({
  6799.          line: index,
  6800.          quantity: value
  6801.        })
  6802.      };
  6803.  
  6804.      fetch('/cart/change.js', request)
  6805.        .then(function(response) {
  6806.          return response.json();
  6807.        })
  6808.        .then(
  6809.          function(state) {
  6810.            this._setCartCountBubble(state.item_count);
  6811.  
  6812.            if (!state.item_count) {
  6813.              this._emptyCart();
  6814.              return;
  6815.            }
  6816.  
  6817.            this._createCart(state);
  6818.  
  6819.            if (!value) {
  6820.              this._showRemoveMessage(itemElement.cloneNode(true));
  6821.              return;
  6822.            }
  6823.  
  6824.            var lineItem = document.querySelector(
  6825.              "[data-cart-item-key='" + key + "']"
  6826.            );
  6827.  
  6828.            var item = this.getItem(key, state);
  6829.  
  6830.            var inputSelector = this.mql.matches
  6831.              ? selectors.quantityInputDesktop
  6832.              : selectors.quantityInputMobile;
  6833.  
  6834.            this._updateLiveRegion(item);
  6835.  
  6836.            if (!lineItem) return;
  6837.            lineItem.querySelector(inputSelector).focus();
  6838.          }.bind(this)
  6839.        )
  6840.        .catch(
  6841.          function() {
  6842.            this._showCartError(null);
  6843.          }.bind(this)
  6844.        );
  6845.    },
  6846.  
  6847.    getItem: function(key, state) {
  6848.      return state.items.find(function(item) {
  6849.        return item.key === key;
  6850.      });
  6851.    },
  6852.  
  6853.    _liveRegionText: function(item) {
  6854.      // Dummy content for live region
  6855.      var liveRegionText =
  6856.        theme.strings.update +
  6857.        ': [QuantityLabel]: [Quantity], [Regular] [$$] [DiscountedPrice] [$]. [PriceInformation]';
  6858.  
  6859.      // Update Quantity
  6860.      liveRegionText = liveRegionText
  6861.        .replace('[QuantityLabel]', theme.strings.quantity)
  6862.        .replace('[Quantity]', item.quantity);
  6863.  
  6864.      // Update pricing information
  6865.      var regularLabel = '';
  6866.      var regularPrice = theme.Currency.formatMoney(
  6867.        item.original_line_price,
  6868.        theme.moneyFormat
  6869.      );
  6870.      var discountLabel = '';
  6871.      var discountPrice = '';
  6872.      var discountInformation = '';
  6873.  
  6874.      if (item.original_line_price > item.final_line_price) {
  6875.         regularLabel = theme.strings.regularTotal;
  6876.  
  6877.         discountLabel = theme.strings.discountedTotal;
  6878.         discountPrice = theme.Currency.formatMoney(
  6879.           item.final_line_price,
  6880.           theme.moneyFormat
  6881.         );
  6882.  
  6883.         discountInformation = theme.strings.priceColumn;
  6884.       }
  6885.  
  6886.       liveRegionText = liveRegionText
  6887.         .replace('[Regular]', regularLabel)
  6888.         .replace('[$$]', regularPrice)
  6889.         .replace('[DiscountedPrice]', discountLabel)
  6890.         .replace('[$]', discountPrice)
  6891.         .replace('[PriceInformation]', discountInformation)
  6892.         .trim();
  6893.  
  6894.       return liveRegionText;
  6895.     },
  6896.  
  6897.     _updateLiveRegion: function(item) {
  6898.       if (!item) return;
  6899.  
  6900.       var liveRegion = this.container.querySelector(selectors.cartStatus);
  6901.  
  6902.       liveRegion.textContent = this._liveRegionText(item);
  6903.       liveRegion.setAttribute('aria-hidden', false);
  6904.  
  6905.       setTimeout(function() {
  6906.         liveRegion.setAttribute('aria-hidden', true);
  6907.       }, 1000);
  6908.     },
  6909.  
  6910.     _createCart: function(state) {
  6911.       var cartDiscountList = this._createCartDiscountList(state);
  6912.  
  6913.       var cartTable = this.container.querySelector(selectors.cartLineItems);
  6914.       cartTable.innerHTML = '';
  6915.  
  6916.       this._createLineItemList(state).forEach(function(lineItem) {
  6917.         cartTable.appendChild(lineItem);
  6918.       });
  6919.  
  6920.       this.setQuantityFormControllers();
  6921.  
  6922.       this.cartNotes =
  6923.         this.cartNotes || this.container.querySelector(selectors.cartNote);
  6924.  
  6925.       if (this.cartNotes) {
  6926.         this.cartNotes.value = state.note;
  6927.       }
  6928.  
  6929.       var discountWrapper = this.container.querySelector(
  6930.         selectors.cartDiscountWrapper
  6931.       );
  6932.  
  6933.       if (cartDiscountList.length === 0) {
  6934.         discountWrapper.innerHTML = '';
  6935.         discountWrapper.classList.add(classes.hide);
  6936.       } else {
  6937.         discountWrapper.innerHTML = '';
  6938.  
  6939.         cartDiscountList.forEach(function(discountItem) {
  6940.           discountWrapper.appendChild(discountItem);
  6941.         });
  6942.  
  6943.         discountWrapper.classList.remove(classes.hide);
  6944.       }
  6945.  
  6946.       this.container.querySelector(
  6947.         selectors.cartSubtotal
  6948.       ).innerHTML = theme.Currency.formatMoney(
  6949.         state.total_price,
  6950.         theme.moneyFormatWithCurrency
  6951.       );
  6952.     },
  6953.  
  6954.     _createCartDiscountList: function(cart) {
  6955.       return cart.cart_level_discount_applications.map(
  6956.         function(discount) {
  6957.           var discountNode = this.cartDiscountTemplate.cloneNode(true);
  6958.  
  6959.           discountNode.querySelector(selectors.cartDiscountTitle).textContent =
  6960.             discount.title;
  6961.  
  6962.           discountNode.querySelector(
  6963.             selectors.cartDiscountAmount
  6964.           ).innerHTML = theme.Currency.formatMoney(
  6965.             discount.total_allocated_amount,
  6966.             theme.moneyFormat
  6967.           );
  6968.  
  6969.           return discountNode;
  6970.         }.bind(this)
  6971.       );
  6972.     },
  6973.  
  6974.     _createLineItemList: function(state) {
  6975.       return state.items.map(
  6976.         function(item, index) {
  6977.           var itemNode = this.itemTemplate.cloneNode(true);
  6978.  
  6979.           var itemPriceList = this.itemPriceListTemplate.cloneNode(true);
  6980.  
  6981.           this._setLineItemAttributes(itemNode, item, index);
  6982.           this._setLineItemImage(itemNode, item.featured_image);
  6983.  
  6984.           var cartItemTitle = itemNode.querySelector(selectors.cartItemTitle);
  6985.           cartItemTitle.textContent = item.product_title;
  6986.           cartItemTitle.setAttribute('href', item.url);
  6987.  
  6988.           var selling_plan_name = item.selling_plan_allocation
  6989.             ? item.selling_plan_allocation.selling_plan.name
  6990.             : null;
  6991.  
  6992.           var productDetailsList = this._createProductDetailsList(
  6993.             item.product_has_only_default_variant,
  6994.             item.options_with_values,
  6995.             item.properties,
  6996.             selling_plan_name
  6997.           );
  6998.  
  6999.           this._setProductDetailsList(itemNode, productDetailsList);
  7000.  
  7001.           this._setItemRemove(itemNode, item.title);
  7002.  
  7003.           itemPriceList.innerHTML = this._createItemPrice(
  7004.             item.original_price,
  7005.             item.final_price
  7006.           ).outerHTML;
  7007.  
  7008.           if (item.unit_price_measurement) {
  7009.             itemPriceList.appendChild(
  7010.               this._createUnitPrice(
  7011.                 item.unit_price,
  7012.                 item.unit_price_measurement
  7013.               )
  7014.             );
  7015.           }
  7016.  
  7017.           this._setItemPrice(itemNode, itemPriceList);
  7018.  
  7019.           var itemDiscountList = this._createItemDiscountList(item);
  7020.           this._setItemDiscountList(itemNode, itemDiscountList);
  7021.           this._setQuantityInputs(itemNode, item, index);
  7022.  
  7023.           var itemLinePrice = this._createItemPrice(
  7024.             item.original_line_price,
  7025.             item.final_line_price
  7026.           );
  7027.  
  7028.           this._setItemLinePrice(itemNode, itemLinePrice);
  7029.  
  7030.           return itemNode;
  7031.         }.bind(this)
  7032.       );
  7033.     },
  7034.  
  7035.     _setLineItemAttributes: function(itemNode, item, index) {
  7036.       itemNode.setAttribute(attributes.cartItemKey, item.key);
  7037.       itemNode.setAttribute(attributes.cartItemUrl, item.url);
  7038.       itemNode.setAttribute(attributes.cartItemTitle, item.title);
  7039.       itemNode.setAttribute(attributes.cartItemIndex, index + 1);
  7040.       itemNode.setAttribute(attributes.cartItemQuantity, item.quantity);
  7041.     },
  7042.  
  7043.     _setLineItemImage: function(itemNode, featuredImage) {
  7044.       var image = itemNode.querySelector(selectors.cartItemImage);
  7045.  
  7046.       var sizedImageUrl =
  7047.         featuredImage.url !== null
  7048.           ? theme.Images.getSizedImageUrl(featuredImage.url, 'x190')
  7049.           : null;
  7050.  
  7051.       if (sizedImageUrl) {
  7052.         image.setAttribute('alt', featuredImage.alt);
  7053.         image.setAttribute('src', sizedImageUrl);
  7054.         image.classList.remove(classes.hide);
  7055.       } else {
  7056.         image.parentNode.removeChild(image);
  7057.       }
  7058.     },
  7059.  
  7060.     _setProductDetailsList: function(item, productDetailsList) {
  7061.       var itemDetails = item.querySelector(selectors.cartItemDetails);
  7062.  
  7063.       if (productDetailsList.length) {
  7064.         itemDetails.classList.remove(classes.hide);
  7065.         itemDetails.innerHTML = productDetailsList.reduce(function(
  7066.           result,
  7067.           element
  7068.         ) {
  7069.           return result + element.outerHTML;
  7070.         },
  7071.         '');
  7072.  
  7073.         return;
  7074.       }
  7075.  
  7076.       itemDetails.classList.add(classes.hide);
  7077.       itemDetails.textContent = '';
  7078.     },
  7079.  
  7080.     _setItemPrice: function(item, price) {
  7081.       item.querySelector(selectors.cartItemPrice).innerHTML = price.outerHTML;
  7082.     },
  7083.  
  7084.     _setItemDiscountList: function(item, discountList) {
  7085.       var itemDiscountList = item.querySelector(selectors.cartItemDiscountList);
  7086.  
  7087.       if (discountList.length === 0) {
  7088.         itemDiscountList.innerHTML = '';
  7089.         itemDiscountList.classList.add(classes.hide);
  7090.       } else {
  7091.         itemDiscountList.innerHTML = discountList.reduce(function(
  7092.           result,
  7093.           element
  7094.         ) {
  7095.           return result + element.outerHTML;
  7096.         },
  7097.         '');
  7098.  
  7099.         itemDiscountList.classList.remove(classes.hide);
  7100.       }
  7101.     },
  7102.  
  7103.     _setItemRemove: function(item, title) {
  7104.       item
  7105.         .querySelector(selectors.cartRemove)
  7106.         .setAttribute(
  7107.           'aria-label',
  7108.           theme.strings.removeLabel.replace('[product]', title)
  7109.         );
  7110.     },
  7111.  
  7112.     _setQuantityInputs: function(itemNode, item, index) {
  7113.       var mobileInput = itemNode.querySelector(selectors.quantityInputMobile);
  7114.       var desktopInput = itemNode.querySelector(selectors.quantityInputDesktop);
  7115.  
  7116.       mobileInput.setAttribute('id', 'updates_' + item.key);
  7117.       desktopInput.setAttribute('id', 'updates_large_' + item.key);
  7118.  
  7119.       [mobileInput, desktopInput].forEach(function(element) {
  7120.         element.setAttribute(attributes.quantityItem, index + 1);
  7121.         element.value = item.quantity;
  7122.       });
  7123.  
  7124.       itemNode
  7125.         .querySelector(selectors.quantityLabelMobile)
  7126.         .setAttribute('for', 'updates_' + item.key);
  7127.  
  7128.       itemNode
  7129.         .querySelector(selectors.quantityLabelDesktop)
  7130.         .setAttribute('for', 'updates_large_' + item.key);
  7131.     },
  7132.  
  7133.     setQuantityFormControllers: function() {
  7134.       var desktopQuantityInputs = document.querySelectorAll(
  7135.         selectors.quantityInputDesktop
  7136.       );
  7137.  
  7138.       var mobileQuantityInputs = document.querySelectorAll(
  7139.         selectors.quantityInputMobile
  7140.       );
  7141.  
  7142.       if (this.mql.matches) {
  7143.         addNameAttribute(desktopQuantityInputs);
  7144.         removeNameAttribute(mobileQuantityInputs);
  7145.       } else {
  7146.         addNameAttribute(mobileQuantityInputs);
  7147.         removeNameAttribute(desktopQuantityInputs);
  7148.       }
  7149.  
  7150.       function addNameAttribute(inputs) {
  7151.         inputs.forEach(function(element) {
  7152.           element.setAttribute('name', 'updates[]');
  7153.         });
  7154.       }
  7155.  
  7156.       function removeNameAttribute(inputs) {
  7157.         inputs.forEach(function(element) {
  7158.           element.removeAttribute('name');
  7159.         });
  7160.       }
  7161.     },
  7162.  
  7163.     _setItemLinePrice: function(item, price) {
  7164.       item.querySelector(selectors.cartItemLinePrice).innerHTML =
  7165.         price.outerHTML;
  7166.     },
  7167.  
  7168.     _createProductDetailsList: function(
  7169.       product_has_only_default_variant,
  7170.       options,
  7171.       properties,
  7172.       selling_plan_name
  7173.     ) {
  7174.       var optionsPropertiesHTML = [];
  7175.  
  7176.       if (!product_has_only_default_variant) {
  7177.         optionsPropertiesHTML = optionsPropertiesHTML.concat(
  7178.           this._getOptionList(options)
  7179.         );
  7180.       }
  7181.  
  7182.       if (selling_plan_name) {
  7183.         optionsPropertiesHTML = optionsPropertiesHTML.concat(
  7184.           this._getSellingPlanName(selling_plan_name)
  7185.         );
  7186.       }
  7187.  
  7188.       if (properties !== null && Object.keys(properties).length !== 0) {
  7189.        optionsPropertiesHTML = optionsPropertiesHTML.concat(
  7190.          this._getPropertyList(properties)
  7191.        );
  7192.       }
  7193.  
  7194.       return optionsPropertiesHTML;
  7195.     },
  7196.  
  7197.     _getOptionList: function(options) {
  7198.       return options.map(
  7199.         function(option) {
  7200.           var optionElement = this.itemOptionTemplate.cloneNode(true);
  7201.  
  7202.           optionElement.textContent = option.name + ': ' + option.value;
  7203.           optionElement.classList.remove(classes.hide);
  7204.  
  7205.           return optionElement;
  7206.         }.bind(this)
  7207.       );
  7208.     },
  7209.  
  7210.     _getPropertyList: function(properties) {
  7211.       var propertiesArray =
  7212.         properties !== null ? Object.entries(properties) : [];
  7213.  
  7214.       var filteredPropertiesArray = propertiesArray.filter(function(property) {
  7215.         // Line item properties prefixed with an underscore are not to be displayed
  7216.         // if the property value has a length of 0 (empty), don't display it
  7217.         if (property[0].charAt(0) === '_' || property[1].length === 0) {
  7218.           return false;
  7219.         } else {
  7220.           return true;
  7221.         }
  7222.       });
  7223.  
  7224.       return filteredPropertiesArray.map(
  7225.         function(property) {
  7226.           var propertyElement = this.itemPropertyTemplate.cloneNode(true);
  7227.  
  7228.           propertyElement.querySelector(
  7229.             selectors.cartItemPropertyName
  7230.           ).textContent = property[0] + ': ';
  7231.  
  7232.           if (property[0].indexOf('/uploads/') === -1) {
  7233.             propertyElement.querySelector(
  7234.               selectors.cartItemPropertyValue
  7235.             ).textContent = property[1];
  7236.           } else {
  7237.             propertyElement.querySelector(
  7238.               selectors.cartItemPropertyValue
  7239.             ).innerHTML =
  7240.               '<a href="' +
  7241.              property[1] +
  7242.              '"> ' +
  7243.               property[1].split('/').pop() +
  7244.               '</a>';
  7245.           }
  7246.  
  7247.           propertyElement.classList.remove(classes.hide);
  7248.  
  7249.           return propertyElement;
  7250.         }.bind(this)
  7251.       );
  7252.     },
  7253.  
  7254.     _getSellingPlanName: function(selling_plan_name) {
  7255.       var sellingPlanNameElement = this.itemSellingPlanNameTemplate.cloneNode(
  7256.         true
  7257.       );
  7258.  
  7259.       sellingPlanNameElement.textContent = selling_plan_name;
  7260.       sellingPlanNameElement.classList.remove(classes.hide);
  7261.  
  7262.       return sellingPlanNameElement;
  7263.     },
  7264.  
  7265.     _createItemPrice: function(original_price, final_price) {
  7266.       var originalPriceHTML = theme.Currency.formatMoney(
  7267.         original_price,
  7268.         theme.moneyFormat
  7269.       );
  7270.  
  7271.       var resultHTML;
  7272.  
  7273.       if (original_price !== final_price) {
  7274.         resultHTML = this.itemPriceListTemplate
  7275.           .querySelector(selectors.cartItemDiscountedPriceGroup)
  7276.           .cloneNode(true);
  7277.  
  7278.         resultHTML.querySelector(
  7279.           selectors.cartItemOriginalPrice
  7280.         ).innerHTML = originalPriceHTML;
  7281.  
  7282.         resultHTML.querySelector(
  7283.           selectors.cartItemFinalPrice
  7284.         ).innerHTML = theme.Currency.formatMoney(
  7285.           final_price,
  7286.           theme.moneyFormat
  7287.         );
  7288.       } else {
  7289.         resultHTML = this.itemPriceListTemplate
  7290.           .querySelector(selectors.cartItemRegularPriceGroup)
  7291.           .cloneNode(true);
  7292.  
  7293.         resultHTML.querySelector(
  7294.           selectors.cartItemRegularPrice
  7295.         ).innerHTML = originalPriceHTML;
  7296.       }
  7297.  
  7298.       resultHTML.classList.remove(classes.hide);
  7299.       return resultHTML;
  7300.     },
  7301.  
  7302.     _createUnitPrice: function(unitPrice, unitPriceMeasurement) {
  7303.       var unitPriceGroup = this.itemPriceListTemplate
  7304.         .querySelector(selectors.unitPriceGroup)
  7305.         .cloneNode(true);
  7306.  
  7307.       var unitPriceBaseUnit =
  7308.         (unitPriceMeasurement.reference_value !== 1
  7309.           ? unitPriceMeasurement.reference_value
  7310.           : '') + unitPriceMeasurement.reference_unit;
  7311.  
  7312.       unitPriceGroup.querySelector(
  7313.         selectors.unitPriceBaseUnit
  7314.       ).textContent = unitPriceBaseUnit;
  7315.  
  7316.       unitPriceGroup.querySelector(
  7317.         selectors.unitPrice
  7318.       ).innerHTML = theme.Currency.formatMoney(unitPrice, theme.moneyFormat);
  7319.  
  7320.       unitPriceGroup.classList.remove(classes.hide);
  7321.  
  7322.       return unitPriceGroup;
  7323.     },
  7324.  
  7325.     _createItemDiscountList: function(item) {
  7326.       return item.line_level_discount_allocations.map(
  7327.         function(discount) {
  7328.           var discountNode = this.itemDiscountTemplate.cloneNode(true);
  7329.  
  7330.           discountNode.querySelector(
  7331.             selectors.cartItemDiscountTitle
  7332.           ).textContent = discount.discount_application.title;
  7333.  
  7334.           discountNode.querySelector(
  7335.             selectors.cartItemDiscountAmount
  7336.           ).innerHTML = theme.Currency.formatMoney(
  7337.             discount.amount,
  7338.             theme.moneyFormat
  7339.           );
  7340.  
  7341.           return discountNode;
  7342.         }.bind(this)
  7343.       );
  7344.     },
  7345.  
  7346.     _showQuantityErrorMessages: function(itemElement) {
  7347.       itemElement
  7348.         .querySelectorAll(selectors.cartQuantityErrorMessage)
  7349.         .forEach(function(element) {
  7350.           element.textContent = theme.strings.quantityMinimumMessage;
  7351.         });
  7352.  
  7353.       itemElement
  7354.         .querySelectorAll(selectors.cartQuantityErrorMessageWrapper)
  7355.         .forEach(function(element) {
  7356.           element.classList.remove(classes.hide);
  7357.         });
  7358.  
  7359.       itemElement
  7360.         .querySelectorAll(selectors.inputQty)
  7361.         .forEach(function(element) {
  7362.           element.classList.add(classes.inputError);
  7363.           element.focus();
  7364.         });
  7365.     },
  7366.  
  7367.     _hideQuantityErrorMessage: function() {
  7368.       var errorMessages = document.querySelectorAll(
  7369.         selectors.cartQuantityErrorMessageWrapper
  7370.       );
  7371.  
  7372.       errorMessages.forEach(function(element) {
  7373.         element.classList.add(classes.hide);
  7374.  
  7375.         element.querySelector(selectors.cartQuantityErrorMessage).textContent =
  7376.           '';
  7377.       });
  7378.  
  7379.       this.container
  7380.         .querySelectorAll(selectors.inputQty)
  7381.         .forEach(function(element) {
  7382.           element.classList.remove(classes.inputError);
  7383.         });
  7384.     },
  7385.  
  7386.     _handleThumbnailClick: function(evt) {
  7387.       if (!evt.target.classList.contains(classes.thumbnails)) return;
  7388.  
  7389.       window.location.href = evt.target
  7390.         .closest(selectors.cartItem)
  7391.         .getAttribute('data-cart-item-url');
  7392.     },
  7393.  
  7394.     _onNoteChange: function(evt) {
  7395.       if (!evt.target.hasAttribute('data-cart-notes')) return;
  7396.  
  7397.       var note = evt.target.value;
  7398.       this._hideCartError();
  7399.       this._hideQuantityErrorMessage();
  7400.  
  7401.       var headers = new Headers({ 'Content-Type': 'application/json' });
  7402.  
  7403.       var request = {
  7404.         method: 'POST',
  7405.         headers: headers,
  7406.         body: JSON.stringify({ note: note })
  7407.       };
  7408.  
  7409.       fetch('/cart/update.js', request).catch(
  7410.         function() {
  7411.           this._showCartError(evt.target);
  7412.         }.bind(this)
  7413.       );
  7414.     },
  7415.  
  7416.     _showCartError: function(elementToFocus) {
  7417.       document.querySelector(selectors.cartErrorMessage).textContent =
  7418.         theme.strings.cartError;
  7419.  
  7420.       document
  7421.         .querySelector(selectors.cartErrorMessageWrapper)
  7422.         .classList.remove(classes.hide);
  7423.  
  7424.       if (!elementToFocus) return;
  7425.       elementToFocus.focus();
  7426.     },
  7427.  
  7428.     _hideCartError: function() {
  7429.       document
  7430.         .querySelector(selectors.cartErrorMessageWrapper)
  7431.         .classList.add(classes.hide);
  7432.       document.querySelector(selectors.cartErrorMessage).textContent = '';
  7433.     },
  7434.  
  7435.     _onRemoveItem: function(evt) {
  7436.       if (!evt.target.hasAttribute('data-cart-remove')) return;
  7437.  
  7438.       evt.preventDefault();
  7439.       var lineItem = evt.target.closest(selectors.cartItem);
  7440.       var index = Number(lineItem.getAttribute(attributes.cartItemIndex));
  7441.  
  7442.       this._hideCartError();
  7443.  
  7444.       var request = {
  7445.         method: 'POST',
  7446.         headers: {
  7447.           'Content-Type': 'application/json;'
  7448.         },
  7449.         body: JSON.stringify({
  7450.           line: index,
  7451.           quantity: 0
  7452.         })
  7453.       };
  7454.  
  7455.       fetch('/cart/change.js', request)
  7456.         .then(function(response) {
  7457.           return response.json();
  7458.         })
  7459.         .then(
  7460.           function(state) {
  7461.             if (state.item_count === 0) {
  7462.               this._emptyCart();
  7463.             } else {
  7464.               this._createCart(state);
  7465.               this._showRemoveMessage(lineItem.cloneNode(true));
  7466.             }
  7467.  
  7468.             this._setCartCountBubble(state.item_count);
  7469.           }.bind(this)
  7470.         )
  7471.         .catch(
  7472.           function() {
  7473.             this._showCartError(null);
  7474.           }.bind(this)
  7475.         );
  7476.     },
  7477.  
  7478.     _showRemoveMessage: function(lineItem) {
  7479.       var index = lineItem.getAttribute('data-cart-item-index');
  7480.       var removeMessage = this._getRemoveMessage(lineItem);
  7481.  
  7482.       if (index - 1 === 0) {
  7483.         this.container
  7484.           .querySelector('[data-cart-item-index="1"]')
  7485.           .insertAdjacentHTML('beforebegin', removeMessage.outerHTML);
  7486.       } else {
  7487.         this.container
  7488.           .querySelector("[data-cart-item-index='" + (index - 1) + "']")
  7489.           .insertAdjacentHTML('afterend', removeMessage.outerHTML);
  7490.       }
  7491.  
  7492.       this.container.querySelector('[data-removed-item-row]').focus();
  7493.     },
  7494.  
  7495.     _getRemoveMessage: function(lineItem) {
  7496.       var formattedMessage = this._formatRemoveMessage(lineItem);
  7497.  
  7498.       var tableCell = lineItem
  7499.         .querySelector(selectors.cartTableCell)
  7500.         .cloneNode(true);
  7501.  
  7502.       tableCell.removeAttribute('class');
  7503.       tableCell.classList.add(classes.cartRemovedProduct);
  7504.       tableCell.setAttribute('colspan', '4');
  7505.       tableCell.innerHTML = formattedMessage;
  7506.  
  7507.       lineItem.setAttribute('role', 'alert');
  7508.       lineItem.setAttribute('tabindex', '-1');
  7509.       lineItem.setAttribute('data-removed-item-row', true);
  7510.       lineItem.innerHTML = tableCell.outerHTML;
  7511.  
  7512.       return lineItem;
  7513.     },
  7514.  
  7515.     _formatRemoveMessage: function(lineItem) {
  7516.       var quantity = lineItem.getAttribute('data-cart-item-quantity');
  7517.       var url = lineItem.getAttribute(attributes.cartItemUrl);
  7518.       var title = lineItem.getAttribute(attributes.cartItemTitle);
  7519.  
  7520.       return theme.strings.removedItemMessage
  7521.         .replace('[quantity]', quantity)
  7522.         .replace(
  7523.           '[link]',
  7524.           '<a ' +
  7525.            'href="' +
  7526.            url +
  7527.            '" class="text-link text-link--accent">' +
  7528.             title +
  7529.             '</a>'
  7530.         );
  7531.     },
  7532.  
  7533.     _setCartCountBubble: function(quantity) {
  7534.       this.cartCountBubble =
  7535.         this.cartCountBubble ||
  7536.         document.querySelector(selectors.cartCountBubble);
  7537.  
  7538.       this.cartCount =
  7539.         this.cartCount || document.querySelector(selectors.cartCount);
  7540.  
  7541.       if (quantity > 0) {
  7542.         this.cartCountBubble.classList.remove(classes.hide);
  7543.         this.cartCount.textContent = quantity;
  7544.       } else {
  7545.         this.cartCountBubble.classList.add(classes.hide);
  7546.         this.cartCount.textContent = '';
  7547.       }
  7548.     },
  7549.  
  7550.     _emptyCart: function() {
  7551.       this.emptyPageContent =
  7552.         this.emptyPageContent ||
  7553.         this.container.querySelector(selectors.emptyPageContent);
  7554.  
  7555.       this.cartWrapper =
  7556.         this.cartWrapper || this.container.querySelector(selectors.cartWrapper);
  7557.  
  7558.       this.emptyPageContent.classList.remove(classes.hide);
  7559.       this.cartWrapper.classList.add(classes.hide);
  7560.     }
  7561.   });
  7562.  
  7563.   return Cart;
  7564. })();
  7565.  
  7566. window.theme = window.theme || {};
  7567.  
  7568. theme.Filters = (function() {
  7569.   var settings = {
  7570.     mediaQueryMediumUp: '(min-width: ' + theme.breakpoints.medium + 'px)'
  7571.   };
  7572.  
  7573.   var selectors = {
  7574.     filterSelection: '#FilterTags',
  7575.     sortSelection: '#SortBy',
  7576.     selectInput: '[data-select-input]'
  7577.   };
  7578.  
  7579.   function Filters(container) {
  7580.     this.filterSelect = container.querySelector(selectors.filterSelection);
  7581.     this.sortSelect = container.querySelector(selectors.sortSelection);
  7582.  
  7583.     this.selects = document.querySelectorAll(selectors.selectInput);
  7584.  
  7585.     if (this.sortSelect) {
  7586.       this.defaultSort = this._getDefaultSortValue();
  7587.     }
  7588.  
  7589.     if (this.selects.length) {
  7590.       this.selects.forEach(function(select) {
  7591.         select.classList.remove('hidden');
  7592.       });
  7593.     }
  7594.  
  7595.     this.initBreakpoints = this._initBreakpoints.bind(this);
  7596.  
  7597.     this.mql = window.matchMedia(settings.mediaQueryMediumUp);
  7598.     this.mql.addListener(this.initBreakpoints);
  7599.  
  7600.     if (this.filterSelect) {
  7601.       this.filterSelect.addEventListener(
  7602.         'change',
  7603.         this._onFilterChange.bind(this)
  7604.       );
  7605.     }
  7606.  
  7607.     if (this.sortSelect) {
  7608.       this.sortSelect.addEventListener('change', this._onSortChange.bind(this));
  7609.     }
  7610.  
  7611.     theme.Helpers.promiseStylesheet().then(
  7612.       function() {
  7613.         this._initBreakpoints();
  7614.       }.bind(this)
  7615.     );
  7616.     this._initParams();
  7617.   }
  7618.  
  7619.   Filters.prototype = Object.assign({}, Filters.prototype, {
  7620.     _initBreakpoints: function() {
  7621.       if (this.mql.matches) {
  7622.         slate.utils.resizeSelects(this.selects);
  7623.       }
  7624.     },
  7625.  
  7626.     _initParams: function() {
  7627.       this.queryParams = {};
  7628.       if (location.search.length) {
  7629.         var aKeyValue;
  7630.         var aCouples = location.search.substr(1).split('&');
  7631.         for (var i = 0; i < aCouples.length; i++) {
  7632.          aKeyValue = aCouples[i].split('=');
  7633.          if (aKeyValue.length > 1) {
  7634.             this.queryParams[
  7635.               decodeURIComponent(aKeyValue[0])
  7636.             ] = decodeURIComponent(aKeyValue[1]);
  7637.           }
  7638.         }
  7639.       }
  7640.     },
  7641.  
  7642.     _onSortChange: function() {
  7643.       this.queryParams.sort_by = this._getSortValue();
  7644.  
  7645.       if (this.queryParams.page) {
  7646.         delete this.queryParams.page;
  7647.       }
  7648.  
  7649.       window.location.search = decodeURIComponent(
  7650.         new URLSearchParams(Object.entries(this.queryParams)).toString()
  7651.       );
  7652.     },
  7653.  
  7654.     _onFilterChange: function() {
  7655.       document.location.href = this._getFilterValue();
  7656.     },
  7657.  
  7658.     _getFilterValue: function() {
  7659.       return this.filterSelect.value;
  7660.     },
  7661.  
  7662.     _getSortValue: function() {
  7663.       return this.sortSelect.value || this.defaultSort;
  7664.     },
  7665.  
  7666.     _getDefaultSortValue: function() {
  7667.       return this.sortSelect.dataset.defaultSortby;
  7668.     },
  7669.  
  7670.     onUnload: function() {
  7671.       if (this.filterSelect) {
  7672.         this.filterSelect.removeEventListener('change', this._onFilterChange);
  7673.       }
  7674.  
  7675.       if (this.sortSelect) {
  7676.         this.sortSelect.removeEventListener('change', this._onSortChange);
  7677.       }
  7678.  
  7679.       this.mql.removeListener(this.initBreakpoints);
  7680.     }
  7681.   });
  7682.  
  7683.   return Filters;
  7684. })();
  7685.  
  7686. window.theme = window.theme || {};
  7687.  
  7688. theme.HeaderSection = (function() {
  7689.   function Header() {
  7690.     theme.Header.init();
  7691.     theme.MobileNav.init();
  7692.     theme.SearchDrawer.init();
  7693.     theme.Search.init();
  7694.   }
  7695.  
  7696.   Header.prototype = Object.assign({}, Header.prototype, {
  7697.     onUnload: function() {
  7698.       theme.Header.unload();
  7699.       theme.Search.unload();
  7700.       theme.MobileNav.unload();
  7701.     }
  7702.   });
  7703.  
  7704.   return Header;
  7705. })();
  7706.  
  7707. theme.Maps = (function() {
  7708.   var config = {
  7709.     zoom: 14
  7710.   };
  7711.   var apiStatus = null;
  7712.   var mapsToLoad = [];
  7713.  
  7714.   var errors = {
  7715.     addressNoResults: theme.strings.addressNoResults,
  7716.     addressQueryLimit: theme.strings.addressQueryLimit,
  7717.     addressError: theme.strings.addressError,
  7718.     authError: theme.strings.authError
  7719.   };
  7720.  
  7721.   var selectors = {
  7722.     section: '[data-section-type="map"]',
  7723.     map: '[data-map]',
  7724.     mapOverlay: '[data-map-overlay]'
  7725.   };
  7726.  
  7727.   var classes = {
  7728.     mapError: 'map-section--load-error',
  7729.     errorMsg: 'map-section__error errors text-center'
  7730.   };
  7731.  
  7732.   // Global function called by Google on auth errors.
  7733.   // Show an auto error message on all map instances.
  7734.   // eslint-disable-next-line camelcase, no-unused-vars
  7735.   window.gm_authFailure = function() {
  7736.     if (!Shopify.designMode) {
  7737.       return;
  7738.     }
  7739.  
  7740.     document.querySelector(selectors.section).classList.add(classes.mapError);
  7741.     document.querySelector(selectors.map).remove();
  7742.     document
  7743.       .querySelector(selectors.mapOverlay)
  7744.       .insertAdjacentHTML(
  7745.         'afterend',
  7746.         '<div class="' +
  7747.          classes.errorMsg +
  7748.          '">' +
  7749.           theme.strings.authError +
  7750.           '</div>'
  7751.       );
  7752.   };
  7753.  
  7754.   function Map(container) {
  7755.     this.map = container.querySelector(selectors.map);
  7756.     if (!this.map) return;
  7757.     this.key = this.map.dataset.apiKey;
  7758.  
  7759.     if (typeof this.key === 'undefined') {
  7760.       return;
  7761.     }
  7762.  
  7763.     if (apiStatus === 'loaded') {
  7764.       this.createMap();
  7765.     } else {
  7766.       mapsToLoad.push(this);
  7767.  
  7768.       if (apiStatus !== 'loading') {
  7769.         apiStatus = 'loading';
  7770.         if (typeof window.google === 'undefined') {
  7771.           theme.Helpers.getScript(
  7772.             'https://maps.googleapis.com/maps/api/js?key=' + this.key
  7773.           ).then(function() {
  7774.             apiStatus = 'loaded';
  7775.             initAllMaps();
  7776.           });
  7777.         }
  7778.       }
  7779.     }
  7780.   }
  7781.  
  7782.   function initAllMaps() {
  7783.     // API has loaded, load all Map instances in queue
  7784.     mapsToLoad.forEach(function(map) {
  7785.       map.createMap();
  7786.     });
  7787.   }
  7788.  
  7789.   function geolocate(map) {
  7790.     return new Promise(function(resolve, reject) {
  7791.       var geocoder = new google.maps.Geocoder();
  7792.       var address = map.dataset.addressSetting;
  7793.  
  7794.       geocoder.geocode({ address: address }, function(results, status) {
  7795.         if (status !== google.maps.GeocoderStatus.OK) {
  7796.           reject(status);
  7797.         }
  7798.  
  7799.         resolve(results);
  7800.       });
  7801.     });
  7802.   }
  7803.  
  7804.   Map.prototype = Object.assign({}, Map.prototype, {
  7805.     createMap: function() {
  7806.       return geolocate(this.map)
  7807.         .then(
  7808.           function(results) {
  7809.             var mapOptions = {
  7810.               zoom: config.zoom,
  7811.               center: results[0].geometry.location,
  7812.               draggable: false,
  7813.               clickableIcons: false,
  7814.               scrollwheel: false,
  7815.               disableDoubleClickZoom: true,
  7816.               disableDefaultUI: true
  7817.             };
  7818.  
  7819.             var map = (this.map = new google.maps.Map(this.map, mapOptions));
  7820.             var center = (this.center = map.getCenter());
  7821.  
  7822.             //eslint-disable-next-line no-unused-vars
  7823.             var marker = new google.maps.Marker({
  7824.               map: map,
  7825.               position: map.getCenter()
  7826.             });
  7827.  
  7828.             google.maps.event.addDomListener(
  7829.               window,
  7830.               'resize',
  7831.               theme.Helpers.debounce(
  7832.                 function() {
  7833.                   google.maps.event.trigger(map, 'resize');
  7834.                   map.setCenter(center);
  7835.                   this.map.removeAttribute('style');
  7836.                 }.bind(this),
  7837.                 250
  7838.               )
  7839.             );
  7840.           }.bind(this)
  7841.         )
  7842.         .catch(
  7843.           function() {
  7844.             var errorMessage;
  7845.  
  7846.             switch (status) {
  7847.               case 'ZERO_RESULTS':
  7848.                 errorMessage = errors.addressNoResults;
  7849.                 break;
  7850.               case 'OVER_QUERY_LIMIT':
  7851.                 errorMessage = errors.addressQueryLimit;
  7852.                 break;
  7853.               case 'REQUEST_DENIED':
  7854.                 errorMessage = errors.authError;
  7855.                 break;
  7856.               default:
  7857.                 errorMessage = errors.addressError;
  7858.                 break;
  7859.             }
  7860.  
  7861.             // Show errors only to merchant in the editor.
  7862.             if (Shopify.designMode) {
  7863.               this.map.parentNode.classList.add(classes.mapError);
  7864.               this.map.parentNode.innerHTML =
  7865.                 '<div class="' +
  7866.                classes.errorMsg +
  7867.                '">' +
  7868.                 errorMessage +
  7869.                 '</div>';
  7870.             }
  7871.           }.bind(this)
  7872.         );
  7873.     },
  7874.  
  7875.     onUnload: function() {
  7876.       if (this.map) {
  7877.         google.maps.event.clearListeners(this.map, 'resize');
  7878.       }
  7879.     }
  7880.   });
  7881.  
  7882.   return Map;
  7883. })();
  7884.  
  7885. /* eslint-disable no-new */
  7886. theme.Product = (function() {
  7887.   function Product(container) {
  7888.     this.container = container;
  7889.     var sectionId = container.getAttribute('data-section-id');
  7890.     this.zoomPictures = [];
  7891.     this.ajaxEnabled = container.getAttribute('data-ajax-enabled') === 'true';
  7892.  
  7893.     this.settings = {
  7894.       // Breakpoints from src/stylesheets/global/variables.scss.liquid
  7895.       mediaQueryMediumUp: 'screen and (min-width: 750px)',
  7896.       mediaQuerySmall: 'screen and (max-width: 749px)',
  7897.       bpSmall: false,
  7898.       enableHistoryState:
  7899.         container.getAttribute('data-enable-history-state') || false,
  7900.       namespace: '.slideshow-' + sectionId,
  7901.       sectionId: sectionId,
  7902.       sliderActive: false,
  7903.       zoomEnabled: false
  7904.     };
  7905.  
  7906.     this.selectors = {
  7907.       addToCart: '[data-add-to-cart]',
  7908.       addToCartText: '[data-add-to-cart-text]',
  7909.       cartCount: '[data-cart-count]',
  7910.       cartCountBubble: '[data-cart-count-bubble]',
  7911.       cartPopup: '[data-cart-popup]',
  7912.       cartPopupCartQuantity: '[data-cart-popup-cart-quantity]',
  7913.       cartPopupClose: '[data-cart-popup-close]',
  7914.       cartPopupDismiss: '[data-cart-popup-dismiss]',
  7915.       cartPopupImage: '[data-cart-popup-image]',
  7916.       cartPopupImageWrapper: '[data-cart-popup-image-wrapper]',
  7917.       cartPopupImagePlaceholder: '[data-image-loading-animation]',
  7918.       cartPopupProductDetails: '[data-cart-popup-product-details]',
  7919.       cartPopupQuantity: '[data-cart-popup-quantity]',
  7920.       cartPopupQuantityLabel: '[data-cart-popup-quantity-label]',
  7921.       cartPopupTitle: '[data-cart-popup-title]',
  7922.       cartPopupWrapper: '[data-cart-popup-wrapper]',
  7923.       loader: '[data-loader]',
  7924.       loaderStatus: '[data-loader-status]',
  7925.       quantity: '[data-quantity-input]',
  7926.       SKU: '.variant-sku',
  7927.       productStatus: '[data-product-status]',
  7928.       originalSelectorId: '#ProductSelect-' + sectionId,
  7929.       productForm: '[data-product-form]',
  7930.       errorMessage: '[data-error-message]',
  7931.       errorMessageWrapper: '[data-error-message-wrapper]',
  7932.       imageZoomWrapper: '[data-image-zoom-wrapper]',
  7933.       productMediaWrapper: '[data-product-single-media-wrapper]',
  7934.       productThumbImages: '.product-single__thumbnail--' + sectionId,
  7935.       productThumbs: '.product-single__thumbnails-' + sectionId,
  7936.       productThumbListItem: '.product-single__thumbnails-item',
  7937.       productThumbsWrapper: '.thumbnails-wrapper',
  7938.       saleLabel: '.product-price__sale-label-' + sectionId,
  7939.       singleOptionSelector: '.single-option-selector-' + sectionId,
  7940.       shopifyPaymentButton: '.shopify-payment-button',
  7941.       productMediaTypeVideo: '[data-product-media-type-video]',
  7942.       productMediaTypeModel: '[data-product-media-type-model]',
  7943.       priceContainer: '[data-price]',
  7944.       regularPrice: '[data-regular-price]',
  7945.       salePrice: '[data-sale-price]',
  7946.       unitPrice: '[data-unit-price]',
  7947.       unitPriceBaseUnit: '[data-unit-price-base-unit]',
  7948.       productPolicies: '[data-product-policies]',
  7949.       storeAvailabilityContainer: '[data-store-availability-container]'
  7950.     };
  7951.  
  7952.     this.classes = {
  7953.       cartPopupWrapperHidden: 'cart-popup-wrapper--hidden',
  7954.       hidden: 'hide',
  7955.       visibilityHidden: 'visibility-hidden',
  7956.       inputError: 'input--error',
  7957.       jsZoomEnabled: 'js-zoom-enabled',
  7958.       productOnSale: 'price--on-sale',
  7959.       productUnitAvailable: 'price--unit-available',
  7960.       productUnavailable: 'price--unavailable',
  7961.       productSoldOut: 'price--sold-out',
  7962.       cartImage: 'cart-popup-item__image',
  7963.       productFormErrorMessageWrapperHidden:
  7964.         'product-form__error-message-wrapper--hidden',
  7965.       activeClass: 'active-thumb',
  7966.       variantSoldOut: 'product-form--variant-sold-out'
  7967.     };
  7968.  
  7969.     this.eventHandlers = {};
  7970.  
  7971.     this.quantityInput = container.querySelector(this.selectors.quantity);
  7972.     this.errorMessageWrapper = container.querySelector(
  7973.       this.selectors.errorMessageWrapper
  7974.     );
  7975.     this.productForm = container.querySelector(this.selectors.productForm);
  7976.     this.addToCart = container.querySelector(this.selectors.addToCart);
  7977.     this.addToCartText = this.addToCart.querySelector(
  7978.       this.selectors.addToCartText
  7979.     );
  7980.     this.shopifyPaymentButton = container.querySelector(
  7981.       this.selectors.shopifyPaymentButton
  7982.     );
  7983.     this.productPolicies = container.querySelector(
  7984.       this.selectors.productPolicies
  7985.     );
  7986.     this.storeAvailabilityContainer = container.querySelector(
  7987.       this.selectors.storeAvailabilityContainer
  7988.     );
  7989.     if (this.storeAvailabilityContainer) {
  7990.       this._initStoreAvailability();
  7991.     }
  7992.  
  7993.     this.loader = this.addToCart.querySelector(this.selectors.loader);
  7994.     this.loaderStatus = container.querySelector(this.selectors.loaderStatus);
  7995.  
  7996.     this.imageZoomWrapper = container.querySelectorAll(
  7997.       this.selectors.imageZoomWrapper
  7998.     );
  7999.  
  8000.     // Stop parsing if we don't have the product json script tag when loading
  8001.     // section in the Theme Editor
  8002.     var productJson = document.getElementById('ProductJson-' + sectionId);
  8003.     if (!productJson || !productJson.innerHTML.length) {
  8004.       return;
  8005.     }
  8006.  
  8007.     this.productSingleObject = JSON.parse(productJson.innerHTML);
  8008.  
  8009.     // Initial state for global productState object
  8010.     this.productState = {
  8011.       available: true,
  8012.       soldOut: false,
  8013.       onSale: false,
  8014.       showUnitPrice: false
  8015.     };
  8016.  
  8017.     this.settings.zoomEnabled =
  8018.       this.imageZoomWrapper.length > 0
  8019.         ? this.imageZoomWrapper[0].classList.contains(
  8020.             this.classes.jsZoomEnabled
  8021.           )
  8022.         : false;
  8023.  
  8024.     this.initMobileBreakpoint = this._initMobileBreakpoint.bind(this);
  8025.     this.initDesktopBreakpoint = this._initDesktopBreakpoint.bind(this);
  8026.  
  8027.     this.mqlSmall = window.matchMedia(this.settings.mediaQuerySmall);
  8028.     this.mqlSmall.addListener(this.initMobileBreakpoint);
  8029.  
  8030.     this.mqlMediumUp = window.matchMedia(this.settings.mediaQueryMediumUp);
  8031.     this.mqlMediumUp.addListener(this.initDesktopBreakpoint);
  8032.  
  8033.     this.initMobileBreakpoint();
  8034.     this.initDesktopBreakpoint();
  8035.     this._stringOverrides();
  8036.     this._initVariants();
  8037.     this._initMediaSwitch();
  8038.     this._initAddToCart();
  8039.     this._setActiveThumbnail();
  8040.     this._initProductVideo();
  8041.     this._initModelViewerLibraries();
  8042.     this._initShopifyXrLaunch();
  8043.   }
  8044.  
  8045.   Product.prototype = Object.assign({}, Product.prototype, {
  8046.     _stringOverrides: function() {
  8047.       theme.productStrings = theme.productStrings || {};
  8048.       theme.strings = Object.assign({}, theme.strings, theme.productStrings);
  8049.     },
  8050.  
  8051.     _initStoreAvailability: function() {
  8052.       this.storeAvailability = new theme.StoreAvailability(
  8053.         this.storeAvailabilityContainer
  8054.       );
  8055.  
  8056.       var storeAvailabilityModalOpenedCallback = function(event) {
  8057.         if (
  8058.           this.cartPopupWrapper &&
  8059.          !this.cartPopupWrapper.classList.contains(
  8060.            this.classes.cartPopupWrapperHidden
  8061.          )
  8062.        ) {
  8063.          this._hideCartPopup(event);
  8064.         }
  8065.       };
  8066.  
  8067.       // hide cart popup modal if the store availability modal is also opened
  8068.       this.storeAvailabilityContainer.addEventListener(
  8069.         'storeAvailabilityModalOpened',
  8070.         storeAvailabilityModalOpenedCallback.bind(this)
  8071.       );
  8072.     },
  8073.  
  8074.     _initMobileBreakpoint: function() {
  8075.       if (this.mqlSmall.matches) {
  8076.         // initialize thumbnail slider on mobile if more than four thumbnails
  8077.         if (
  8078.           this.container.querySelectorAll(this.selectors.productThumbImages)
  8079.             .length > 4
  8080.         ) {
  8081.           this._initThumbnailSlider();
  8082.         }
  8083.  
  8084.         // destroy image zooming if enabled
  8085.         if (this.settings.zoomEnabled) {
  8086.           this.imageZoomWrapper.forEach(
  8087.             function(element, index) {
  8088.               this._destroyZoom(index);
  8089.             }.bind(this)
  8090.           );
  8091.         }
  8092.  
  8093.         this.settings.bpSmall = true;
  8094.       } else {
  8095.         if (this.settings.sliderActive) {
  8096.           this._destroyThumbnailSlider();
  8097.         }
  8098.  
  8099.         this.settings.bpSmall = false;
  8100.       }
  8101.     },
  8102.  
  8103.     _initDesktopBreakpoint: function() {
  8104.       if (this.mqlMediumUp.matches && this.settings.zoomEnabled) {
  8105.        this.imageZoomWrapper.forEach(
  8106.          function(element, index) {
  8107.            this._enableZoom(element, index);
  8108.           }.bind(this)
  8109.         );
  8110.       }
  8111.     },
  8112.  
  8113.     _initVariants: function() {
  8114.       var options = {
  8115.         container: this.container,
  8116.         enableHistoryState:
  8117.           this.container.getAttribute('data-enable-history-state') || false,
  8118.         singleOptionSelector: this.selectors.singleOptionSelector,
  8119.         originalSelectorId: this.selectors.originalSelectorId,
  8120.         product: this.productSingleObject
  8121.       };
  8122.  
  8123.       this.variants = new slate.Variants(options);
  8124.       if (this.storeAvailability && this.variants.currentVariant.available) {
  8125.        this.storeAvailability.updateContent(this.variants.currentVariant.id);
  8126.       }
  8127.  
  8128.       this.eventHandlers.updateAvailability = this._updateAvailability.bind(
  8129.         this
  8130.       );
  8131.       this.eventHandlers.updateMedia = this._updateMedia.bind(this);
  8132.       this.eventHandlers.updatePrice = this._updatePrice.bind(this);
  8133.       this.eventHandlers.updateSKU = this._updateSKU.bind(this);
  8134.  
  8135.       this.container.addEventListener(
  8136.         'variantChange',
  8137.         this.eventHandlers.updateAvailability
  8138.       );
  8139.       this.container.addEventListener(
  8140.         'variantImageChange',
  8141.         this.eventHandlers.updateMedia
  8142.       );
  8143.       this.container.addEventListener(
  8144.         'variantPriceChange',
  8145.         this.eventHandlers.updatePrice
  8146.       );
  8147.       this.container.addEventListener(
  8148.         'variantSKUChange',
  8149.         this.eventHandlers.updateSKU
  8150.       );
  8151.     },
  8152.  
  8153.     _initMediaSwitch: function() {
  8154.       if (!document.querySelector(this.selectors.productThumbImages)) {
  8155.         return;
  8156.       }
  8157.  
  8158.       var self = this;
  8159.  
  8160.       var productThumbImages = document.querySelectorAll(
  8161.         this.selectors.productThumbImages
  8162.       );
  8163.  
  8164.       this.eventHandlers.handleMediaFocus = this._handleMediaFocus.bind(this);
  8165.  
  8166.       productThumbImages.forEach(function(el) {
  8167.         el.addEventListener('click', function(evt) {
  8168.           evt.preventDefault();
  8169.           var mediaId = el.getAttribute('data-thumbnail-id');
  8170.  
  8171.           self._switchMedia(mediaId);
  8172.           self._setActiveThumbnail(mediaId);
  8173.         });
  8174.         el.addEventListener('keyup', self.eventHandlers.handleMediaFocus);
  8175.       });
  8176.     },
  8177.  
  8178.     _initAddToCart: function() {
  8179.       this.productForm.addEventListener(
  8180.         'submit',
  8181.         function(evt) {
  8182.           if (this.addToCart.getAttribute('aria-disabled') === 'true') {
  8183.             evt.preventDefault();
  8184.             return;
  8185.           }
  8186.  
  8187.           if (!this.ajaxEnabled) return;
  8188.  
  8189.           evt.preventDefault();
  8190.  
  8191.           this.previouslyFocusedElement = document.activeElement;
  8192.  
  8193.           var isInvalidQuantity =
  8194.             !!this.quantityInput && this.quantityInput.value <= 0;
  8195.  
  8196.           if (isInvalidQuantity) {
  8197.             this._showErrorMessage(theme.strings.quantityMinimumMessage);
  8198.             return;
  8199.           }
  8200.  
  8201.           if (!isInvalidQuantity && this.ajaxEnabled) {
  8202.            // disable the addToCart and dynamic checkout button while
  8203.            // request/cart popup is loading and handle loading state
  8204.            this._handleButtonLoadingState(true);
  8205.             this._addItemToCart(this.productForm);
  8206.             return;
  8207.           }
  8208.         }.bind(this)
  8209.       );
  8210.     },
  8211.  
  8212.     _initProductVideo: function() {
  8213.       var sectionId = this.settings.sectionId;
  8214.  
  8215.       var productMediaTypeVideo = this.container.querySelectorAll(
  8216.         this.selectors.productMediaTypeVideo
  8217.       );
  8218.       productMediaTypeVideo.forEach(function(el) {
  8219.         theme.ProductVideo.init(el, sectionId);
  8220.       });
  8221.     },
  8222.  
  8223.     _initModelViewerLibraries: function() {
  8224.       var modelViewerElements = this.container.querySelectorAll(
  8225.         this.selectors.productMediaTypeModel
  8226.       );
  8227.       if (modelViewerElements.length < 1) return;
  8228.      theme.ProductModel.init(modelViewerElements, this.settings.sectionId);
  8229.    },
  8230.  
  8231.    _initShopifyXrLaunch: function() {
  8232.      this.eventHandlers.initShopifyXrLaunchHandler = this._initShopifyXrLaunchHandler.bind(
  8233.        this
  8234.      );
  8235.      document.addEventListener(
  8236.        'shopify_xr_launch',
  8237.        this.eventHandlers.initShopifyXrLaunchHandler
  8238.      );
  8239.    },
  8240.  
  8241.    _initShopifyXrLaunchHandler: function() {
  8242.      var currentMedia = this.container.querySelector(
  8243.        this.selectors.productMediaWrapper +
  8244.          ':not(.' +
  8245.          self.classes.hidden +
  8246.          ')'
  8247.      );
  8248.      currentMedia.dispatchEvent(
  8249.        new CustomEvent('xrLaunch', {
  8250.          bubbles: true,
  8251.          cancelable: true
  8252.        })
  8253.      );
  8254.    },
  8255.  
  8256.    _addItemToCart: function(form) {
  8257.      var self = this;
  8258.  
  8259.      fetch('/cart/add.js', {
  8260.        method: 'POST',
  8261.        credentials: 'same-origin',
  8262.        headers: {
  8263.          'Content-Type': 'application/x-www-form-urlencoded',
  8264.          'X-Requested-With': 'XMLHttpRequest'
  8265.        },
  8266.        body: theme.Helpers.serialize(form)
  8267.      })
  8268.        .then(function(response) {
  8269.          return response.json();
  8270.        })
  8271.        .then(function(json) {
  8272.          if (json.status && json.status !== 200) {
  8273.            var error = new Error(json.description);
  8274.            error.isFromServer = true;
  8275.            throw error;
  8276.          }
  8277.          self._hideErrorMessage();
  8278.          self._setupCartPopup(json);
  8279.        })
  8280.        .catch(function(error) {
  8281.          self.previouslyFocusedElement.focus();
  8282.          self._showErrorMessage(
  8283.            error.isFromServer && error.message.length
  8284.              ? error.message
  8285.              : theme.strings.cartError
  8286.          );
  8287.          self._handleButtonLoadingState(false);
  8288.          // eslint-disable-next-line no-console
  8289.          console.log(error);
  8290.        });
  8291.    },
  8292.  
  8293.    _handleButtonLoadingState: function(isLoading) {
  8294.      if (isLoading) {
  8295.        this.addToCart.setAttribute('aria-disabled', true);
  8296.        this.addToCartText.classList.add(this.classes.hidden);
  8297.        this.loader.classList.remove(this.classes.hidden);
  8298.  
  8299.        if (this.shopifyPaymentButton) {
  8300.          this.shopifyPaymentButton.setAttribute('disabled', true);
  8301.        }
  8302.  
  8303.        this.loaderStatus.setAttribute('aria-hidden', false);
  8304.      } else {
  8305.        this.addToCart.removeAttribute('aria-disabled');
  8306.        this.addToCartText.classList.remove(this.classes.hidden);
  8307.        this.loader.classList.add(this.classes.hidden);
  8308.  
  8309.        if (this.shopifyPaymentButton) {
  8310.          this.shopifyPaymentButton.removeAttribute('disabled');
  8311.        }
  8312.  
  8313.        this.loaderStatus.setAttribute('aria-hidden', true);
  8314.      }
  8315.    },
  8316.  
  8317.    _showErrorMessage: function(errorMessage) {
  8318.      var errorMessageContainer = this.container.querySelector(
  8319.        this.selectors.errorMessage
  8320.      );
  8321.      errorMessageContainer.innerHTML = errorMessage;
  8322.  
  8323.      if (this.quantityInput) {
  8324.        this.quantityInput.classList.add(this.classes.inputError);
  8325.      }
  8326.  
  8327.      this.errorMessageWrapper.classList.remove(
  8328.        this.classes.productFormErrorMessageWrapperHidden
  8329.      );
  8330.      this.errorMessageWrapper.setAttribute('aria-hidden', true);
  8331.      this.errorMessageWrapper.removeAttribute('aria-hidden');
  8332.    },
  8333.  
  8334.    _hideErrorMessage: function() {
  8335.      this.errorMessageWrapper.classList.add(
  8336.        this.classes.productFormErrorMessageWrapperHidden
  8337.      );
  8338.  
  8339.      if (this.quantityInput) {
  8340.        this.quantityInput.classList.remove(this.classes.inputError);
  8341.      }
  8342.    },
  8343.  
  8344.    _setupCartPopup: function(item) {
  8345.      this.cartPopup =
  8346.        this.cartPopup || document.querySelector(this.selectors.cartPopup);
  8347.      this.cartPopupWrapper =
  8348.        this.cartPopupWrapper ||
  8349.        document.querySelector(this.selectors.cartPopupWrapper);
  8350.      this.cartPopupTitle =
  8351.        this.cartPopupTitle ||
  8352.        document.querySelector(this.selectors.cartPopupTitle);
  8353.      this.cartPopupQuantity =
  8354.        this.cartPopupQuantity ||
  8355.        document.querySelector(this.selectors.cartPopupQuantity);
  8356.      this.cartPopupQuantityLabel =
  8357.        this.cartPopupQuantityLabel ||
  8358.        document.querySelector(this.selectors.cartPopupQuantityLabel);
  8359.      this.cartPopupClose =
  8360.        this.cartPopupClose ||
  8361.        document.querySelector(this.selectors.cartPopupClose);
  8362.      this.cartPopupDismiss =
  8363.        this.cartPopupDismiss ||
  8364.        document.querySelector(this.selectors.cartPopupDismiss);
  8365.      this.cartPopupImagePlaceholder =
  8366.        this.cartPopupImagePlaceholder ||
  8367.        document.querySelector(this.selectors.cartPopupImagePlaceholder);
  8368.  
  8369.      this._setupCartPopupEventListeners();
  8370.  
  8371.      this._updateCartPopupContent(item);
  8372.    },
  8373.  
  8374.    _updateCartPopupContent: function(item) {
  8375.      var self = this;
  8376.  
  8377.      var quantity = this.quantityInput ? this.quantityInput.value : 1;
  8378.  
  8379.      var selling_plan_name = item.selling_plan_allocation
  8380.        ? item.selling_plan_allocation.selling_plan.name
  8381.        : null;
  8382.  
  8383.      this.cartPopupTitle.textContent = item.product_title;
  8384.      this.cartPopupQuantity.textContent = quantity;
  8385.      this.cartPopupQuantityLabel.textContent = theme.strings.quantityLabel.replace(
  8386.        '[count]',
  8387.        quantity
  8388.      );
  8389.  
  8390.      this._setCartPopupPlaceholder(item.featured_image.url);
  8391.      this._setCartPopupImage(item.featured_image.url, item.featured_image.alt);
  8392.      this._setCartPopupProductDetails(
  8393.        item.product_has_only_default_variant,
  8394.        item.options_with_values,
  8395.        item.properties,
  8396.        selling_plan_name
  8397.      );
  8398.  
  8399.      fetch('/cart.js', { credentials: 'same-origin' })
  8400.        .then(function(response) {
  8401.          return response.json();
  8402.        })
  8403.        .then(function(cart) {
  8404.          self._setCartQuantity(cart.item_count);
  8405.          self._setCartCountBubble(cart.item_count);
  8406.          self._showCartPopup();
  8407.        })
  8408.        .catch(function(error) {
  8409.          // eslint-disable-next-line no-console
  8410.          console.log(error);
  8411.        });
  8412.    },
  8413.  
  8414.    _setupCartPopupEventListeners: function() {
  8415.      this.eventHandlers.cartPopupWrapperKeyupHandler = this._cartPopupWrapperKeyupHandler.bind(
  8416.        this
  8417.      );
  8418.      this.eventHandlers.hideCartPopup = this._hideCartPopup.bind(this);
  8419.      this.eventHandlers.onBodyClick = this._onBodyClick.bind(this);
  8420.  
  8421.      this.cartPopupWrapper.addEventListener(
  8422.        'keyup',
  8423.        this.eventHandlers.cartPopupWrapperKeyupHandler
  8424.      );
  8425.      this.cartPopupClose.addEventListener(
  8426.        'click',
  8427.        this.eventHandlers.hideCartPopup
  8428.      );
  8429.      this.cartPopupDismiss.addEventListener(
  8430.        'click',
  8431.        this.eventHandlers.hideCartPopup
  8432.      );
  8433.      document.body.addEventListener('click', this.eventHandlers.onBodyClick);
  8434.    },
  8435.  
  8436.    _cartPopupWrapperKeyupHandler: function(event) {
  8437.      if (event.keyCode === slate.utils.keyboardKeys.ESCAPE) {
  8438.        this._hideCartPopup(event);
  8439.      }
  8440.    },
  8441.  
  8442.    _setCartPopupPlaceholder: function(imageUrl) {
  8443.      this.cartPopupImageWrapper =
  8444.        this.cartPopupImageWrapper ||
  8445.        document.querySelector(this.selectors.cartPopupImageWrapper);
  8446.  
  8447.      if (imageUrl === null) {
  8448.        this.cartPopupImageWrapper.classList.add(this.classes.hidden);
  8449.        return;
  8450.      }
  8451.    },
  8452.  
  8453.    _setCartPopupImage: function(imageUrl, imageAlt) {
  8454.      if (imageUrl === null) return;
  8455.  
  8456.      this.cartPopupImageWrapper.classList.remove(this.classes.hidden);
  8457.      var sizedImageUrl = theme.Images.getSizedImageUrl(imageUrl, '200x');
  8458.      var image = document.createElement('img');
  8459.      image.src = sizedImageUrl;
  8460.      image.alt = imageAlt;
  8461.      image.classList.add(this.classes.cartImage);
  8462.      image.setAttribute('data-cart-popup-image', '');
  8463.  
  8464.      image.onload = function() {
  8465.        this.cartPopupImagePlaceholder.removeAttribute(
  8466.          'data-image-loading-animation'
  8467.        );
  8468.        this.cartPopupImageWrapper.append(image);
  8469.      }.bind(this);
  8470.    },
  8471.  
  8472.    _setCartPopupProductDetails: function(
  8473.      product_has_only_default_variant,
  8474.      options,
  8475.      properties,
  8476.      selling_plan_name
  8477.    ) {
  8478.      this.cartPopupProductDetails =
  8479.        this.cartPopupProductDetails ||
  8480.        document.querySelector(this.selectors.cartPopupProductDetails);
  8481.      var variantPropertiesHTML = '';
  8482.  
  8483.      if (!product_has_only_default_variant) {
  8484.        variantPropertiesHTML =
  8485.          variantPropertiesHTML + this._getVariantOptionList(options);
  8486.      }
  8487.  
  8488.      if (selling_plan_name) {
  8489.        variantPropertiesHTML =
  8490.          variantPropertiesHTML + this._getSellingPlanHTML(selling_plan_name);
  8491.      }
  8492.  
  8493.      if (properties !== null && Object.keys(properties).length !== 0) {
  8494.        variantPropertiesHTML =
  8495.          variantPropertiesHTML + this._getPropertyList(properties);
  8496.      }
  8497.  
  8498.      if (variantPropertiesHTML.length === 0) {
  8499.        this.cartPopupProductDetails.innerHTML = '';
  8500.        this.cartPopupProductDetails.setAttribute('hidden', '');
  8501.      } else {
  8502.        this.cartPopupProductDetails.innerHTML = variantPropertiesHTML;
  8503.        this.cartPopupProductDetails.removeAttribute('hidden');
  8504.      }
  8505.    },
  8506.  
  8507.    _getVariantOptionList: function(variantOptions) {
  8508.      var variantOptionListHTML = '';
  8509.  
  8510.      variantOptions.forEach(function(variantOption) {
  8511.        variantOptionListHTML =
  8512.          variantOptionListHTML +
  8513.          '<li class="product-details__item product-details__item--variant-option">' +
  8514.           variantOption.name +
  8515.           ': ' +
  8516.           variantOption.value +
  8517.           '</li>';
  8518.       });
  8519.  
  8520.       return variantOptionListHTML;
  8521.     },
  8522.  
  8523.     _getPropertyList: function(properties) {
  8524.       var propertyListHTML = '';
  8525.       var propertiesArray = Object.entries(properties);
  8526.  
  8527.       propertiesArray.forEach(function(property) {
  8528.         // Line item properties prefixed with an underscore are not to be displayed
  8529.         if (property[0].charAt(0) === '_') return;
  8530.  
  8531.         // if the property value has a length of 0 (empty), don't display it
  8532.         if (property[1].length === 0) return;
  8533.  
  8534.         propertyListHTML =
  8535.           propertyListHTML +
  8536.           '<li class="product-details__item product-details__item--property">' +
  8537.           '<span class="product-details__property-label">' +
  8538.           property[0] +
  8539.           ': </span>' +
  8540.           property[1];
  8541.         ': ' + '</li>';
  8542.       });
  8543.  
  8544.       return propertyListHTML;
  8545.     },
  8546.  
  8547.     _getSellingPlanHTML: function(selling_plan_name) {
  8548.       var sellingPlanHTML =
  8549.         '<li class="product-details__item product-details__item--property">' +
  8550.         selling_plan_name +
  8551.         '</li>';
  8552.  
  8553.       return sellingPlanHTML;
  8554.     },
  8555.  
  8556.     _setCartQuantity: function(quantity) {
  8557.       this.cartPopupCartQuantity =
  8558.         this.cartPopupCartQuantity ||
  8559.         document.querySelector(this.selectors.cartPopupCartQuantity);
  8560.       var ariaLabel;
  8561.  
  8562.       if (quantity === 1) {
  8563.         ariaLabel = theme.strings.oneCartCount;
  8564.       } else if (quantity > 1) {
  8565.         ariaLabel = theme.strings.otherCartCount.replace('[count]', quantity);
  8566.       }
  8567.  
  8568.       this.cartPopupCartQuantity.textContent = quantity;
  8569.       this.cartPopupCartQuantity.setAttribute('aria-label', ariaLabel);
  8570.     },
  8571.  
  8572.     _setCartCountBubble: function(quantity) {
  8573.       this.cartCountBubble =
  8574.         this.cartCountBubble ||
  8575.         document.querySelector(this.selectors.cartCountBubble);
  8576.       this.cartCount =
  8577.         this.cartCount || document.querySelector(this.selectors.cartCount);
  8578.  
  8579.       this.cartCountBubble.classList.remove(this.classes.hidden);
  8580.       this.cartCount.textContent = quantity;
  8581.     },
  8582.  
  8583.     _showCartPopup: function() {
  8584.       theme.Helpers.prepareTransition(this.cartPopupWrapper);
  8585.  
  8586.       this.cartPopupWrapper.classList.remove(
  8587.         this.classes.cartPopupWrapperHidden
  8588.       );
  8589.       this._handleButtonLoadingState(false);
  8590.  
  8591.       slate.a11y.trapFocus({
  8592.         container: this.cartPopupWrapper,
  8593.         elementToFocus: this.cartPopup,
  8594.         namespace: 'cartPopupFocus'
  8595.       });
  8596.     },
  8597.  
  8598.     _hideCartPopup: function(event) {
  8599.       var setFocus = event.detail === 0 ? true : false;
  8600.       theme.Helpers.prepareTransition(this.cartPopupWrapper);
  8601.       this.cartPopupWrapper.classList.add(this.classes.cartPopupWrapperHidden);
  8602.  
  8603.       var cartPopupImage = document.querySelector(
  8604.         this.selectors.cartPopupImage
  8605.       );
  8606.       if (cartPopupImage) {
  8607.         cartPopupImage.remove();
  8608.       }
  8609.       this.cartPopupImagePlaceholder.setAttribute(
  8610.         'data-image-loading-animation',
  8611.         ''
  8612.       );
  8613.  
  8614.       slate.a11y.removeTrapFocus({
  8615.         container: this.cartPopupWrapper,
  8616.         namespace: 'cartPopupFocus'
  8617.       });
  8618.  
  8619.       if (setFocus) this.previouslyFocusedElement.focus();
  8620.  
  8621.       this.cartPopupWrapper.removeEventListener(
  8622.         'keyup',
  8623.         this.eventHandlers.cartPopupWrapperKeyupHandler
  8624.       );
  8625.       this.cartPopupClose.removeEventListener(
  8626.         'click',
  8627.         this.eventHandlers.hideCartPopup
  8628.       );
  8629.       this.cartPopupDismiss.removeEventListener(
  8630.         'click',
  8631.         this.eventHandlers.hideCartPopup
  8632.       );
  8633.       document.body.removeEventListener(
  8634.         'click',
  8635.         this.eventHandlers.onBodyClick
  8636.       );
  8637.     },
  8638.  
  8639.     _onBodyClick: function(event) {
  8640.       var target = event.target;
  8641.  
  8642.       if (
  8643.         target !== this.cartPopupWrapper &&
  8644.        !target.closest(this.selectors.cartPopup)
  8645.      ) {
  8646.        this._hideCartPopup(event);
  8647.       }
  8648.     },
  8649.  
  8650.     _setActiveThumbnail: function(mediaId) {
  8651.       // If there is no element passed, find it by the current product image
  8652.       if (typeof mediaId === 'undefined') {
  8653.         var productMediaWrapper = this.container.querySelector(
  8654.           this.selectors.productMediaWrapper + ':not(.hide)'
  8655.         );
  8656.  
  8657.         if (!productMediaWrapper) return;
  8658.         mediaId = productMediaWrapper.getAttribute('data-media-id');
  8659.       }
  8660.  
  8661.       var thumbnailWrappers = this.container.querySelectorAll(
  8662.         this.selectors.productThumbListItem + ':not(.slick-cloned)'
  8663.       );
  8664.  
  8665.       var activeThumbnail;
  8666.       thumbnailWrappers.forEach(
  8667.         function(el) {
  8668.           var current = el.querySelector(
  8669.             this.selectors.productThumbImages +
  8670.               "[data-thumbnail-id='" +
  8671.               mediaId +
  8672.               "']"
  8673.           );
  8674.           if (current) {
  8675.             activeThumbnail = current;
  8676.           }
  8677.         }.bind(this)
  8678.       );
  8679.  
  8680.       var productThumbImages = document.querySelectorAll(
  8681.         this.selectors.productThumbImages
  8682.       );
  8683.       productThumbImages.forEach(
  8684.         function(el) {
  8685.           el.classList.remove(this.classes.activeClass);
  8686.           el.removeAttribute('aria-current');
  8687.         }.bind(this)
  8688.       );
  8689.  
  8690.       if (activeThumbnail) {
  8691.         activeThumbnail.classList.add(this.classes.activeClass);
  8692.         activeThumbnail.setAttribute('aria-current', true);
  8693.         this._adjustThumbnailSlider(activeThumbnail);
  8694.       }
  8695.     },
  8696.  
  8697.     _adjustThumbnailSlider: function(activeThumbnail) {
  8698.       var sliderItem = activeThumbnail.closest('[data-slider-item]');
  8699.       if (!sliderItem) return;
  8700.  
  8701.       var slideGroupLeaderIndex =
  8702.         Math.floor(
  8703.           Number(sliderItem.getAttribute('data-slider-slide-index')) / 3
  8704.         ) * 3;
  8705.  
  8706.       window.setTimeout(
  8707.         function() {
  8708.           if (!this.slideshow) return;
  8709.           this.slideshow.goToSlideByIndex(slideGroupLeaderIndex);
  8710.         }.bind(this),
  8711.         251
  8712.       );
  8713.     },
  8714.  
  8715.     _switchMedia: function(mediaId) {
  8716.       var currentMedia = this.container.querySelector(
  8717.         this.selectors.productMediaWrapper +
  8718.           ':not(.' +
  8719.           this.classes.hidden +
  8720.           ')'
  8721.       );
  8722.  
  8723.       var newMedia = this.container.querySelector(
  8724.         this.selectors.productMediaWrapper + "[data-media-id='" + mediaId + "']"
  8725.       );
  8726.  
  8727.       var otherMedia = this.container.querySelectorAll(
  8728.         this.selectors.productMediaWrapper +
  8729.           ":not([data-media-id='" +
  8730.           mediaId +
  8731.           "'])"
  8732.       );
  8733.  
  8734.       currentMedia.dispatchEvent(
  8735.         new CustomEvent('mediaHidden', {
  8736.           bubbles: true,
  8737.           cancelable: true
  8738.         })
  8739.       );
  8740.       newMedia.classList.remove(this.classes.hidden);
  8741.       newMedia.dispatchEvent(
  8742.         new CustomEvent('mediaVisible', {
  8743.           bubbles: true,
  8744.           cancelable: true
  8745.         })
  8746.       );
  8747.       otherMedia.forEach(
  8748.         function(el) {
  8749.           el.classList.add(this.classes.hidden);
  8750.         }.bind(this)
  8751.       );
  8752.     },
  8753.  
  8754.     _handleMediaFocus: function(evt) {
  8755.       if (evt.keyCode !== slate.utils.keyboardKeys.ENTER) return;
  8756.  
  8757.       var mediaId = evt.currentTarget.getAttribute('data-thumbnail-id');
  8758.  
  8759.       var productMediaWrapper = this.container.querySelector(
  8760.         this.selectors.productMediaWrapper + "[data-media-id='" + mediaId + "']"
  8761.       );
  8762.       productMediaWrapper.focus();
  8763.     },
  8764.  
  8765.     _initThumbnailSlider: function() {
  8766.       setTimeout(
  8767.         function() {
  8768.           this.slideshow = new theme.Slideshow(
  8769.             this.container.querySelector('[data-thumbnail-slider]'),
  8770.             {
  8771.               canUseTouchEvents: true,
  8772.               type: 'slide',
  8773.               slideActiveClass: 'slick-active',
  8774.               slidesToShow: 3,
  8775.               slidesToScroll: 3
  8776.             }
  8777.           );
  8778.  
  8779.           this.settings.sliderActive = true;
  8780.         }.bind(this),
  8781.         250
  8782.       );
  8783.     },
  8784.  
  8785.     _destroyThumbnailSlider: function() {
  8786.       var sliderButtons = this.container.querySelectorAll(
  8787.         '[data-slider-button]'
  8788.       );
  8789.       var sliderTrack = this.container.querySelector('[data-slider-track]');
  8790.       var sliderItems = sliderTrack.querySelectorAll('[data-slider-item');
  8791.       this.settings.sliderActive = false;
  8792.  
  8793.       if (sliderTrack) {
  8794.         sliderTrack.removeAttribute('style');
  8795.         sliderItems.forEach(function(sliderItem) {
  8796.           var sliderItemLink = sliderItem.querySelector(
  8797.             '[data-slider-item-link]'
  8798.           );
  8799.           sliderItem.classList.remove('slick-active');
  8800.           sliderItem.removeAttribute('style');
  8801.           sliderItem.removeAttribute('tabindex');
  8802.           sliderItem.removeAttribute('aria-hidden');
  8803.           sliderItemLink.removeAttribute('tabindex');
  8804.         });
  8805.       }
  8806.  
  8807.       sliderButtons.forEach(function(sliderButton) {
  8808.         sliderButton.removeAttribute('aria-disabled');
  8809.       });
  8810.  
  8811.       this.slideshow.destroy();
  8812.       this.slideshow = null;
  8813.     },
  8814.  
  8815.     _liveRegionText: function(variant) {
  8816.       // Dummy content for live region
  8817.       var liveRegionText =
  8818.         '[Availability] [Regular] [$$] [Sale] [$]. [UnitPrice] [$$$]';
  8819.  
  8820.       if (!this.productState.available) {
  8821.         liveRegionText = theme.strings.unavailable;
  8822.         return liveRegionText;
  8823.       }
  8824.  
  8825.       // Update availability
  8826.       var availability = this.productState.soldOut
  8827.         ? theme.strings.soldOut + ','
  8828.         : '';
  8829.       liveRegionText = liveRegionText.replace('[Availability]', availability);
  8830.  
  8831.       // Update pricing information
  8832.       var regularLabel = '';
  8833.       var regularPrice = theme.Currency.formatMoney(
  8834.         variant.price,
  8835.         theme.moneyFormat
  8836.       );
  8837.       var saleLabel = '';
  8838.       var salePrice = '';
  8839.       var unitLabel = '';
  8840.       var unitPrice = '';
  8841.  
  8842.       if (this.productState.onSale) {
  8843.         regularLabel = theme.strings.regularPrice;
  8844.         regularPrice =
  8845.           theme.Currency.formatMoney(
  8846.             variant.compare_at_price,
  8847.             theme.moneyFormat
  8848.           ) + ',';
  8849.         saleLabel = theme.strings.sale;
  8850.         salePrice = theme.Currency.formatMoney(
  8851.           variant.price,
  8852.           theme.moneyFormat
  8853.         );
  8854.       }
  8855.  
  8856.       if (this.productState.showUnitPrice) {
  8857.         unitLabel = theme.strings.unitPrice;
  8858.         unitPrice =
  8859.           theme.Currency.formatMoney(variant.unit_price, theme.moneyFormat) +
  8860.           ' ' +
  8861.           theme.strings.unitPriceSeparator +
  8862.           ' ' +
  8863.           this._getBaseUnit(variant);
  8864.       }
  8865.  
  8866.       liveRegionText = liveRegionText
  8867.         .replace('[Regular]', regularLabel)
  8868.         .replace('[$$]', regularPrice)
  8869.         .replace('[Sale]', saleLabel)
  8870.         .replace('[$]', salePrice)
  8871.         .replace('[UnitPrice]', unitLabel)
  8872.         .replace('[$$$]', unitPrice)
  8873.         .trim();
  8874.  
  8875.       return liveRegionText;
  8876.     },
  8877.  
  8878.     _updateLiveRegion: function(evt) {
  8879.       var variant = evt.detail.variant;
  8880.       var liveRegion = this.container.querySelector(
  8881.         this.selectors.productStatus
  8882.       );
  8883.       liveRegion.innerHTML = this._liveRegionText(variant);
  8884.       liveRegion.setAttribute('aria-hidden', false);
  8885.       // hide content from accessibility tree after announcement
  8886.       setTimeout(function() {
  8887.         liveRegion.setAttribute('aria-hidden', true);
  8888.       }, 1000);
  8889.     },
  8890.  
  8891.     _enableAddToCart: function(message) {
  8892.       this.addToCart.removeAttribute('aria-disabled');
  8893.       this.addToCart.setAttribute('aria-label', message);
  8894.       this.addToCartText.innerHTML = message;
  8895.       this.productForm.classList.remove(this.classes.variantSoldOut);
  8896.     },
  8897.  
  8898.     _disableAddToCart: function(message) {
  8899.       message = message || theme.strings.unavailable;
  8900.       this.addToCart.setAttribute('aria-disabled', true);
  8901.       this.addToCart.setAttribute('aria-label', message);
  8902.       this.addToCartText.innerHTML = message;
  8903.       this.productForm.classList.add(this.classes.variantSoldOut);
  8904.     },
  8905.  
  8906.     _updateAddToCart: function() {
  8907.       if (!this.productState.available) {
  8908.         this._disableAddToCart(theme.strings.unavailable);
  8909.         return;
  8910.       }
  8911.       if (this.productState.soldOut) {
  8912.         this._disableAddToCart(theme.strings.soldOut);
  8913.         return;
  8914.       }
  8915.  
  8916.       this._enableAddToCart(theme.strings.addToCart);
  8917.     },
  8918.  
  8919.     /**
  8920.      * The returned productState object keeps track of a number of properties about the current variant and product
  8921.      * Multiple functions within product.js leverage the productState object to determine how to update the page's UI
  8922.      * @param {object} evt - object returned from variant change event
  8923.      * @return {object} productState - current product variant's state
  8924.      *                  productState.available - true if current product options result in valid variant
  8925.      *                  productState.soldOut - true if variant is sold out
  8926.      *                  productState.onSale - true if variant is on sale
  8927.      *                  productState.showUnitPrice - true if variant has unit price value
  8928.      */
  8929.     _setProductState: function(evt) {
  8930.       var variant = evt.detail.variant;
  8931.  
  8932.       if (!variant) {
  8933.         this.productState.available = false;
  8934.         return;
  8935.       }
  8936.  
  8937.       this.productState.available = true;
  8938.       this.productState.soldOut = !variant.available;
  8939.       this.productState.onSale = variant.compare_at_price > variant.price;
  8940.       this.productState.showUnitPrice = !!variant.unit_price;
  8941.     },
  8942.  
  8943.     _updateAvailability: function(evt) {
  8944.       // remove error message if one is showing
  8945.       this._hideErrorMessage();
  8946.  
  8947.       // set product state
  8948.       this._setProductState(evt);
  8949.  
  8950.       // update store availabilities info
  8951.       this._updateStoreAvailabilityContent(evt);
  8952.       // update form submit
  8953.       this._updateAddToCart();
  8954.       // update live region
  8955.       this._updateLiveRegion(evt);
  8956.  
  8957.       this._updatePrice(evt);
  8958.     },
  8959.  
  8960.     _updateStoreAvailabilityContent: function(evt) {
  8961.       if (!this.storeAvailability) {
  8962.         return;
  8963.       }
  8964.  
  8965.       if (this.productState.available && !this.productState.soldOut) {
  8966.        this.storeAvailability.updateContent(evt.detail.variant.id);
  8967.       } else {
  8968.         this.storeAvailability.clearContent();
  8969.       }
  8970.     },
  8971.  
  8972.     _updateMedia: function(evt) {
  8973.       var variant = evt.detail.variant;
  8974.       var mediaId = variant.featured_media.id;
  8975.       var sectionMediaId = this.settings.sectionId + '-' + mediaId;
  8976.  
  8977.       this._switchMedia(sectionMediaId);
  8978.       this._setActiveThumbnail(sectionMediaId);
  8979.     },
  8980.  
  8981.     _updatePrice: function(evt) {
  8982.       var variant = evt.detail.variant;
  8983.  
  8984.       var priceContainer = this.container.querySelector(
  8985.         this.selectors.priceContainer
  8986.       );
  8987.       var regularPrices = priceContainer.querySelectorAll(
  8988.         this.selectors.regularPrice
  8989.       );
  8990.       var salePrice = priceContainer.querySelector(this.selectors.salePrice);
  8991.       var unitPrice = priceContainer.querySelector(this.selectors.unitPrice);
  8992.       var unitPriceBaseUnit = priceContainer.querySelector(
  8993.         this.selectors.unitPriceBaseUnit
  8994.       );
  8995.  
  8996.       var formatRegularPrice = function(regularPriceElement, price) {
  8997.         regularPriceElement.innerHTML = theme.Currency.formatMoney(
  8998.           price,
  8999.           theme.moneyFormat
  9000.         );
  9001.       };
  9002.  
  9003.       // Reset product price state
  9004.  
  9005.       priceContainer.classList.remove(
  9006.         this.classes.productUnavailable,
  9007.         this.classes.productOnSale,
  9008.         this.classes.productUnitAvailable,
  9009.         this.classes.productSoldOut
  9010.       );
  9011.       priceContainer.removeAttribute('aria-hidden');
  9012.  
  9013.       if (this.productPolicies) {
  9014.         this.productPolicies.classList.remove(this.classes.visibilityHidden);
  9015.       }
  9016.  
  9017.       // Unavailable
  9018.       if (!this.productState.available) {
  9019.         priceContainer.classList.add(this.classes.productUnavailable);
  9020.         priceContainer.setAttribute('aria-hidden', true);
  9021.  
  9022.         if (this.productPolicies) {
  9023.           this.productPolicies.classList.add(this.classes.visibilityHidden);
  9024.         }
  9025.         return;
  9026.       }
  9027.  
  9028.       // Sold out
  9029.       if (this.productState.soldOut) {
  9030.         priceContainer.classList.add(this.classes.productSoldOut);
  9031.       }
  9032.  
  9033.       // On sale
  9034.       if (this.productState.onSale) {
  9035.         regularPrices.forEach(function(regularPrice) {
  9036.           formatRegularPrice(regularPrice, variant.compare_at_price);
  9037.         });
  9038.  
  9039.         salePrice.innerHTML = theme.Currency.formatMoney(
  9040.           variant.price,
  9041.           theme.moneyFormat
  9042.         );
  9043.         priceContainer.classList.add(this.classes.productOnSale);
  9044.       } else {
  9045.         // Regular price
  9046.         regularPrices.forEach(function(regularPrice) {
  9047.           formatRegularPrice(regularPrice, variant.price);
  9048.         });
  9049.       }
  9050.  
  9051.       // Unit price
  9052.       if (this.productState.showUnitPrice) {
  9053.         unitPrice.innerHTML = theme.Currency.formatMoney(
  9054.           variant.unit_price,
  9055.           theme.moneyFormat
  9056.         );
  9057.         unitPriceBaseUnit.innerHTML = this._getBaseUnit(variant);
  9058.         priceContainer.classList.add(this.classes.productUnitAvailable);
  9059.       }
  9060.     },
  9061.  
  9062.     _getBaseUnit: function(variant) {
  9063.       return variant.unit_price_measurement.reference_value === 1
  9064.         ? variant.unit_price_measurement.reference_unit
  9065.         : variant.unit_price_measurement.reference_value +
  9066.             variant.unit_price_measurement.reference_unit;
  9067.     },
  9068.  
  9069.     _updateSKU: function(evt) {
  9070.       var variant = evt.detail.variant;
  9071.  
  9072.       // Update the sku
  9073.       var sku = document.querySelector(this.selectors.SKU);
  9074.       if (!sku) return;
  9075.       sku.innerHTML = variant.sku;
  9076.     },
  9077.  
  9078.     _enableZoom: function(el, index) {
  9079.       this.zoomPictures[index] = new theme.Zoom(el);
  9080.     },
  9081.  
  9082.     _destroyZoom: function(index) {
  9083.       if (this.zoomPictures.length === 0) return;
  9084.       this.zoomPictures[index].unload();
  9085.     },
  9086.  
  9087.     onUnload: function() {
  9088.       this.container.removeEventListener(
  9089.         'variantChange',
  9090.         this.eventHandlers.updateAvailability
  9091.       );
  9092.       this.container.removeEventListener(
  9093.         'variantImageChange',
  9094.         this.eventHandlers.updateMedia
  9095.       );
  9096.       this.container.removeEventListener(
  9097.         'variantPriceChange',
  9098.         this.eventHandlers.updatePrice
  9099.       );
  9100.       this.container.removeEventListener(
  9101.         'variantSKUChange',
  9102.         this.eventHandlers.updateSKU
  9103.       );
  9104.       theme.ProductVideo.removeSectionVideos(this.settings.sectionId);
  9105.       theme.ProductModel.removeSectionModels(this.settings.sectionId);
  9106.  
  9107.       if (this.mqlSmall) {
  9108.         this.mqlSmall.removeListener(this.initMobileBreakpoint);
  9109.       }
  9110.  
  9111.       if (this.mqlMediumUp) {
  9112.         this.mqlMediumUp.removeListener(this.initDesktopBreakpoint);
  9113.       }
  9114.     }
  9115.   });
  9116.  
  9117.   return Product;
  9118. })();
  9119.  
  9120. theme.ProductRecommendations = (function() {
  9121.   function ProductRecommendations(container) {
  9122.     var baseUrl = container.dataset.baseUrl;
  9123.     var productId = container.dataset.productId;
  9124.     var recommendationsSectionUrl =
  9125.       baseUrl +
  9126.       '?section_id=product-recommendations&product_id=' +
  9127.      productId +
  9128.      '&limit=4';
  9129.  
  9130.     window.performance.mark(
  9131.       'debut:product:fetch_product_recommendations.start'
  9132.     );
  9133.  
  9134.     fetch(recommendationsSectionUrl)
  9135.       .then(function(response) {
  9136.         return response.text();
  9137.       })
  9138.       .then(function(productHtml) {
  9139.         if (productHtml.trim() === '') return;
  9140.  
  9141.         container.innerHTML = productHtml;
  9142.         container.innerHTML = container.firstElementChild.innerHTML;
  9143.  
  9144.         window.performance.mark(
  9145.           'debut:product:fetch_product_recommendations.end'
  9146.         );
  9147.  
  9148.         performance.measure(
  9149.           'debut:product:fetch_product_recommendations',
  9150.           'debut:product:fetch_product_recommendations.start',
  9151.           'debut:product:fetch_product_recommendations.end'
  9152.         );
  9153.       });
  9154.   }
  9155.  
  9156.   return ProductRecommendations;
  9157. })();
  9158.  
  9159. theme.Quotes = (function() {
  9160.   var config = {
  9161.     mediaQuerySmall: 'screen and (max-width: 749px)',
  9162.     mediaQueryMediumUp: 'screen and (min-width: 750px)',
  9163.     slideCount: 0
  9164.   };
  9165.  
  9166.   var defaults = {
  9167.     canUseKeyboardArrows: false,
  9168.     type: 'slide',
  9169.     slidesToShow: 3
  9170.   };
  9171.  
  9172.   function Quotes(container) {
  9173.     this.container = container;
  9174.     var sectionId = container.getAttribute('data-section-id');
  9175.     this.slider = document.getElementById('Quotes-' + sectionId);
  9176.  
  9177.     this.sliderActive = false;
  9178.  
  9179.     this.mobileOptions = Object.assign({}, defaults, {
  9180.       canUseTouchEvents: true,
  9181.       slidesToShow: 1
  9182.     });
  9183.  
  9184.     this.desktopOptions = Object.assign({}, defaults, {
  9185.       slidesToShow: Math.min(
  9186.         defaults.slidesToShow,
  9187.         this.slider.getAttribute('data-count')
  9188.       )
  9189.     });
  9190.  
  9191.     this.initMobileSlider = this._initMobileSlider.bind(this);
  9192.     this.initDesktopSlider = this._initDesktopSlider.bind(this);
  9193.  
  9194.     this.mqlSmall = window.matchMedia(config.mediaQuerySmall);
  9195.     this.mqlSmall.addListener(this.initMobileSlider);
  9196.  
  9197.     this.mqlMediumUp = window.matchMedia(config.mediaQueryMediumUp);
  9198.     this.mqlMediumUp.addListener(this.initDesktopSlider);
  9199.  
  9200.     this.initMobileSlider();
  9201.     this.initDesktopSlider();
  9202.   }
  9203.  
  9204.   Quotes.prototype = Object.assign({}, Quotes.prototype, {
  9205.     onUnload: function() {
  9206.       this.mqlSmall.removeListener(this.initMobileSlider);
  9207.       this.mqlMediumUp.removeListener(this.initDesktopSlider);
  9208.       this.slideshow.destroy();
  9209.     },
  9210.  
  9211.     // eslint-disable-next-line no-unused-vars
  9212.     onBlockSelect: function(evt) {
  9213.       var slide = document.querySelector(
  9214.         '.quotes-slide--' + evt.detail.blockId
  9215.       );
  9216.       var slideIndex = Number(slide.getAttribute('data-slider-slide-index'));
  9217.  
  9218.       if (this.mqlMediumUp.matches) {
  9219.         slideIndex = Math.max(
  9220.           0,
  9221.           Math.min(slideIndex, this.slideshow.slides.length - 3)
  9222.         );
  9223.       }
  9224.  
  9225.       this.slideshow.goToSlideByIndex(slideIndex);
  9226.     },
  9227.  
  9228.     _initMobileSlider: function() {
  9229.       if (this.mqlSmall.matches) {
  9230.         this._initSlider(this.mobileOptions);
  9231.       }
  9232.     },
  9233.  
  9234.     _initDesktopSlider: function() {
  9235.       if (this.mqlMediumUp.matches) {
  9236.         this._initSlider(this.desktopOptions);
  9237.       }
  9238.     },
  9239.  
  9240.     // eslint-disable-next-line no-unused-vars
  9241.     _initSlider: function(args) {
  9242.       if (this.sliderActive) {
  9243.         this.slideshow.destroy();
  9244.         this.sliderActive = false;
  9245.       }
  9246.  
  9247.       this.slideshow = new theme.Slideshow(this.container, args);
  9248.       this.sliderActive = true;
  9249.     }
  9250.   });
  9251.  
  9252.   return Quotes;
  9253. })();
  9254.  
  9255. theme.SlideshowSection = (function() {
  9256.   var selectors = {
  9257.     sliderMobileContentIndex: '[data-slider-mobile-content-index]'
  9258.   };
  9259.  
  9260.   function SlideshowSection(container) {
  9261.     var sectionId = container.dataset.sectionId;
  9262.  
  9263.     this.container = container;
  9264.     this.eventHandlers = {};
  9265.     this.slideshowDom = container.querySelector('#Slideshow-' + sectionId);
  9266.     this.sliderMobileContentIndex = container.querySelectorAll(
  9267.       selectors.sliderMobileContentIndex
  9268.     );
  9269.  
  9270.     this.slideshow = new theme.Slideshow(container, {
  9271.       autoplay: this.slideshowDom.getAttribute('data-autorotate') === 'true',
  9272.       slideInterval: this.slideshowDom.getAttribute('data-speed')
  9273.     });
  9274.     this._setupEventListeners();
  9275.   }
  9276.  
  9277.   return SlideshowSection;
  9278. })();
  9279.  
  9280. theme.SlideshowSection.prototype = Object.assign(
  9281.   {},
  9282.   theme.SlideshowSection.prototype,
  9283.   {
  9284.     _setupEventListeners: function() {
  9285.       this.eventHandlers.onSliderSlideChanged = function(event) {
  9286.         this._onSliderSlideChanged(event.detail);
  9287.       }.bind(this);
  9288.  
  9289.       this.container.addEventListener(
  9290.         'slider_slide_changed',
  9291.         this.eventHandlers.onSliderSlideChanged
  9292.       );
  9293.     },
  9294.  
  9295.     _onSliderSlideChanged: function(slideIndex) {
  9296.       var activeClass = 'slideshow__text-content--mobile-active';
  9297.  
  9298.       this.sliderMobileContentIndex.forEach(function(element) {
  9299.         if (
  9300.           Number(element.getAttribute('data-slider-mobile-content-index')) ===
  9301.           slideIndex
  9302.         ) {
  9303.           element.classList.add(activeClass);
  9304.         } else {
  9305.           element.classList.remove(activeClass);
  9306.         }
  9307.       });
  9308.     },
  9309.  
  9310.     onUnload: function() {
  9311.       this.slideshow.destroy();
  9312.     },
  9313.  
  9314.     onBlockSelect: function(evt) {
  9315.       if (this.slideshow.adaptHeight) {
  9316.         this.slideshow.setSlideshowHeight();
  9317.       }
  9318.  
  9319.       // Get slide's index using theme editor's id
  9320.       var slide = this.container.querySelector(
  9321.         '.slideshow__slide--' + evt.detail.blockId
  9322.       );
  9323.       var slideIndex = slide.getAttribute('data-slider-slide-index');
  9324.  
  9325.       // Go to selected slide, pause auto-rotate
  9326.       this.slideshow.setSlide(slideIndex);
  9327.       this.slideshow.stopAutoplay();
  9328.     },
  9329.  
  9330.     onBlockDeselect: function() {
  9331.       // Resume auto-rotate
  9332.       this.slideshow.startAutoplay();
  9333.     }
  9334.   }
  9335. );
  9336.  
  9337. window.theme = window.theme || {};
  9338.  
  9339. theme.StoreAvailability = (function() {
  9340.   var selectors = {
  9341.     storeAvailabilityModalOpen: '[data-store-availability-modal-open]',
  9342.     storeAvailabilityModalProductTitle:
  9343.       '[data-store-availability-modal-product-title]',
  9344.     storeAvailabilityModalVariantTitle:
  9345.       '[data-store-availability-modal-variant-title]'
  9346.   };
  9347.  
  9348.   var classes = {
  9349.     hidden: 'hide'
  9350.   };
  9351.  
  9352.   function StoreAvailability(container) {
  9353.     this.container = container;
  9354.     this.productTitle = this.container.dataset.productTitle;
  9355.     this.hasOnlyDefaultVariant =
  9356.       this.container.dataset.hasOnlyDefaultVariant === 'true';
  9357.   }
  9358.  
  9359.   StoreAvailability.prototype = Object.assign({}, StoreAvailability.prototype, {
  9360.     updateContent: function(variantId) {
  9361.       var variantSectionUrl =
  9362.         this.container.dataset.baseUrl +
  9363.         '/variants/' +
  9364.         variantId +
  9365.         '/?section_id=store-availability';
  9366.       var self = this;
  9367.  
  9368.       var storeAvailabilityModalOpen = self.container.querySelector(
  9369.         selectors.storeAvailabilityModalOpen
  9370.       );
  9371.  
  9372.       this.container.style.opacity = 0.5;
  9373.       if (storeAvailabilityModalOpen) {
  9374.         storeAvailabilityModalOpen.disabled = true;
  9375.         storeAvailabilityModalOpen.setAttribute('aria-busy', true);
  9376.       }
  9377.  
  9378.       fetch(variantSectionUrl)
  9379.         .then(function(response) {
  9380.           return response.text();
  9381.         })
  9382.         .then(function(storeAvailabilityHTML) {
  9383.           if (storeAvailabilityHTML.trim() === '') {
  9384.             return;
  9385.           }
  9386.           self.container.innerHTML = storeAvailabilityHTML;
  9387.           self.container.innerHTML = self.container.firstElementChild.innerHTML;
  9388.           self.container.style.opacity = 1;
  9389.  
  9390.           // Need to query this again because we updated the DOM
  9391.           storeAvailabilityModalOpen = self.container.querySelector(
  9392.             selectors.storeAvailabilityModalOpen
  9393.           );
  9394.  
  9395.           if (!storeAvailabilityModalOpen) {
  9396.             return;
  9397.           }
  9398.  
  9399.           storeAvailabilityModalOpen.addEventListener(
  9400.             'click',
  9401.             self._onClickModalOpen.bind(self)
  9402.           );
  9403.  
  9404.           self.modal = self._initModal();
  9405.           self._updateProductTitle();
  9406.           if (self.hasOnlyDefaultVariant) {
  9407.             self._hideVariantTitle();
  9408.           }
  9409.         });
  9410.     },
  9411.  
  9412.     clearContent: function() {
  9413.       this.container.innerHTML = '';
  9414.     },
  9415.  
  9416.     _onClickModalOpen: function() {
  9417.       this.container.dispatchEvent(
  9418.         new CustomEvent('storeAvailabilityModalOpened', {
  9419.           bubbles: true,
  9420.           cancelable: true
  9421.         })
  9422.       );
  9423.     },
  9424.  
  9425.     _initModal: function() {
  9426.       return new window.Modals(
  9427.         'StoreAvailabilityModal',
  9428.         'store-availability-modal',
  9429.         {
  9430.           close: '.js-modal-close-store-availability-modal',
  9431.           closeModalOnClick: true,
  9432.           openClass: 'store-availabilities-modal--active'
  9433.         }
  9434.       );
  9435.     },
  9436.  
  9437.     _updateProductTitle: function() {
  9438.       var storeAvailabilityModalProductTitle = this.container.querySelector(
  9439.         selectors.storeAvailabilityModalProductTitle
  9440.       );
  9441.       storeAvailabilityModalProductTitle.textContent = this.productTitle;
  9442.     },
  9443.  
  9444.     _hideVariantTitle: function() {
  9445.       var storeAvailabilityModalVariantTitle = this.container.querySelector(
  9446.         selectors.storeAvailabilityModalVariantTitle
  9447.       );
  9448.       storeAvailabilityModalVariantTitle.classList.add(classes.hidden);
  9449.     }
  9450.   });
  9451.  
  9452.   return StoreAvailability;
  9453. })();
  9454.  
  9455. theme.VideoSection = (function() {
  9456.   function VideoSection(container) {
  9457.     container.querySelectorAll('.video').forEach(function(el) {
  9458.       theme.Video.init(el);
  9459.       theme.Video.editorLoadVideo(el.id);
  9460.     });
  9461.   }
  9462.  
  9463.   return VideoSection;
  9464. })();
  9465.  
  9466. theme.VideoSection.prototype = Object.assign({}, theme.VideoSection.prototype, {
  9467.   onUnload: function() {
  9468.     theme.Video.removeEvents();
  9469.   }
  9470. });
  9471.  
  9472. theme.heros = {};
  9473.  
  9474. theme.HeroSection = (function() {
  9475.   function HeroSection(container) {
  9476.     var sectionId = container.getAttribute('data-section-id');
  9477.     var hero = '#Hero-' + sectionId;
  9478.     theme.heros[hero] = new theme.Hero(hero, sectionId);
  9479.   }
  9480.  
  9481.   return HeroSection;
  9482. })();
  9483.  
  9484. window.theme = window.theme || {};
  9485.  
  9486. var selectors = {
  9487.   disclosureLocale: '[data-disclosure-locale]',
  9488.   disclosureCurrency: '[data-disclosure-currency]'
  9489. };
  9490.  
  9491. theme.FooterSection = (function() {
  9492.   function Footer(container) {
  9493.     this.container = container;
  9494.     this.cache = {};
  9495.     this.cacheSelectors();
  9496.  
  9497.     if (this.cache.localeDisclosure) {
  9498.       this.localeDisclosure = new theme.Disclosure(this.cache.localeDisclosure);
  9499.     }
  9500.  
  9501.     if (this.cache.currencyDisclosure) {
  9502.       this.currencyDisclosure = new theme.Disclosure(
  9503.         this.cache.currencyDisclosure
  9504.       );
  9505.     }
  9506.   }
  9507.  
  9508.   Footer.prototype = Object.assign({}, Footer.prototype, {
  9509.     cacheSelectors: function() {
  9510.       this.cache = {
  9511.         localeDisclosure: this.container.querySelector(
  9512.           selectors.disclosureLocale
  9513.         ),
  9514.         currencyDisclosure: this.container.querySelector(
  9515.           selectors.disclosureCurrency
  9516.         )
  9517.       };
  9518.     },
  9519.  
  9520.     onUnload: function() {
  9521.       if (this.cache.localeDisclosure) {
  9522.         this.localeDisclosure.destroy();
  9523.       }
  9524.  
  9525.       if (this.cache.currencyDisclosure) {
  9526.         this.currencyDisclosure.destroy();
  9527.       }
  9528.     }
  9529.   });
  9530.  
  9531.   return Footer;
  9532. })();
  9533.  
  9534.  
  9535. document.addEventListener('DOMContentLoaded', function() {
  9536.   var sections = new theme.Sections();
  9537.  
  9538.   sections.register('cart-template', theme.Cart);
  9539.   sections.register('product', theme.Product);
  9540.   sections.register('collection-template', theme.Filters);
  9541.   sections.register('product-template', theme.Product);
  9542.   sections.register('header-section', theme.HeaderSection);
  9543.   sections.register('map', theme.Maps);
  9544.   sections.register('slideshow-section', theme.SlideshowSection);
  9545.   sections.register('store-availability', theme.StoreAvailability);
  9546.   sections.register('video-section', theme.VideoSection);
  9547.   sections.register('quotes', theme.Quotes);
  9548.   sections.register('hero-section', theme.HeroSection);
  9549.   sections.register('product-recommendations', theme.ProductRecommendations);
  9550.   sections.register('footer-section', theme.FooterSection);
  9551.  
  9552.   theme.customerTemplates.init();
  9553.  
  9554.   // Theme-specific selectors to make tables scrollable
  9555.   var tableSelectors = '.rte table,' + '.custom__item-inner--html table';
  9556.  
  9557.   slate.rte.wrapTable({
  9558.     tables: document.querySelectorAll(tableSelectors),
  9559.     tableWrapperClass: 'scrollable-wrapper'
  9560.   });
  9561.  
  9562.   // Theme-specific selectors to make iframes responsive
  9563.   var iframeSelectors =
  9564.     '.rte iframe[src*="youtube.com/embed"],' +
  9565.     '.rte iframe[src*="player.vimeo"],' +
  9566.     '.custom__item-inner--html iframe[src*="youtube.com/embed"],' +
  9567.     '.custom__item-inner--html iframe[src*="player.vimeo"]';
  9568.  
  9569.   slate.rte.wrapIframe({
  9570.     iframes: document.querySelectorAll(iframeSelectors),
  9571.     iframeWrapperClass: 'video-wrapper'
  9572.   });
  9573.  
  9574.   // Common a11y fixes
  9575.   slate.a11y.pageLinkFocus(
  9576.     document.getElementById(window.location.hash.substr(1))
  9577.   );
  9578.  
  9579.   var inPageLink = document.querySelector('.in-page-link');
  9580.   if (inPageLink) {
  9581.     inPageLink.addEventListener('click', function(evt) {
  9582.       slate.a11y.pageLinkFocus(
  9583.         document.getElementById(evt.currentTarget.hash.substr(1))
  9584.       );
  9585.     });
  9586.   }
  9587.  
  9588.   document.querySelectorAll('a[href="#"]').forEach(function(anchor) {
  9589.     anchor.addEventListener('click', function(evt) {
  9590.       evt.preventDefault();
  9591.     });
  9592.   });
  9593.  
  9594.   slate.a11y.accessibleLinks({
  9595.     messages: {
  9596.       newWindow: theme.strings.newWindow,
  9597.       external: theme.strings.external,
  9598.       newWindowExternal: theme.strings.newWindowExternal
  9599.     },
  9600.     links: document.querySelectorAll(
  9601.       'a[href]:not([aria-describedby]), .product-single__thumbnail'
  9602.     )
  9603.   });
  9604.  
  9605.   theme.FormStatus.init();
  9606.  
  9607.   var selectors = {
  9608.     image: '[data-image]',
  9609.     lazyloaded: '.lazyloaded'
  9610.   };
  9611.  
  9612.   document.addEventListener('lazyloaded', function(evt) {
  9613.     var image = evt.target;
  9614.  
  9615.     removeImageLoadingAnimation(image);
  9616.  
  9617.     if (document.body.classList.contains('template-index')) {
  9618.       var mainContent = document.getElementById('MainContent');
  9619.  
  9620.       if (mainContent && mainContent.children && mainContent.children.length) {
  9621.        var firstSection = document.getElementsByClassName('index-section')[0];
  9622.  
  9623.         if (!firstSection.contains(image)) return;
  9624.  
  9625.         window.performance.mark('debut:index:first_image_visible');
  9626.       }
  9627.     }
  9628.  
  9629.     if (image.hasAttribute('data-bgset')) {
  9630.       var innerImage = image.querySelector(selectors.lazyloaded);
  9631.  
  9632.       if (innerImage) {
  9633.         var alt = image.getAttribute('data-alt');
  9634.         var src = innerImage.hasAttribute('data-src')
  9635.           ? innerImage.getAttribute('data-src')
  9636.           : image.getAttribute('data-bg');
  9637.  
  9638.         image.setAttribute('alt', alt ? alt : '');
  9639.         image.setAttribute('src', src ? src : '');
  9640.       }
  9641.     }
  9642.  
  9643.     if (!image.hasAttribute('data-image')) {
  9644.       return;
  9645.     }
  9646.   });
  9647.  
  9648.   // When the theme loads, lazysizes might load images before the "lazyloaded"
  9649.   // event listener has been attached. When this happens, the following function
  9650.   // hides the loading placeholders.
  9651.   function onLoadHideLazysizesAnimation() {
  9652.     var alreadyLazyloaded = document.querySelectorAll('.lazyloaded');
  9653.     alreadyLazyloaded.forEach(function(image) {
  9654.       removeImageLoadingAnimation(image);
  9655.     });
  9656.   }
  9657.  
  9658.   onLoadHideLazysizesAnimation();
  9659.  
  9660.   document.addEventListener(
  9661.     'touchstart',
  9662.     function() {
  9663.       theme.Helpers.setTouch();
  9664.     },
  9665.     { once: true }
  9666.   );
  9667.  
  9668.   if (document.fonts) {
  9669.     document.fonts.ready.then(function() {
  9670.       window.performance.mark('debut:fonts_loaded');
  9671.     });
  9672.   }
  9673. });
  9674.  
  9675. // Youtube API callback
  9676. // eslint-disable-next-line no-unused-vars
  9677. function onYouTubeIframeAPIReady() {
  9678.   theme.Video.loadVideos();
  9679.   theme.ProductVideo.loadVideos(theme.ProductVideo.hosts.youtube);
  9680. }
  9681.  
  9682. function removeImageLoadingAnimation(image) {
  9683.   // Remove loading animation
  9684.   var imageWrapper = image.hasAttribute('data-image-loading-animation')
  9685.     ? image
  9686.     : image.closest('[data-image-loading-animation]');
  9687.  
  9688.   if (imageWrapper) {
  9689.     imageWrapper.removeAttribute('data-image-loading-animation');
  9690.   }
  9691. }
  9692.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement