Skip to content

Instantly share code, notes, and snippets.

@majedur-rahaman
Created May 16, 2020 09:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save majedur-rahaman/90e2ac75c91ef62df6ade4e278925cc3 to your computer and use it in GitHub Desktop.
Save majedur-rahaman/90e2ac75c91ef62df6ade4e278925cc3 to your computer and use it in GitHub Desktop.
Custom js for bigcartel store
window.theme = window.theme || {};
/* ================ SLATE ================ */
window.theme = window.theme || {};
theme.Sections = function Sections() {
this.constructors = {};
this.instances = [];
$(document)
.on('shopify:section:load', this._onSectionLoad.bind(this))
.on('shopify:section:unload', this._onSectionUnload.bind(this))
.on('shopify:section:select', this._onSelect.bind(this))
.on('shopify:section:deselect', this._onDeselect.bind(this))
.on('shopify:block:select', this._onBlockSelect.bind(this))
.on('shopify:block:deselect', this._onBlockDeselect.bind(this));
};
theme.Sections.prototype = _.assignIn({}, theme.Sections.prototype, {
_createInstance: function(container, constructor) {
var $container = $(container);
var id = $container.attr('data-section-id');
var type = $container.attr('data-section-type');
constructor = constructor || this.constructors[type];
if (_.isUndefined(constructor)) {
return;
}
var instance = _.assignIn(new constructor(container), {
id: id,
type: type,
container: container
});
this.instances.push(instance);
},
_onSectionLoad: function(evt) {
var container = $('[data-section-id]', evt.target)[0];
if (container) {
this._createInstance(container);
}
},
_onSectionUnload: function(evt) {
this.instances = _.filter(this.instances, function(instance) {
var isEventInstance = instance.id === evt.detail.sectionId;
if (isEventInstance) {
if (_.isFunction(instance.onUnload)) {
instance.onUnload(evt);
}
}
return !isEventInstance;
});
},
_onSelect: function(evt) {
// eslint-disable-next-line no-shadow
var instance = _.find(this.instances, function(instance) {
return instance.id === evt.detail.sectionId;
});
if (!_.isUndefined(instance) && _.isFunction(instance.onSelect)) {
instance.onSelect(evt);
}
},
_onDeselect: function(evt) {
// eslint-disable-next-line no-shadow
var instance = _.find(this.instances, function(instance) {
return instance.id === evt.detail.sectionId;
});
if (!_.isUndefined(instance) && _.isFunction(instance.onDeselect)) {
instance.onDeselect(evt);
}
},
_onBlockSelect: function(evt) {
// eslint-disable-next-line no-shadow
var instance = _.find(this.instances, function(instance) {
return instance.id === evt.detail.sectionId;
});
if (!_.isUndefined(instance) && _.isFunction(instance.onBlockSelect)) {
instance.onBlockSelect(evt);
}
},
_onBlockDeselect: function(evt) {
// eslint-disable-next-line no-shadow
var instance = _.find(this.instances, function(instance) {
return instance.id === evt.detail.sectionId;
});
if (!_.isUndefined(instance) && _.isFunction(instance.onBlockDeselect)) {
instance.onBlockDeselect(evt);
}
},
register: function(type, constructor) {
this.constructors[type] = constructor;
$('[data-section-type=' + type + ']').each(
function(index, container) {
this._createInstance(container, constructor);
}.bind(this)
);
}
});
window.slate = window.slate || {};
/**
* Slate utilities
* -----------------------------------------------------------------------------
* A collection of useful utilities to help build your theme
*
*
* @namespace utils
*/
slate.utils = {
/**
* Get the query params in a Url
* Ex
* https://mysite.com/search?q=noodles&b
* getParameterByName('q') = "noodles"
* getParameterByName('b') = "" (empty value)
* getParameterByName('test') = null (absent)
*/
getParameterByName: function(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
},
keyboardKeys: {
TAB: 9,
ENTER: 13
}
};
window.slate = window.slate || {};
/**
* iFrames
* -----------------------------------------------------------------------------
* Wrap videos in div to force responsive layout.
*
* @namespace iframes
*/
slate.rte = {
/**
* Wrap tables in a container div to make them scrollable when needed
*
* @param {object} options - Options to be used
* @param {jquery} options.$tables - jquery object(s) of the table(s) to wrap
* @param {string} options.tableWrapperClass - table wrapper class name
*/
wrapTable: function(options) {
options.$tables.wrap(
'<div class="' + options.tableWrapperClass + '"></div>'
);
},
/**
* Wrap iframes in a container div to make them responsive
*
* @param {object} options - Options to be used
* @param {jquery} options.$iframes - jquery object(s) of the iframe(s) to wrap
* @param {string} options.iframeWrapperClass - class name used on the wrapping div
*/
wrapIframe: function(options) {
options.$iframes.each(function() {
// Add wrapper to make video responsive
$(this).wrap('<div class="' + options.iframeWrapperClass + '"></div>');
// Re-set the src attribute on each iframe after page load
// for Chrome's "incorrect iFrame content on 'back'" bug.
// https://code.google.com/p/chromium/issues/detail?id=395791
// Need to specifically target video and admin bar
this.src = this.src;
});
}
};
window.slate = window.slate || {};
/**
* A11y Helpers
* -----------------------------------------------------------------------------
* A collection of useful functions that help make your theme more accessible
* to users with visual impairments.
*
*
* @namespace a11y
*/
slate.a11y = {
/**
* For use when focus shifts to a container rather than a link
* eg for In-page links, after scroll, focus shifts to content area so that
* next `tab` is where user expects if focusing a link, just $link.focus();
*
* @param {JQuery} $element - The element to be acted upon
*/
pageLinkFocus: function($element) {
var focusClass = 'js-focus-hidden';
$element
.first()
.attr('tabIndex', '-1')
.focus()
.addClass(focusClass)
.one('blur', callback);
function callback() {
$element
.first()
.removeClass(focusClass)
.removeAttr('tabindex');
}
},
/**
* If there's a hash in the url, focus the appropriate element
*/
focusHash: function() {
var hash = window.location.hash;
// is there a hash in the url? is it an element on the page?
if (hash && document.getElementById(hash.slice(1))) {
this.pageLinkFocus($(hash));
}
},
/**
* When an in-page (url w/hash) link is clicked, focus the appropriate element
*/
bindInPageLinks: function() {
$('a[href*=#]').on(
'click',
function(evt) {
this.pageLinkFocus($(evt.currentTarget.hash));
}.bind(this)
);
},
/**
* Traps the focus in a particular container
*
* @param {object} options - Options to be used
* @param {jQuery} options.$container - Container to trap focus within
* @param {jQuery} options.$elementToFocus - Element to be focused when focus leaves container
* @param {string} options.namespace - Namespace used for new focus event handler
*/
trapFocus: function(options) {
var eventsName = {
focusin: options.namespace ? 'focusin.' + options.namespace : 'focusin',
focusout: options.namespace
? 'focusout.' + options.namespace
: 'focusout',
keydown: options.namespace
? 'keydown.' + options.namespace
: 'keydown.handleFocus'
};
/**
* Get every possible visible focusable element
*/
var $focusableElements = options.$container.find(
$(
'button, [href], input, select, textarea, [tabindex]:not([tabindex^="-"])'
).filter(':visible')
);
var firstFocusable = $focusableElements[0];
var lastFocusable = $focusableElements[$focusableElements.length - 1];
if (!options.$elementToFocus) {
options.$elementToFocus = options.$container;
}
function _manageFocus(evt) {
if (evt.keyCode !== slate.utils.keyboardKeys.TAB) return;
/**
* On the last focusable element and tab forward,
* focus the first element.
*/
if (evt.target === lastFocusable && !evt.shiftKey) {
evt.preventDefault();
firstFocusable.focus();
}
/**
* On the first focusable element and tab backward,
* focus the last element.
*/
if (evt.target === firstFocusable && evt.shiftKey) {
evt.preventDefault();
lastFocusable.focus();
}
}
options.$container.attr('tabindex', '-1');
options.$elementToFocus.focus();
$(document).off('focusin');
$(document).on(eventsName.focusout, function() {
$(document).off(eventsName.keydown);
});
$(document).on(eventsName.focusin, function(evt) {
if (evt.target !== lastFocusable && evt.target !== firstFocusable) return;
$(document).on(eventsName.keydown, function(evt) {
_manageFocus(evt);
});
});
},
/**
* Removes the trap of focus in a particular container
*
* @param {object} options - Options to be used
* @param {jQuery} options.$container - Container to trap focus within
* @param {string} options.namespace - Namespace used for new focus event handler
*/
removeTrapFocus: function(options) {
var eventName = options.namespace
? 'focusin.' + options.namespace
: 'focusin';
if (options.$container && options.$container.length) {
options.$container.removeAttr('tabindex');
}
$(document).off(eventName);
},
/**
* Add aria-describedby attribute to external and new window links
*
* @param {object} options - Options to be used
* @param {object} options.messages - Custom messages to be used
* @param {jQuery} options.$links - Specific links to be targeted
*/
accessibleLinks: function(options) {
var body = document.querySelector('body');
var idSelectors = {
newWindow: 'a11y-new-window-message',
external: 'a11y-external-message',
newWindowExternal: 'a11y-new-window-external-message'
};
if (options.$links === undefined || !options.$links.jquery) {
options.$links = $('a:not([aria-describedby])');
}
function generateHTML(customMessages) {
if (typeof customMessages !== 'object') {
customMessages = {};
}
var messages = $.extend(
{
newWindow: 'Opens in a new window.',
external: 'Opens external website.',
newWindowExternal: 'Opens external website in a new window.'
},
customMessages
);
var container = document.createElement('ul');
var htmlMessages = '';
for (var message in messages) {
htmlMessages +=
'<li id=' + idSelectors[message] + '>' + messages[message] + '</li>';
}
container.setAttribute('hidden', true);
container.innerHTML = htmlMessages;
body.appendChild(container);
}
function _externalSite($link) {
var hostname = window.location.hostname;
return $link[0].hostname !== hostname;
}
$.each(options.$links, function() {
var $link = $(this);
var target = $link.attr('target');
var rel = $link.attr('rel');
var isExternal = _externalSite($link);
var isTargetBlank = target === '_blank';
if (isExternal) {
$link.attr('aria-describedby', idSelectors.external);
}
if (isTargetBlank) {
if (rel === undefined || rel.indexOf('noopener') === -1) {
$link.attr('rel', 'noopener');
}
$link.attr('aria-describedby', idSelectors.newWindow);
}
if (isExternal && isTargetBlank) {
$link.attr('aria-describedby', idSelectors.newWindowExternal);
}
});
generateHTML(options.messages);
}
};
/**
* Image Helper Functions
* -----------------------------------------------------------------------------
* A collection of functions that help with basic image operations.
*
*/
theme.Images = (function() {
/**
* Preloads an image in memory and uses the browsers cache to store it until needed.
*
* @param {Array} images - A list of image urls
* @param {String} size - A shopify image size attribute
*/
function preload(images, size) {
if (typeof images === 'string') {
images = [images];
}
for (var i = 0; i < images.length; i++) {
var image = images[i];
this.loadImage(this.getSizedImageUrl(image, size));
}
}
/**
* Loads and caches an image in the browsers cache.
* @param {string} path - An image url
*/
function loadImage(path) {
new Image().src = path;
}
/**
* Swaps the src of an image for another OR returns the imageURL to the callback function
* @param image
* @param element
* @param callback
*/
function switchImage(image, element, callback) {
var size = this.imageSize(element.src);
var imageUrl = this.getSizedImageUrl(image.src, size);
if (callback) {
callback(imageUrl, image, element); // eslint-disable-line callback-return
} else {
element.src = imageUrl;
}
}
/**
* +++ Useful
* Find the Shopify image attribute size
*
* @param {string} src
* @returns {null}
*/
function imageSize(src) {
var match = src.match(
/.+_((?:pico|icon|thumb|small|compact|medium|large|grande)|\d{1,4}x\d{0,4}|x\d{1,4})[_\\.@]/
);
if (match !== null) {
if (match[2] !== undefined) {
return match[1] + match[2];
} else {
return match[1];
}
} else {
return null;
}
}
/**
* +++ Useful
* Adds a Shopify size attribute to a URL
*
* @param src
* @param size
* @returns {*}
*/
function getSizedImageUrl(src, size) {
if (size === null) {
return src;
}
if (size === 'master') {
return this.removeProtocol(src);
}
var match = src.match(
/\.(jpg|jpeg|gif|png|bmp|bitmap|tiff|tif)(\?v=\d+)?$/i
);
if (match !== null) {
var prefix = src.split(match[0]);
var suffix = match[0];
return this.removeProtocol(prefix[0] + '_' + size + suffix);
}
return null;
}
function removeProtocol(path) {
return path.replace(/http(s)?:/, '');
}
return {
preload: preload,
loadImage: loadImage,
switchImage: switchImage,
imageSize: imageSize,
getSizedImageUrl: getSizedImageUrl,
removeProtocol: removeProtocol
};
})();
/**
* Currency Helpers
* -----------------------------------------------------------------------------
* A collection of useful functions that help with currency formatting
*
* Current contents
* - formatMoney - Takes an amount in cents and returns it as a formatted dollar value.
*
* Alternatives
* - Accounting.js - http://openexchangerates.github.io/accounting.js/
*
*/
theme.Currency = (function() {
var moneyFormat = '${{amount}}'; // eslint-disable-line camelcase
function formatMoney(cents, format) {
if (typeof cents === 'string') {
cents = cents.replace('.', '');
}
var value = '';
var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
var formatString = format || moneyFormat;
function formatWithDelimiters(number, precision, thousands, decimal) {
thousands = thousands || ',';
decimal = decimal || '.';
if (isNaN(number) || number === null) {
return 0;
}
number = (number / 100.0).toFixed(precision);
var parts = number.split('.');
var dollarsAmount = parts[0].replace(
/(\d)(?=(\d\d\d)+(?!\d))/g,
'$1' + thousands
);
var centsAmount = parts[1] ? decimal + parts[1] : '';
return dollarsAmount + centsAmount;
}
switch (formatString.match(placeholderRegex)[1]) {
case 'amount':
value = formatWithDelimiters(cents, 2);
break;
case 'amount_no_decimals':
value = formatWithDelimiters(cents, 0);
break;
case 'amount_with_comma_separator':
value = formatWithDelimiters(cents, 2, '.', ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = formatWithDelimiters(cents, 0, '.', ',');
break;
case 'amount_no_decimals_with_space_separator':
value = formatWithDelimiters(cents, 0, ' ');
break;
case 'amount_with_apostrophe_separator':
value = formatWithDelimiters(cents, 2, "'");
break;
}
return formatString.replace(placeholderRegex, value);
}
return {
formatMoney: formatMoney
};
})();
/**
* Variant Selection scripts
* ------------------------------------------------------------------------------
*
* Handles change events from the variant inputs in any `cart/add` forms that may
* exist. Also updates the master select and triggers updates when the variants
* price or image changes.
*
* @namespace variants
*/
slate.Variants = (function() {
/**
* Variant constructor
*
* @param {object} options - Settings from `product.js`
*/
function Variants(options) {
this.$container = options.$container;
this.product = options.product;
this.singleOptionSelector = options.singleOptionSelector;
this.originalSelectorId = options.originalSelectorId;
this.enableHistoryState = options.enableHistoryState;
this.currentVariant = this._getVariantFromOptions();
$(this.singleOptionSelector, this.$container).on(
'change',
this._onSelectChange.bind(this)
);
}
Variants.prototype = _.assignIn({}, Variants.prototype, {
/**
* Get the currently selected options from add-to-cart form. Works with all
* form input elements.
*
* @return {array} options - Values of currently selected variants
*/
_getCurrentOptions: function() {
var currentOptions = _.map(
$(this.singleOptionSelector, this.$container),
function(element) {
var $element = $(element);
var type = $element.attr('type');
var currentOption = {};
if (type === 'radio' || type === 'checkbox') {
if ($element[0].checked) {
currentOption.value = $element.val();
currentOption.index = $element.data('index');
return currentOption;
} else {
return false;
}
} else {
currentOption.value = $element.val();
currentOption.index = $element.data('index');
return currentOption;
}
}
);
// remove any unchecked input values if using radio buttons or checkboxes
currentOptions = _.compact(currentOptions);
return currentOptions;
},
/**
* Find variant based on selected values.
*
* @param {array} selectedValues - Values of variant inputs
* @return {object || undefined} found - Variant object from product.variants
*/
_getVariantFromOptions: function() {
var selectedValues = this._getCurrentOptions();
var variants = this.product.variants;
var found = _.find(variants, function(variant) {
return selectedValues.every(function(values) {
return _.isEqual(variant[values.index], values.value);
});
});
return found;
},
/**
* Event handler for when a variant input changes.
*/
_onSelectChange: function() {
var variant = this._getVariantFromOptions();
this.$container.trigger({
type: 'variantChange',
variant: variant
});
if (!variant) {
return;
}
this._updateMasterSelect(variant);
this._updateImages(variant);
this._updatePrice(variant);
this._updateSKU(variant);
this.currentVariant = variant;
if (this.enableHistoryState) {
this._updateHistoryState(variant);
}
},
/**
* Trigger event when variant image changes
*
* @param {object} variant - Currently selected variant
* @return {event} variantImageChange
*/
_updateImages: function(variant) {
var variantImage = variant.featured_image || {};
var currentVariantImage = this.currentVariant.featured_image || {};
if (
!variant.featured_image ||
variantImage.src === currentVariantImage.src
) {
return;
}
this.$container.trigger({
type: 'variantImageChange',
variant: variant
});
},
/**
* Trigger event when variant price changes.
*
* @param {object} variant - Currently selected variant
* @return {event} variantPriceChange
*/
_updatePrice: function(variant) {
if (
variant.price === this.currentVariant.price &&
variant.compare_at_price === this.currentVariant.compare_at_price
) {
return;
}
this.$container.trigger({
type: 'variantPriceChange',
variant: variant
});
},
/**
* Trigger event when variant sku changes.
*
* @param {object} variant - Currently selected variant
* @return {event} variantSKUChange
*/
_updateSKU: function(variant) {
if (variant.sku === this.currentVariant.sku) {
return;
}
this.$container.trigger({
type: 'variantSKUChange',
variant: variant
});
},
/**
* Update history state for product deeplinking
*
* @param {variant} variant - Currently selected variant
* @return {k} [description]
*/
_updateHistoryState: function(variant) {
if (!history.replaceState || !variant) {
return;
}
var newurl =
window.location.protocol +
'//' +
window.location.host +
window.location.pathname +
'?variant=' +
variant.id;
window.history.replaceState({ path: newurl }, '', newurl);
},
/**
* Update hidden master select of variant change
*
* @param {variant} variant - Currently selected variant
*/
_updateMasterSelect: function(variant) {
$(this.originalSelectorId, this.$container).val(variant.id);
}
});
return Variants;
})();
/* ================ GLOBAL ================ */
/*============================================================================
Drawer modules
==============================================================================*/
theme.Drawers = (function() {
function Drawer(id, position, options) {
var defaults = {
close: '.js-drawer-close',
open: '.js-drawer-open-' + position,
openClass: 'js-drawer-open',
dirOpenClass: 'js-drawer-open-' + position
};
this.nodes = {
$parent: $('html').add('body'),
$page: $('#PageContainer')
};
this.config = $.extend(defaults, options);
this.position = position;
this.$drawer = $('#' + id);
if (!this.$drawer.length) {
return false;
}
this.drawerIsOpen = false;
this.init();
}
Drawer.prototype.init = function() {
$(this.config.open).on('click', $.proxy(this.open, this));
this.$drawer.on('click', this.config.close, $.proxy(this.close, this));
};
Drawer.prototype.open = function(evt) {
// Keep track if drawer was opened from a click, or called by another function
var externalCall = false;
// Prevent following href if link is clicked
if (evt) {
evt.preventDefault();
} else {
externalCall = true;
}
// Without this, the drawer opens, the click event bubbles up to nodes.$page
// which closes the drawer.
if (evt && evt.stopPropagation) {
evt.stopPropagation();
// save the source of the click, we'll focus to this on close
this.$activeSource = $(evt.currentTarget);
}
if (this.drawerIsOpen && !externalCall) {
return this.close();
}
// Add is-transitioning class to moved elements on open so drawer can have
// transition for close animation
this.$drawer.prepareTransition();
this.nodes.$parent.addClass(
this.config.openClass + ' ' + this.config.dirOpenClass
);
this.drawerIsOpen = true;
// Set focus on drawer
slate.a11y.trapFocus({
$container: this.$drawer,
namespace: 'drawer_focus'
});
// Run function when draw opens if set
if (
this.config.onDrawerOpen &&
typeof this.config.onDrawerOpen === 'function'
) {
if (!externalCall) {
this.config.onDrawerOpen();
}
}
if (this.$activeSource && this.$activeSource.attr('aria-expanded')) {
this.$activeSource.attr('aria-expanded', 'true');
}
this.bindEvents();
return this;
};
Drawer.prototype.close = function() {
if (!this.drawerIsOpen) {
// don't close a closed drawer
return;
}
// deselect any focused form elements
$(document.activeElement).trigger('blur');
// Ensure closing transition is applied to moved elements, like the nav
this.$drawer.prepareTransition();
this.nodes.$parent.removeClass(
this.config.dirOpenClass + ' ' + this.config.openClass
);
if (this.$activeSource && this.$activeSource.attr('aria-expanded')) {
this.$activeSource.attr('aria-expanded', 'false');
}
this.drawerIsOpen = false;
// Remove focus on drawer
slate.a11y.removeTrapFocus({
$container: this.$drawer,
namespace: 'drawer_focus'
});
this.unbindEvents();
// Run function when draw closes if set
if (
this.config.onDrawerClose &&
typeof this.config.onDrawerClose === 'function'
) {
this.config.onDrawerClose();
}
};
Drawer.prototype.bindEvents = function() {
this.nodes.$parent.on(
'keyup.drawer',
$.proxy(function(evt) {
// close on 'esc' keypress
if (evt.keyCode === 27) {
this.close();
return false;
} else {
return true;
}
}, this)
);
// Lock scrolling on mobile
this.nodes.$page.on('touchmove.drawer', function() {
return false;
});
this.nodes.$page.on(
'click.drawer',
$.proxy(function() {
this.close();
return false;
}, this)
);
};
Drawer.prototype.unbindEvents = function() {
this.nodes.$page.off('.drawer');
this.nodes.$parent.off('.drawer');
};
return Drawer;
})();
/* ================ MODULES ================ */
window.theme = window.theme || {};
theme.Header = (function() {
var selectors = {
body: 'body',
navigation: '#AccessibleNav',
siteNavHasDropdown: '.site-nav--has-dropdown',
siteNavChildLinks: '.site-nav__child-link',
siteNavActiveDropdown: '.site-nav--active-dropdown',
siteNavLinkMain: '.site-nav__link--main',
siteNavChildLink: '.site-nav__link--last'
};
var config = {
activeClass: 'site-nav--active-dropdown',
childLinkClass: 'site-nav__child-link'
};
var cache = {};
function init() {
cacheSelectors();
cache.$parents.on('click.siteNav', function() {
var $el = $(this);
$el.hasClass(config.activeClass) ? hideDropdown($el) : showDropdown($el);
});
// check when we're leaving a dropdown and close the active dropdown
$(selectors.siteNavChildLink).on('focusout.siteNav', function() {
setTimeout(function() {
if (
$(document.activeElement).hasClass(config.childLinkClass) ||
!cache.$activeDropdown.length
) {
return;
}
hideDropdown(cache.$activeDropdown);
});
});
// close dropdowns when on top level nav
cache.$topLevel.on('focus.siteNav', function() {
if (cache.$activeDropdown.length) {
hideDropdown(cache.$activeDropdown);
}
});
cache.$subMenuLinks.on('click.siteNav', function(evt) {
// Prevent click on body from firing instead of link
evt.stopImmediatePropagation();
});
}
function cacheSelectors() {
cache = {
$nav: $(selectors.navigation),
$topLevel: $(selectors.siteNavLinkMain),
$parents: $(selectors.navigation).find(selectors.siteNavHasDropdown),
$subMenuLinks: $(selectors.siteNavChildLinks),
$activeDropdown: $(selectors.siteNavActiveDropdown)
};
}
function showDropdown($el) {
$el.addClass(config.activeClass);
// close open dropdowns
if (cache.$activeDropdown.length) {
hideDropdown(cache.$activeDropdown);
}
cache.$activeDropdown = $el;
// set expanded on open dropdown
$el.find(selectors.siteNavLinkMain).attr('aria-expanded', 'true');
setTimeout(function() {
$(window).on('keyup.siteNav', function(evt) {
if (evt.keyCode === 27) {
hideDropdown($el);
}
});
$(selectors.body).on('click.siteNav', function() {
hideDropdown($el);
});
}, 250);
}
function hideDropdown($el) {
// remove aria on open dropdown
$el.find(selectors.siteNavLinkMain).attr('aria-expanded', 'false');
$el.removeClass(config.activeClass);
// reset active dropdown
cache.$activeDropdown = $(selectors.siteNavActiveDropdown);
$(selectors.body).off('click.siteNav');
$(window).off('keyup.siteNav');
}
function unload() {
$(window).off('.siteNav');
cache.$parents.off('.siteNav');
cache.$subMenuLinks.off('.siteNav');
cache.$topLevel.off('.siteNav');
$(selectors.siteNavChildLink).off('.siteNav');
$(selectors.body).off('.siteNav');
}
return {
init: init,
unload: unload
};
})();
window.theme = window.theme || {};
theme.MobileNav = (function() {
var classes = {
mobileNavOpenIcon: 'mobile-nav--open',
mobileNavCloseIcon: 'mobile-nav--close',
navLinkWrapper: 'mobile-nav__item',
navLink: 'mobile-nav__link',
subNavLink: 'mobile-nav__sublist-link',
return: 'mobile-nav__return-btn',
subNavActive: 'is-active',
subNavClosing: 'is-closing',
navOpen: 'js-menu--is-open',
subNavShowing: 'sub-nav--is-open',
thirdNavShowing: 'third-nav--is-open',
subNavToggleBtn: 'js-toggle-submenu'
};
var cache = {};
var isTransitioning;
var $activeSubNav;
var $activeTrigger;
var menuLevel = 1;
// Breakpoints from src/stylesheets/global/variables.scss.liquid
var mediaQuerySmall = 'screen and (max-width: 749px)';
function init() {
cacheSelectors();
cache.$mobileNavToggle.on('click', toggleMobileNav);
cache.$subNavToggleBtn.on('click.subNav', toggleSubNav);
// Close mobile nav when unmatching mobile breakpoint
enquire.register(mediaQuerySmall, {
unmatch: function() {
closeMobileNav();
}
});
}
function toggleMobileNav() {
if (cache.$mobileNavToggle.hasClass(classes.mobileNavCloseIcon)) {
closeMobileNav();
} else {
openMobileNav();
}
}
function cacheSelectors() {
cache = {
$pageContainer: $('#PageContainer'),
$siteHeader: $('.site-header'),
$mobileNavToggle: $('.js-mobile-nav-toggle'),
$mobileNavContainer: $('.mobile-nav-wrapper'),
$mobileNav: $('#MobileNav'),
$sectionHeader: $('#shopify-section-header'),
$subNavToggleBtn: $('.' + classes.subNavToggleBtn)
};
}
function openMobileNav() {
var translateHeaderHeight = cache.$siteHeader.outerHeight();
cache.$mobileNavContainer.prepareTransition().addClass(classes.navOpen);
cache.$mobileNavContainer.css({
transform: 'translateY(' + translateHeaderHeight + 'px)'
});
cache.$pageContainer.css({
transform:
'translate3d(0, ' + cache.$mobileNavContainer[0].scrollHeight + 'px, 0)'
});
slate.a11y.trapFocus({
$container: cache.$sectionHeader,
$elementToFocus: cache.$mobileNavToggle,
namespace: 'navFocus'
});
cache.$mobileNavToggle
.addClass(classes.mobileNavCloseIcon)
.removeClass(classes.mobileNavOpenIcon)
.attr('aria-expanded', true);
// close on escape
$(window).on('keyup.mobileNav', function(evt) {
if (evt.which === 27) {
closeMobileNav();
}
});
}
function closeMobileNav() {
cache.$mobileNavContainer.prepareTransition().removeClass(classes.navOpen);
cache.$mobileNavContainer.css({
transform: 'translateY(-100%)'
});
cache.$pageContainer.removeAttr('style');
slate.a11y.trapFocus({
$container: $('html'),
$elementToFocus: $('body')
});
cache.$mobileNavContainer.one(
'TransitionEnd.navToggle webkitTransitionEnd.navToggle transitionend.navToggle oTransitionEnd.navToggle',
function() {
slate.a11y.removeTrapFocus({
$container: cache.$mobileNav,
namespace: 'navFocus'
});
}
);
cache.$mobileNavToggle
.addClass(classes.mobileNavOpenIcon)
.removeClass(classes.mobileNavCloseIcon)
.attr('aria-expanded', false)
.focus();
$(window).off('keyup.mobileNav');
scrollTo(0, 0);
}
function toggleSubNav(evt) {
if (isTransitioning) {
return;
}
var $toggleBtn = $(evt.currentTarget);
var isReturn = $toggleBtn.hasClass(classes.return);
isTransitioning = true;
if (isReturn) {
// Close all subnavs by removing active class on buttons
$(
'.' + classes.subNavToggleBtn + '[data-level="' + (menuLevel - 1) + '"]'
).removeClass(classes.subNavActive);
if ($activeTrigger && $activeTrigger.length) {
$activeTrigger.removeClass(classes.subNavActive);
}
} else {
$toggleBtn.addClass(classes.subNavActive);
}
$activeTrigger = $toggleBtn;
goToSubnav($toggleBtn.data('target'));
}
function goToSubnav(target) {
/*eslint-disable shopify/jquery-dollar-sign-reference */
var $targetMenu = target
? $('.mobile-nav__dropdown[data-parent="' + target + '"]')
: cache.$mobileNav;
menuLevel = $targetMenu.data('level') ? $targetMenu.data('level') : 1;
if ($activeSubNav && $activeSubNav.length) {
$activeSubNav.prepareTransition().addClass(classes.subNavClosing);
}
$activeSubNav = $targetMenu;
/*eslint-enable shopify/jquery-dollar-sign-reference */
var translateMenuHeight = $targetMenu.outerHeight();
var openNavClass =
menuLevel > 2 ? classes.thirdNavShowing : classes.subNavShowing;
cache.$mobileNavContainer
.css('height', translateMenuHeight)
.removeClass(classes.thirdNavShowing)
.addClass(openNavClass);
if (!target) {
// Show top level nav
cache.$mobileNavContainer
.removeClass(classes.thirdNavShowing)
.removeClass(classes.subNavShowing);
}
/* if going back to first subnav, focus is on whole header */
var $container = menuLevel === 1 ? cache.$sectionHeader : $targetMenu;
var $menuTitle = $targetMenu.find('[data-menu-title=' + menuLevel + ']');
var $elementToFocus = $menuTitle ? $menuTitle : $targetMenu;
// Focusing an item in the subnav early forces element into view and breaks the animation.
cache.$mobileNavContainer.one(
'TransitionEnd.subnavToggle webkitTransitionEnd.subnavToggle transitionend.subnavToggle oTransitionEnd.subnavToggle',
function() {
slate.a11y.trapFocus({
$container: $container,
$elementToFocus: $elementToFocus,
namespace: 'subNavFocus'
});
cache.$mobileNavContainer.off('.subnavToggle');
isTransitioning = false;
}
);
// Match height of subnav
cache.$pageContainer.css({
transform: 'translateY(' + translateMenuHeight + 'px)'
});
$activeSubNav.removeClass(classes.subNavClosing);
}
return {
init: init,
closeMobileNav: closeMobileNav
};
})(jQuery);
window.theme = window.theme || {};
theme.Search = (function() {
var selectors = {
search: '.search',
searchSubmit: '.search__submit',
searchInput: '.search__input',
siteHeader: '.site-header',
siteHeaderSearchToggle: '.site-header__search-toggle',
siteHeaderSearch: '.site-header__search',
searchDrawer: '.search-bar',
searchDrawerInput: '.search-bar__input',
searchHeader: '.search-header',
searchHeaderInput: '.search-header__input',
searchHeaderSubmit: '.search-header__submit',
searchResultSubmit: '#SearchResultSubmit',
searchResultInput: '#SearchInput',
searchResultMessage: '[data-search-error-message]',
mobileNavWrapper: '.mobile-nav-wrapper'
};
var classes = {
focus: 'search--focus',
hidden: 'hide',
mobileNavIsOpen: 'js-menu--is-open'
};
function init() {
if (!$(selectors.siteHeader).length) {
return;
}
this.$searchResultInput = $(selectors.searchResultInput);
this.$searchErrorMessage = $(selectors.searchResultMessage);
initDrawer();
var searchQuery = slate.utils.getParameterByName('q');
if (searchQuery !== null) {
validateSearchResultForm.call(this);
}
$(selectors.searchResultSubmit).on(
'click',
validateSearchResultForm.bind(this)
);
$(selectors.searchHeaderInput)
.add(selectors.searchHeaderSubmit)
.on('focus blur', function() {
$(selectors.searchHeader).toggleClass(classes.focus);
});
$(selectors.siteHeaderSearchToggle).on('click', function() {
var searchHeight = $(selectors.siteHeader).outerHeight();
var searchOffset = $(selectors.siteHeader).offset().top - searchHeight;
$(selectors.searchDrawer).css({
height: searchHeight + 'px',
top: searchOffset + 'px'
});
});
}
function initDrawer() {
// Add required classes to HTML
$('#PageContainer').addClass('drawer-page-content');
$('.js-drawer-open-top')
.attr('aria-controls', 'SearchDrawer')
.attr('aria-expanded', 'false')
.attr('aria-haspopup', 'dialog');
theme.SearchDrawer = new theme.Drawers('SearchDrawer', 'top', {
onDrawerOpen: searchDrawerFocus,
onDrawerClose: searchDrawerFocusClose
});
}
function searchDrawerFocus() {
searchFocus($(selectors.searchDrawerInput));
if ($(selectors.mobileNavWrapper).hasClass(classes.mobileNavIsOpen)) {
theme.MobileNav.closeMobileNav();
}
}
function searchFocus($el) {
$el.focus();
// set selection range hack for iOS
$el[0].setSelectionRange(0, $el[0].value.length);
}
function searchDrawerFocusClose() {
$(selectors.siteHeaderSearchToggle).focus();
}
/**
* Remove the aria-attributes and hide the error messages
*/
function hideErrorMessage() {
this.$searchErrorMessage.addClass(classes.hidden);
this.$searchResultInput
.removeAttr('aria-describedby')
.removeAttr('aria-invalid');
}
/**
* Add the aria-attributes and show the error messages
*/
function showErrorMessage() {
this.$searchErrorMessage.removeClass(classes.hidden);
this.$searchResultInput
.attr('aria-describedby', 'error-search-form')
.attr('aria-invalid', true);
}
function validateSearchResultForm(evt) {
var isInputValueEmpty = this.$searchResultInput.val().trim().length === 0;
if (!isInputValueEmpty) {
hideErrorMessage.call(this);
return;
}
if (typeof evt !== 'undefined') {
evt.preventDefault();
}
searchFocus(this.$searchResultInput);
showErrorMessage.call(this);
}
return {
init: init
};
})();
(function() {
var selectors = {
backButton: '.return-link'
};
var $backButton = $(selectors.backButton);
if (!document.referrer || !$backButton.length || !window.history.length) {
return;
}
$backButton.one('click', function(evt) {
evt.preventDefault();
var referrerDomain = urlDomain(document.referrer);
var shopDomain = urlDomain(window.location.href);
if (shopDomain === referrerDomain) {
history.back();
}
return false;
});
function urlDomain(url) {
var anchor = document.createElement('a');
anchor.ref = url;
return anchor.hostname;
}
})();
theme.Slideshow = (function() {
this.$slideshow = null;
var classes = {
wrapper: 'slideshow-wrapper',
slideshow: 'slideshow',
currentSlide: 'slick-current',
video: 'slideshow__video',
videoBackground: 'slideshow__video--background',
closeVideoBtn: 'slideshow__video-control--close',
pauseButton: 'slideshow__pause',
isPaused: 'is-paused'
};
function slideshow(el) {
this.$slideshow = $(el);
this.$wrapper = this.$slideshow.closest('.' + classes.wrapper);
this.$pause = this.$wrapper.find('.' + classes.pauseButton);
this.settings = {
accessibility: true,
arrows: false,
dots: true,
fade: true,
draggable: true,
touchThreshold: 20,
autoplay: this.$slideshow.data('autoplay'),
autoplaySpeed: this.$slideshow.data('speed')
};
this.$slideshow.on('beforeChange', beforeChange.bind(this));
this.$slideshow.on('init', slideshowA11y.bind(this));
this.$slideshow.slick(this.settings);
this.$pause.on('click', this.togglePause.bind(this));
}
function slideshowA11y(event, obj) {
var $slider = obj.$slider;
var $list = obj.$list;
var $wrapper = this.$wrapper;
var autoplay = this.settings.autoplay;
// Remove default Slick aria-live attr until slider is focused
$list.removeAttr('aria-live');
// When an element in the slider is focused
// pause slideshow and set aria-live.
$wrapper.on('focusin', function(evt) {
if (!$wrapper.has(evt.target).length) {
return;
}
$list.attr('aria-live', 'polite');
if (autoplay) {
$slider.slick('slickPause');
}
});
// Resume autoplay
$wrapper.on('focusout', function(evt) {
if (!$wrapper.has(evt.target).length) {
return;
}
$list.removeAttr('aria-live');
if (autoplay) {
// Manual check if the focused element was the video close button
// to ensure autoplay does not resume when focus goes inside YouTube iframe
if ($(evt.target).hasClass(classes.closeVideoBtn)) {
return;
}
$slider.slick('slickPlay');
}
});
// Add arrow key support when focused
if (obj.$dots) {
obj.$dots.on('keydown', function(evt) {
if (evt.which === 37) {
$slider.slick('slickPrev');
}
if (evt.which === 39) {
$slider.slick('slickNext');
}
// Update focus on newly selected tab
if (evt.which === 37 || evt.which === 39) {
obj.$dots.find('.slick-active button').focus();
}
});
}
}
function beforeChange(event, slick, currentSlide, nextSlide) {
var $slider = slick.$slider;
var $currentSlide = $slider.find('.' + classes.currentSlide);
var $nextSlide = $slider.find(
'.slideshow__slide[data-slick-index="' + nextSlide + '"]'
);
if (isVideoInSlide($currentSlide)) {
var $currentVideo = $currentSlide.find('.' + classes.video);
var currentVideoId = $currentVideo.attr('id');
theme.SlideshowVideo.pauseVideo(currentVideoId);
$currentVideo.attr('tabindex', '-1');
}
if (isVideoInSlide($nextSlide)) {
var $video = $nextSlide.find('.' + classes.video);
var videoId = $video.attr('id');
var isBackground = $video.hasClass(classes.videoBackground);
if (isBackground) {
theme.SlideshowVideo.playVideo(videoId);
} else {
$video.attr('tabindex', '0');
}
}
}
function isVideoInSlide($slide) {
return $slide.find('.' + classes.video).length;
}
slideshow.prototype.togglePause = function() {
var slideshowSelector = getSlideshowId(this.$pause);
if (this.$pause.hasClass(classes.isPaused)) {
this.$pause.removeClass(classes.isPaused);
$(slideshowSelector).slick('slickPlay');
} else {
this.$pause.addClass(classes.isPaused);
$(slideshowSelector).slick('slickPause');
}
};
function getSlideshowId($el) {
return '#Slideshow-' + $el.data('id');
}
return slideshow;
})();
// Youtube API callback
// eslint-disable-next-line no-unused-vars
function onYouTubeIframeAPIReady() {
theme.SlideshowVideo.loadVideos();
}
theme.SlideshowVideo = (function() {
var autoplayCheckComplete = false;
var autoplayAvailable = false;
var playOnClickChecked = false;
var playOnClick = false;
var youtubeLoaded = false;
var videos = {};
var videoPlayers = [];
var videoOptions = {
ratio: 16 / 9,
playerVars: {
// eslint-disable-next-line camelcase
iv_load_policy: 3,
modestbranding: 1,
autoplay: 0,
controls: 0,
showinfo: 0,
wmode: 'opaque',
branding: 0,
autohide: 0,
rel: 0
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerChange
}
};
var classes = {
playing: 'video-is-playing',
paused: 'video-is-paused',
loading: 'video-is-loading',
loaded: 'video-is-loaded',
slideshowWrapper: 'slideshow-wrapper',
slide: 'slideshow__slide',
slideBackgroundVideo: 'slideshow__slide--background-video',
slideDots: 'slick-dots',
videoChrome: 'slideshow__video--chrome',
videoBackground: 'slideshow__video--background',
playVideoBtn: 'slideshow__video-control--play',
closeVideoBtn: 'slideshow__video-control--close',
currentSlide: 'slick-current',
slickClone: 'slick-cloned',
supportsAutoplay: 'autoplay',
supportsNoAutoplay: 'no-autoplay'
};
/**
* Public functions
*/
function init($video) {
if (!$video.length) {
return;
}
videos[$video.attr('id')] = {
id: $video.attr('id'),
videoId: $video.data('id'),
type: $video.data('type'),
status: $video.data('type') === 'chrome' ? 'closed' : 'background', // closed, open, background
videoSelector: $video.attr('id'),
$parentSlide: $video.closest('.' + classes.slide),
$parentSlideshowWrapper: $video.closest('.' + classes.slideshowWrapper),
controls: $video.data('type') === 'background' ? 0 : 1,
slideshow: $video.data('slideshow')
};
if (!youtubeLoaded) {
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
}
function customPlayVideo(playerId) {
// Do not autoplay just because the slideshow asked you to.
// If the slideshow asks to play a video, make sure
// we have done the playOnClick check first
if (!playOnClickChecked && !playOnClick) {
return;
}
if (playerId && typeof videoPlayers[playerId].playVideo === 'function') {
privatePlayVideo(playerId);
}
}
function pauseVideo(playerId) {
if (
videoPlayers[playerId] &&
typeof videoPlayers[playerId].pauseVideo === 'function'
) {
videoPlayers[playerId].pauseVideo();
}
}
function loadVideos() {
for (var key in videos) {
if (videos.hasOwnProperty(key)) {
var args = $.extend({}, videoOptions, videos[key]);
args.playerVars.controls = args.controls;
videoPlayers[key] = new YT.Player(key, args);
}
}
initEvents();
youtubeLoaded = true;
}
function loadVideo(key) {
if (!youtubeLoaded) {
return;
}
var args = $.extend({}, videoOptions, videos[key]);
args.playerVars.controls = args.controls;
videoPlayers[key] = new YT.Player(key, args);
initEvents();
}
/**
* Private functions
*/
function privatePlayVideo(id, clicked) {
var videoData = videos[id];
var player = videoPlayers[id];
var $slide = videos[id].$parentSlide;
if (playOnClick) {
// playOnClick means we are probably on mobile (no autoplay).
// setAsPlaying will show the iframe, requiring another click
// to play the video.
setAsPlaying(videoData);
} else if (clicked || (autoplayCheckComplete && autoplayAvailable)) {
// Play if autoplay is available or clicked to play
$slide.removeClass(classes.loading);
setAsPlaying(videoData);
player.playVideo();
return;
}
// Check for autoplay if not already done
if (!autoplayCheckComplete) {
autoplayCheckFunction(player, $slide);
}
}
function setAutoplaySupport(supported) {
var supportClass = supported
? classes.supportsAutoplay
: classes.supportsNoAutoplay;
$(document.documentElement).addClass(supportClass);
if (!supported) {
playOnClick = true;
}
autoplayCheckComplete = true;
}
function autoplayCheckFunction(player, $slide) {
// attempt to play video
player.playVideo();
autoplayTest(player)
.then(function() {
setAutoplaySupport(true);
})
.fail(function() {
// No autoplay available (or took too long to start playing).
// Show fallback image. Stop video for safety.
setAutoplaySupport(false);
player.stopVideo();
})
.always(function() {
autoplayCheckComplete = true;
$slide.removeClass(classes.loading);
});
}
function autoplayTest(player) {
var deferred = $.Deferred();
var wait;
var timeout;
wait = setInterval(function() {
if (player.getCurrentTime() <= 0) {
return;
}
autoplayAvailable = true;
clearInterval(wait);
clearTimeout(timeout);
deferred.resolve();
}, 500);
timeout = setTimeout(function() {
clearInterval(wait);
deferred.reject();
}, 4000); // subjective. test up to 8 times over 4 seconds
return deferred;
}
function playOnClickCheck() {
// Bail early for a few instances:
// - small screen
// - device sniff mobile browser
if (playOnClickChecked) {
return;
}
if ($(window).width() < 750) {
playOnClick = true;
} else if (window.mobileCheck()) {
playOnClick = true;
}
if (playOnClick) {
// No need to also do the autoplay check
setAutoplaySupport(false);
}
playOnClickChecked = true;
}
// The API will call this function when each video player is ready
function onPlayerReady(evt) {
evt.target.setPlaybackQuality('hd1080');
var videoData = getVideoOptions(evt);
playOnClickCheck();
// Prevent tabbing through YouTube player controls until visible
$('#' + videoData.id).attr('tabindex', '-1');
sizeBackgroundVideos();
// Customize based on options from the video ID
switch (videoData.type) {
case 'background-chrome':
case 'background':
evt.target.mute();
// Only play the video if it is in the active slide
if (videoData.$parentSlide.hasClass(classes.currentSlide)) {
privatePlayVideo(videoData.id);
}
break;
}
videoData.$parentSlide.addClass(classes.loaded);
}
function onPlayerChange(evt) {
var videoData = getVideoOptions(evt);
switch (evt.data) {
case 0: // ended
setAsFinished(videoData);
break;
case 1: // playing
setAsPlaying(videoData);
break;
case 2: // paused
setAsPaused(videoData);
break;
}
}
function setAsFinished(videoData) {
switch (videoData.type) {
case 'background':
videoPlayers[videoData.id].seekTo(0);
break;
case 'background-chrome':
videoPlayers[videoData.id].seekTo(0);
closeVideo(videoData.id);
break;
case 'chrome':
closeVideo(videoData.id);
break;
}
}
function setAsPlaying(videoData) {
var $slideshow = videoData.$parentSlideshowWrapper;
var $slide = videoData.$parentSlide;
$slide.removeClass(classes.loading);
// Do not change element visibility if it is a background video
if (videoData.status === 'background') {
return;
}
$('#' + videoData.id).attr('tabindex', '0');
switch (videoData.type) {
case 'chrome':
case 'background-chrome':
$slideshow.removeClass(classes.paused).addClass(classes.playing);
$slide.removeClass(classes.paused).addClass(classes.playing);
break;
}
// Update focus to the close button so we stay within the slide
$slide.find('.' + classes.closeVideoBtn).focus();
}
function setAsPaused(videoData) {
var $slideshow = videoData.$parentSlideshowWrapper;
var $slide = videoData.$parentSlide;
if (videoData.type === 'background-chrome') {
closeVideo(videoData.id);
return;
}
// YT's events fire after our click event. This status flag ensures
// we don't interact with a closed or background video.
if (videoData.status !== 'closed' && videoData.type !== 'background') {
$slideshow.addClass(classes.paused);
$slide.addClass(classes.paused);
}
if (videoData.type === 'chrome' && videoData.status === 'closed') {
$slideshow.removeClass(classes.paused);
$slide.removeClass(classes.paused);
}
$slideshow.removeClass(classes.playing);
$slide.removeClass(classes.playing);
}
function closeVideo(playerId) {
var videoData = videos[playerId];
var $slideshow = videoData.$parentSlideshowWrapper;
var $slide = videoData.$parentSlide;
var classesToRemove = [classes.pause, classes.playing].join(' ');
$('#' + videoData.id).attr('tabindex', '-1');
videoData.status = 'closed';
switch (videoData.type) {
case 'background-chrome':
videoPlayers[playerId].mute();
setBackgroundVideo(playerId);
break;
case 'chrome':
videoPlayers[playerId].stopVideo();
setAsPaused(videoData); // in case the video is already paused
break;
}
$slideshow.removeClass(classesToRemove);
$slide.removeClass(classesToRemove);
}
function getVideoOptions(evt) {
return videos[evt.target.h.id];
}
function startVideoOnClick(playerId) {
var videoData = videos[playerId];
// add loading class to slide
videoData.$parentSlide.addClass(classes.loading);
videoData.status = 'open';
switch (videoData.type) {
case 'background-chrome':
unsetBackgroundVideo(playerId, videoData);
videoPlayers[playerId].unMute();
privatePlayVideo(playerId, true);
break;
case 'chrome':
privatePlayVideo(playerId, true);
break;
}
// esc to close video player
$(document).on('keydown.videoPlayer', function(evt) {
if (evt.keyCode === 27) {
closeVideo(playerId);
}
});
}
function sizeBackgroundVideos() {
$('.' + classes.videoBackground).each(function(index, el) {
sizeBackgroundVideo($(el));
});
}
function sizeBackgroundVideo($player) {
var $slide = $player.closest('.' + classes.slide);
// Ignore cloned slides
if ($slide.hasClass(classes.slickClone)) {
return;
}
var slideWidth = $slide.width();
var playerWidth = $player.width();
var playerHeight = $player.height();
// when screen aspect ratio differs from video, video must center and underlay one dimension
if (slideWidth / videoOptions.ratio < playerHeight) {
playerWidth = Math.ceil(playerHeight * videoOptions.ratio); // get new player width
$player
.width(playerWidth)
.height(playerHeight)
.css({
left: (slideWidth - playerWidth) / 2,
top: 0
}); // player width is greater, offset left; reset top
} else {
// new video width < window width (gap to right)
playerHeight = Math.ceil(slideWidth / videoOptions.ratio); // get new player height
$player
.width(slideWidth)
.height(playerHeight)
.css({
left: 0,
top: (playerHeight - playerHeight) / 2
}); // player height is greater, offset top; reset left
}
$player.prepareTransition().addClass(classes.loaded);
}
function unsetBackgroundVideo(playerId) {
// Switch the background-chrome to a chrome-only player once played
$('#' + playerId)
.removeAttr('style')
.removeClass(classes.videoBackground)
.addClass(classes.videoChrome);
videos[playerId].$parentSlideshowWrapper
.removeClass(classes.slideBackgroundVideo)
.addClass(classes.playing);
videos[playerId].$parentSlide
.removeClass(classes.slideBackgroundVideo)
.addClass(classes.playing);
videos[playerId].status = 'open';
}
function setBackgroundVideo(playerId) {
// Switch back to background-chrome when closed
var $player = $('#' + playerId)
.addClass(classes.videoBackground)
.removeClass(classes.videoChrome);
videos[playerId].$parentSlide.addClass(classes.slideBackgroundVideo);
videos[playerId].status = 'background';
sizeBackgroundVideo($player);
}
function initEvents() {
$(document).on('click.videoPlayer', '.' + classes.playVideoBtn, function(
evt
) {
var playerId = $(evt.currentTarget).data('controls');
startVideoOnClick(playerId);
});
$(document).on('click.videoPlayer', '.' + classes.closeVideoBtn, function(
evt
) {
var playerId = $(evt.currentTarget).data('controls');
closeVideo(playerId);
});
// Listen to resize to keep a background-size:cover-like layout
$(window).on(
'resize.videoPlayer',
$.debounce(250, function() {
if (youtubeLoaded) {
sizeBackgroundVideos();
}
})
);
}
function removeEvents() {
$(document).off('.videoPlayer');
$(window).off('.videoPlayer');
}
return {
init: init,
loadVideos: loadVideos,
loadVideo: loadVideo,
playVideo: customPlayVideo,
pauseVideo: pauseVideo,
removeEvents: removeEvents
};
})();
window.theme = window.theme || {};
theme.FormStatus = (function() {
var selectors = {
statusMessage: '[data-form-status]'
};
function init() {
this.$statusMessage = $(selectors.statusMessage);
if (!this.$statusMessage) return;
this.$statusMessage.focus();
}
return {
init: init
};
})();
/* ================ TEMPLATES ================ */
(function() {
var $filterBy = $('#BlogTagFilter');
if (!$filterBy.length) {
return;
}
$filterBy.on('change', function() {
location.href = $(this).val();
});
})();
window.theme = theme || {};
theme.customerTemplates = (function() {
function initEventListeners() {
// Show reset password form
$('#RecoverPassword').on('click', function(evt) {
evt.preventDefault();
toggleRecoverPasswordForm();
});
// Hide reset password form
$('#HideRecoverPasswordLink').on('click', function(evt) {
evt.preventDefault();
toggleRecoverPasswordForm();
});
}
/**
*
* Show/Hide recover password form
*
*/
function toggleRecoverPasswordForm() {
$('#RecoverPasswordForm').toggleClass('hide');
$('#CustomerLoginForm').toggleClass('hide');
}
/**
*
* Show reset password success message
*
*/
function resetPasswordSuccess() {
var $formState = $('.reset-password-success');
// check if reset password form was successfully submited.
if (!$formState.length) {
return;
}
// show success message
$('#ResetSuccess').removeClass('hide');
}
/**
*
* Show/hide customer address forms
*
*/
function customerAddressForm() {
var $newAddressForm = $('#AddressNewForm');
if (!$newAddressForm.length) {
return;
}
// Initialize observers on address selectors, defined in shopify_common.js
if (Shopify) {
// eslint-disable-next-line no-new
new Shopify.CountryProvinceSelector(
'AddressCountryNew',
'AddressProvinceNew',
{
hideElement: 'AddressProvinceContainerNew'
}
);
}
// Initialize each edit form's country/province selector
$('.address-country-option').each(function() {
var formId = $(this).data('form-id');
var countrySelector = 'AddressCountry_' + formId;
var provinceSelector = 'AddressProvince_' + formId;
var containerSelector = 'AddressProvinceContainer_' + formId;
// eslint-disable-next-line no-new
new Shopify.CountryProvinceSelector(countrySelector, provinceSelector, {
hideElement: containerSelector
});
});
// Toggle new/edit address forms
$('.address-new-toggle').on('click', function() {
$newAddressForm.toggleClass('hide');
});
$('.address-edit-toggle').on('click', function() {
var formId = $(this).data('form-id');
$('#EditAddress_' + formId).toggleClass('hide');
});
$('.address-delete').on('click', function() {
var $el = $(this);
var formId = $el.data('form-id');
var confirmMessage = $el.data('confirm-message');
// eslint-disable-next-line no-alert
if (
confirm(
confirmMessage || 'Are you sure you wish to delete this address?'
)
) {
Shopify.postLink('/account/addresses/' + formId, {
parameters: { _method: 'delete' }
});
}
});
}
/**
*
* Check URL for reset password hash
*
*/
function checkUrlHash() {
var hash = window.location.hash;
// Allow deep linking to recover password form
if (hash === '#recover') {
toggleRecoverPasswordForm();
}
}
return {
init: function() {
checkUrlHash();
initEventListeners();
resetPasswordSuccess();
customerAddressForm();
}
};
})();
/*================ SECTIONS ================*/
window.theme = window.theme || {};
theme.Cart = (function() {
var selectors = {
edit: '.js-edit-toggle'
};
var config = {
showClass: 'cart__update--show',
showEditClass: 'cart__edit--active',
cartNoCookies: 'cart--no-cookies'
};
function Cart(container) {
this.$container = $(container);
this.$edit = $(selectors.edit, this.$container);
if (!this.cookiesEnabled()) {
this.$container.addClass(config.cartNoCookies);
}
this.$edit.on('click', this._onEditClick.bind(this));
}
Cart.prototype = _.assignIn({}, Cart.prototype, {
onUnload: function() {
this.$edit.off('click', this._onEditClick);
},
_onEditClick: function(evt) {
var $evtTarget = $(evt.target);
var $updateLine = $('.' + $evtTarget.data('target'));
$evtTarget.toggleClass(config.showEditClass);
$updateLine.toggleClass(config.showClass);
},
cookiesEnabled: function() {
var cookieEnabled = navigator.cookieEnabled;
if (!cookieEnabled) {
document.cookie = 'testcookie';
cookieEnabled = document.cookie.indexOf('testcookie') !== -1;
}
return cookieEnabled;
}
});
return Cart;
})();
window.theme = window.theme || {};
theme.Filters = (function() {
var constants = {
SORT_BY: 'sort_by'
};
var selectors = {
mainContent: '#MainContent',
filterSelection: '#SortTags',
sortSelection: '#SortBy',
defaultSort: '#DefaultSortBy'
};
function Filters(container) {
var $container = (this.$container = $(container));
this.$filterSelect = $(selectors.filterSelection, $container);
this.$sortSelect = $(selectors.sortSelection, $container);
this.$selects = $(selectors.filterSelection, $container).add(
$(selectors.sortSelection, $container)
);
this.defaultSort = this._getDefaultSortValue();
this._resizeSelect(this.$selects);
this.$selects.removeClass('hidden');
this.$filterSelect.on('change', this._onFilterChange.bind(this));
this.$sortSelect.on('change', this._onSortChange.bind(this));
}
Filters.prototype = _.assignIn({}, Filters.prototype, {
_onSortChange: function(evt) {
var sort = this._sortValue();
var url = window.location.href.replace(window.location.search, '');
if (sort.length) {
var urlStripped = url.replace(window.location.hash, '');
window.location.href = urlStripped + '?' + sort + selectors.mainContent;
} else {
// clean up our url if the sort value is blank for default
window.location.href = url;
}
this._resizeSelect($(evt.target));
},
_onFilterChange: function(evt) {
var filter = this._getFilterValue();
// remove the 'page' parameter to go to the first page of results
var search = document.location.search.replace(/\?(page=\w+)?&?/, '');
// only add the search parameters to the url if they exist
search = search !== '' ? '?' + search : '';
document.location.href = filter + search + selectors.mainContent;
this._resizeSelect($(evt.target));
},
_getFilterValue: function() {
return this.$filterSelect.val();
},
_getSortValue: function() {
return this.$sortSelect.val() || this.defaultSort;
},
_getDefaultSortValue: function() {
return $(selectors.defaultSort, this.$container).val();
},
_sortValue: function() {
var sort = this._getSortValue();
var query = '';
if (sort !== this.defaultSort) {
query = constants.SORT_BY + '=' + sort;
}
return query;
},
_resizeSelect: function($selection) {
$selection.each(function() {
var $this = $(this);
var arrowWidth = 10;
// create test element
var text = $this.find('option:selected').text();
var $test = $('<span>').html(text);
// add to body, get width, and get out
$test.appendTo('body');
var width = $test.width();
$test.remove();
// set select width
$this.width(width + arrowWidth);
});
},
onUnload: function() {
this.$filterSelect.off('change', this._onFilterChange);
this.$sortSelect.off('change', this._onSortChange);
}
});
return Filters;
})();
window.theme = window.theme || {};
theme.HeaderSection = (function() {
function Header() {
theme.Header.init();
theme.MobileNav.init();
theme.Search.init();
}
Header.prototype = _.assignIn({}, Header.prototype, {
onUnload: function() {
theme.Header.unload();
}
});
return Header;
})();
theme.Maps = (function() {
var config = {
zoom: 14
};
var apiStatus = null;
var mapsToLoad = [];
var errors = {
addressNoResults: theme.strings.addressNoResults,
addressQueryLimit: theme.strings.addressQueryLimit,
addressError: theme.strings.addressError,
authError: theme.strings.authError
};
var selectors = {
section: '[data-section-type="map"]',
map: '[data-map]',
mapOverlay: '[data-map-overlay]'
};
var classes = {
mapError: 'map-section--load-error',
errorMsg: 'map-section__error errors text-center'
};
// Global function called by Google on auth errors.
// Show an auto error message on all map instances.
// eslint-disable-next-line camelcase, no-unused-vars
window.gm_authFailure = function() {
if (!Shopify.designMode) {
return;
}
$(selectors.section).addClass(classes.mapError);
$(selectors.map).remove();
$(selectors.mapOverlay).after(
'<div class="' +
classes.errorMsg +
'">' +
theme.strings.authError +
'</div>'
);
};
function Map(container) {
this.$container = $(container);
this.$map = this.$container.find(selectors.map);
this.key = this.$map.data('api-key');
if (typeof this.key === 'undefined') {
return;
}
if (apiStatus === 'loaded') {
this.createMap();
} else {
mapsToLoad.push(this);
if (apiStatus !== 'loading') {
apiStatus = 'loading';
if (typeof window.google === 'undefined') {
$.getScript(
'https://maps.googleapis.com/maps/api/js?key=' + this.key
).then(function() {
apiStatus = 'loaded';
initAllMaps();
});
}
}
}
}
function initAllMaps() {
// API has loaded, load all Map instances in queue
$.each(mapsToLoad, function(index, instance) {
instance.createMap();
});
}
function geolocate($map) {
var deferred = $.Deferred();
var geocoder = new google.maps.Geocoder();
var address = $map.data('address-setting');
geocoder.geocode({ address: address }, function(results, status) {
if (status !== google.maps.GeocoderStatus.OK) {
deferred.reject(status);
}
deferred.resolve(results);
});
return deferred;
}
Map.prototype = _.assignIn({}, Map.prototype, {
createMap: function() {
var $map = this.$map;
return geolocate($map)
.then(
function(results) {
var mapOptions = {
zoom: config.zoom,
center: results[0].geometry.location,
draggable: false,
clickableIcons: false,
scrollwheel: false,
disableDoubleClickZoom: true,
disableDefaultUI: true
};
var map = (this.map = new google.maps.Map($map[0], mapOptions));
var center = (this.center = map.getCenter());
//eslint-disable-next-line no-unused-vars
var marker = new google.maps.Marker({
map: map,
position: map.getCenter()
});
google.maps.event.addDomListener(
window,
'resize',
$.debounce(250, function() {
google.maps.event.trigger(map, 'resize');
map.setCenter(center);
$map.removeAttr('style');
})
);
}.bind(this)
)
.fail(function() {
var errorMessage;
switch (status) {
case 'ZERO_RESULTS':
errorMessage = errors.addressNoResults;
break;
case 'OVER_QUERY_LIMIT':
errorMessage = errors.addressQueryLimit;
break;
case 'REQUEST_DENIED':
errorMessage = errors.authError;
break;
default:
errorMessage = errors.addressError;
break;
}
// Show errors only to merchant in the editor.
if (Shopify.designMode) {
$map
.parent()
.addClass(classes.mapError)
.html(
'<div class="' +
classes.errorMsg +
'">' +
errorMessage +
'</div>'
);
}
});
},
onUnload: function() {
if (this.$map.length === 0) {
return;
}
google.maps.event.clearListeners(this.map, 'resize');
}
});
return Map;
})();
/* eslint-disable no-new */
theme.Product = (function() {
function Product(container) {
var $container = (this.$container = $(container));
var sectionId = $container.attr('data-section-id');
this.settings = {
// Breakpoints from src/stylesheets/global/variables.scss.liquid
mediaQueryMediumUp: 'screen and (min-width: 750px)',
mediaQuerySmall: 'screen and (max-width: 749px)',
bpSmall: false,
enableHistoryState: $container.data('enable-history-state') || false,
namespace: '.slideshow-' + sectionId,
sectionId: sectionId,
sliderActive: false,
zoomEnabled: false
};
this.selectors = {
addToCart: '#AddToCart-' + sectionId,
addToCartText: '#AddToCartText-' + sectionId,
SKU: '.variant-sku',
productStatus: '[data-product-status]',
originalSelectorId: '#ProductSelect-' + sectionId,
productImageWraps: '.product-single__photo',
productThumbImages: '.product-single__thumbnail--' + sectionId,
productThumbs: '.product-single__thumbnails-' + sectionId,
productFeaturedImage: '.product-featured-img',
productThumbsWrapper: '.thumbnails-wrapper',
saleLabel: '.product-price__sale-label-' + sectionId,
singleOptionSelector: '.single-option-selector-' + sectionId,
shopifyPaymentButton: '.shopify-payment-button',
priceContainer: '[data-price]',
regularPrice: '[data-regular-price]',
salePrice: '[data-sale-price]'
};
this.classes = {
productOnSale: 'price--on-sale',
productUnavailable: 'price--unavailable'
};
// Stop parsing if we don't have the product json script tag when loading
// section in the Theme Editor
if (!$('#ProductJson-' + sectionId).html()) {
return;
}
this.productSingleObject = JSON.parse(
document.getElementById('ProductJson-' + sectionId).innerHTML
);
this.settings.zoomEnabled = $(this.selectors.productImageWraps).hasClass(
'js-zoom-enabled'
);
this._initBreakpoints();
this._stringOverrides();
this._initVariants();
this._initImageSwitch();
this._setActiveThumbnail();
}
Product.prototype = _.assignIn({}, Product.prototype, {
_stringOverrides: function() {
theme.productStrings = theme.productStrings || {};
$.extend(theme.strings, theme.productStrings);
},
_initBreakpoints: function() {
var self = this;
enquire.register(this.settings.mediaQuerySmall, {
match: function() {
// initialize thumbnail slider on mobile if more than three thumbnails
if ($(self.selectors.productThumbImages).length > 3) {
self._initThumbnailSlider();
}
// destroy image zooming if enabled
if (self.settings.zoomEnabled) {
$(self.selectors.productImageWraps).each(function() {
_destroyZoom(this);
});
}
self.settings.bpSmall = true;
},
unmatch: function() {
if (self.settings.sliderActive) {
self._destroyThumbnailSlider();
}
self.settings.bpSmall = false;
}
});
enquire.register(this.settings.mediaQueryMediumUp, {
match: function() {
if (self.settings.zoomEnabled) {
$(self.selectors.productImageWraps).each(function() {
_enableZoom(this);
});
}
}
});
},
_initVariants: function() {
var options = {
$container: this.$container,
enableHistoryState:
this.$container.data('enable-history-state') || false,
singleOptionSelector: this.selectors.singleOptionSelector,
originalSelectorId: this.selectors.originalSelectorId,
product: this.productSingleObject
};
this.variants = new slate.Variants(options);
this.$container.on(
'variantChange' + this.settings.namespace,
this._updateAvailability.bind(this)
);
this.$container.on(
'variantImageChange' + this.settings.namespace,
this._updateImages.bind(this)
);
this.$container.on(
'variantPriceChange' + this.settings.namespace,
this._updatePrice.bind(this)
);
this.$container.on(
'variantSKUChange' + this.settings.namespace,
this._updateSKU.bind(this)
);
},
_initImageSwitch: function() {
if (!$(this.selectors.productThumbImages).length) {
return;
}
var self = this;
$(this.selectors.productThumbImages)
.on('click', function(evt) {
evt.preventDefault();
var $el = $(this);
var imageId = $el.data('thumbnail-id');
self._switchImage(imageId);
self._setActiveThumbnail(imageId);
})
.on('keyup', self._handleImageFocus.bind(self));
},
_setActiveThumbnail: function(imageId) {
var activeClass = 'active-thumb';
// If there is no element passed, find it by the current product image
if (typeof imageId === 'undefined') {
imageId = $(this.selectors.productImageWraps + ":not('.hide')").data(
'image-id'
);
}
var $thumbnail = $(
this.selectors.productThumbImages +
"[data-thumbnail-id='" +
imageId +
"']"
);
$(this.selectors.productThumbImages)
.removeClass(activeClass)
.removeAttr('aria-current');
$thumbnail.addClass(activeClass);
$thumbnail.attr('aria-current', true);
},
_switchImage: function(imageId) {
var $newImage = $(
this.selectors.productImageWraps + "[data-image-id='" + imageId + "']",
this.$container
);
var $otherImages = $(
this.selectors.productImageWraps +
":not([data-image-id='" +
imageId +
"'])",
this.$container
);
$newImage.removeClass('hide');
$otherImages.addClass('hide');
},
_handleImageFocus: function(evt) {
if (evt.keyCode !== slate.utils.keyboardKeys.ENTER) return;
$(this.selectors.productFeaturedImage + ':visible').focus();
},
_initThumbnailSlider: function() {
var options = {
slidesToShow: 4,
slidesToScroll: 3,
infinite: false,
prevArrow: '.thumbnails-slider__prev--' + this.settings.sectionId,
nextArrow: '.thumbnails-slider__next--' + this.settings.sectionId,
responsive: [
{
breakpoint: 321,
settings: {
slidesToShow: 3
}
}
]
};
$(this.selectors.productThumbs).slick(options);
// Accessibility concerns not yet fixed in Slick Slider
$(this.selectors.productThumbsWrapper, this.$container)
.find('.slick-list')
.removeAttr('aria-live');
$(this.selectors.productThumbsWrapper, this.$container)
.find('.slick-disabled')
.removeAttr('aria-disabled');
this.settings.sliderActive = true;
},
_destroyThumbnailSlider: function() {
$(this.selectors.productThumbs).slick('unslick');
this.settings.sliderActive = false;
// Accessibility concerns not yet fixed in Slick Slider
$(this.selectors.productThumbsWrapper, this.$container)
.find('[tabindex="-1"]')
.removeAttr('tabindex');
},
_liveRegionText: function(variant) {
// Dummy content for live region
var liveRegionText = '[Availability] [Regular] [$$] [Sale] [$]';
if (!variant) {
liveRegionText = theme.strings.unavailable;
return liveRegionText;
}
// Update availability
var availability = variant.available ? '' : theme.strings.soldOut + ',';
liveRegionText = liveRegionText.replace('[Availability]', availability);
// Update pricing information
var regularLabel = '';
var regularPrice = theme.Currency.formatMoney(
variant.price,
theme.moneyFormat
);
var saleLabel = '';
var salePrice = '';
if (variant.compare_at_price > variant.price) {
regularLabel = theme.strings.regularPrice;
regularPrice =
theme.Currency.formatMoney(
variant.compare_at_price,
theme.moneyFormat
) + ',';
saleLabel = theme.strings.sale;
salePrice = theme.Currency.formatMoney(
variant.price,
theme.moneyFormat
);
}
liveRegionText = liveRegionText
.replace('[Regular]', regularLabel)
.replace('[$$]', regularPrice)
.replace('[Sale]', saleLabel)
.replace('[$]', salePrice)
.trim();
return liveRegionText;
},
_updateLiveRegion: function(evt) {
var variant = evt.variant;
var liveRegion = this.container.querySelector(
this.selectors.productStatus
);
liveRegion.textContent = this._liveRegionText(variant);
liveRegion.setAttribute('aria-hidden', false);
// hide content from accessibility tree after announcement
setTimeout(function() {
liveRegion.setAttribute('aria-hidden', true);
}, 1000);
},
_updateAddToCart: function(evt) {
var variant = evt.variant;
if (variant) {
if (variant.available) {
$(this.selectors.addToCart).prop('disabled', false);
$(this.selectors.addToCartText).text(theme.strings.addToCart);
$(this.selectors.shopifyPaymentButton, this.$container).show();
} else {
// The variant doesn't exist, disable submit button and change the text.
// This may be an error or notice that a specific variant is not available.
$(this.selectors.addToCart).prop('disabled', true);
$(this.selectors.addToCartText).text(theme.strings.soldOut);
$(this.selectors.shopifyPaymentButton, this.$container).hide();
}
} else {
$(this.selectors.addToCart).prop('disabled', true);
$(this.selectors.addToCartText).text(theme.strings.unavailable);
$(this.selectors.shopifyPaymentButton, this.$container).hide();
}
},
_updateAvailability: function(evt) {
// update form submit
this._updateAddToCart(evt);
// update live region
this._updateLiveRegion(evt);
this._updatePrice(evt);
},
_updateImages: function(evt) {
var variant = evt.variant;
var imageId = variant.featured_image.id;
this._switchImage(imageId);
this._setActiveThumbnail(imageId);
},
_updatePrice: function(evt) {
var variant = evt.variant;
var $priceContainer = $(this.selectors.priceContainer, this.$container);
var $regularPrice = $(this.selectors.regularPrice, $priceContainer);
var $salePrice = $(this.selectors.salePrice, $priceContainer);
// Reset product price state
$priceContainer
.removeClass(this.classes.productUnavailable)
.removeClass(this.classes.productOnSale)
.removeAttr('aria-hidden');
// Unavailable
if (!variant) {
$priceContainer
.addClass(this.classes.productUnavailable)
.attr('aria-hidden', true);
return;
}
// On sale
if (variant.compare_at_price > variant.price) {
$regularPrice.html(
theme.Currency.formatMoney(
variant.compare_at_price,
theme.moneyFormat
)
);
$salePrice.html(
theme.Currency.formatMoney(variant.price, theme.moneyFormat)
);
$priceContainer.addClass(this.classes.productOnSale);
} else {
// Regular price
$regularPrice.html(
theme.Currency.formatMoney(variant.price, theme.moneyFormat)
);
}
},
_updateSKU: function(evt) {
var variant = evt.variant;
// Update the sku
$(this.selectors.SKU).html(variant.sku);
},
onUnload: function() {
this.$container.off(this.settings.namespace);
}
});
function _enableZoom(el) {
var zoomUrl = $(el).data('zoom');
$(el).zoom({
url: zoomUrl
});
}
function _destroyZoom(el) {
$(el).trigger('zoom.destroy');
}
return Product;
})();
theme.Quotes = (function() {
var config = {
mediaQuerySmall: 'screen and (max-width: 749px)',
mediaQueryMediumUp: 'screen and (min-width: 750px)',
slideCount: 0
};
var defaults = {
accessibility: true,
arrows: false,
dots: true,
autoplay: false,
touchThreshold: 20,
slidesToShow: 3,
slidesToScroll: 3
};
function Quotes(container) {
var $container = (this.$container = $(container));
var sectionId = $container.attr('data-section-id');
var wrapper = (this.wrapper = '.quotes-wrapper');
var slider = (this.slider = '#Quotes-' + sectionId);
var $slider = $(slider, wrapper);
var sliderActive = false;
var mobileOptions = $.extend({}, defaults, {
slidesToShow: 1,
slidesToScroll: 1,
adaptiveHeight: true
});
config.slideCount = $slider.data('count');
// Override slidesToShow/Scroll if there are not enough blocks
if (config.slideCount < defaults.slidesToShow) {
defaults.slidesToShow = config.slideCount;
defaults.slidesToScroll = config.slideCount;
}
$slider.on('init', this.a11y.bind(this));
enquire.register(config.mediaQuerySmall, {
match: function() {
initSlider($slider, mobileOptions);
}
});
enquire.register(config.mediaQueryMediumUp, {
match: function() {
initSlider($slider, defaults);
}
});
function initSlider(sliderObj, args) {
if (sliderActive) {
sliderObj.slick('unslick');
sliderActive = false;
}
sliderObj.slick(args);
sliderActive = true;
}
}
Quotes.prototype = _.assignIn({}, Quotes.prototype, {
onUnload: function() {
enquire.unregister(config.mediaQuerySmall);
enquire.unregister(config.mediaQueryMediumUp);
$(this.slider, this.wrapper).slick('unslick');
},
onBlockSelect: function(evt) {
// Ignore the cloned version
var $slide = $(
'.quotes-slide--' + evt.detail.blockId + ':not(.slick-cloned)'
);
var slideIndex = $slide.data('slick-index');
// Go to selected slide, pause autoplay
$(this.slider, this.wrapper).slick('slickGoTo', slideIndex);
},
a11y: function(event, obj) {
var $list = obj.$list;
var $wrapper = $(this.wrapper, this.$container);
// Remove default Slick aria-live attr until slider is focused
$list.removeAttr('aria-live');
// When an element in the slider is focused set aria-live
$wrapper.on('focusin', function(evt) {
if ($wrapper.has(evt.target).length) {
$list.attr('aria-live', 'polite');
}
});
// Remove aria-live
$wrapper.on('focusout', function(evt) {
if ($wrapper.has(evt.target).length) {
$list.removeAttr('aria-live');
}
});
}
});
return Quotes;
})();
theme.slideshows = {};
theme.SlideshowSection = (function() {
function SlideshowSection(container) {
var $container = (this.$container = $(container));
var sectionId = $container.attr('data-section-id');
var slideshow = (this.slideshow = '#Slideshow-' + sectionId);
$('.slideshow__video', slideshow).each(function() {
var $el = $(this);
theme.SlideshowVideo.init($el);
theme.SlideshowVideo.loadVideo($el.attr('id'));
});
theme.slideshows[slideshow] = new theme.Slideshow(slideshow);
}
return SlideshowSection;
})();
theme.SlideshowSection.prototype = _.assignIn(
{},
theme.SlideshowSection.prototype,
{
onUnload: function() {
delete theme.slideshows[this.slideshow];
},
onBlockSelect: function(evt) {
var $slideshow = $(this.slideshow);
// Ignore the cloned version
var $slide = $(
'.slideshow__slide--' + evt.detail.blockId + ':not(.slick-cloned)'
);
var slideIndex = $slide.data('slick-index');
// Go to selected slide, pause autoplay
$slideshow.slick('slickGoTo', slideIndex).slick('slickPause');
},
onBlockDeselect: function() {
// Resume autoplay
$(this.slideshow).slick('slickPlay');
}
}
);
$(document).ready(function() {
var sections = new theme.Sections();
sections.register('cart-template', theme.Cart);
sections.register('product', theme.Product);
sections.register('collection-template', theme.Filters);
sections.register('product-template', theme.Product);
sections.register('header-section', theme.HeaderSection);
sections.register('map', theme.Maps);
sections.register('slideshow-section', theme.SlideshowSection);
sections.register('quotes', theme.Quotes);
});
theme.init = function() {
theme.customerTemplates.init();
// Theme-specific selectors to make tables scrollable
var tableSelectors = '.rte table,' + '.custom__item-inner--html table';
slate.rte.wrapTable({
$tables: $(tableSelectors),
tableWrapperClass: 'scrollable-wrapper'
});
// Theme-specific selectors to make iframes responsive
var iframeSelectors =
'.rte iframe[src*="youtube.com/embed"],' +
'.rte iframe[src*="player.vimeo"],' +
'.custom__item-inner--html iframe[src*="youtube.com/embed"],' +
'.custom__item-inner--html iframe[src*="player.vimeo"]';
slate.rte.wrapIframe({
$iframes: $(iframeSelectors),
iframeWrapperClass: 'video-wrapper'
});
// Common a11y fixes
slate.a11y.pageLinkFocus($(window.location.hash));
$('.in-page-link').on('click', function(evt) {
slate.a11y.pageLinkFocus($(evt.currentTarget.hash));
});
$('a[href="#"]').on('click', function(evt) {
evt.preventDefault();
});
slate.a11y.accessibleLinks({
messages: {
newWindow: theme.strings.newWindow,
external: theme.strings.external,
newWindowExternal: theme.strings.newWindowExternal
},
$links: $('a:not([aria-describedby], .product-single__thumbnail)')
});
theme.FormStatus.init();
};
$(theme.init);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment