window.theme = window.theme || {};
window.Shopify = window.Shopify || {};
theme.config = {
hasSessionStorage: true,
hasLocalStorage: true,
isTouch: ('ontouchstart' in window) || window.DocumentTouch && window.document instanceof DocumentTouch || window.navigator.maxTouchPoints || window.navigator.msMaxTouchPoints ? true : false,
rtl: document.documentElement.getAttribute('dir') == 'rtl' ? true : false
};
(function(){
'use strict';
theme.delegate = {
on: function(event, callback, options){
if( !this.namespaces )
this.namespaces = {};
this.namespaces[event] = callback;
options = options || false;
this.addEventListener(event.split('.')[0], callback, options);
return this;
},
off: function(event) {
if (!this.namespaces) { return }
this.removeEventListener(event.split('.')[0], this.namespaces[event]);
delete this.namespaces[event];
return this;
}
};
theme.utils = {
defaultTo: function(value, defaultValue) {
return (value == null || value !== value) ? defaultValue : value
},
wrap: function(el, wrapper) {
el.parentNode.insertBefore(wrapper, el);
wrapper.appendChild(el);
},
debounce: function(wait, callback, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) callback.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) callback.apply(context, args);
}
},
throttle: function(limit, callback) {
var waiting = false;
return function () {
if (!waiting) {
callback.apply(this, arguments);
waiting = true;
setTimeout(function () {
waiting = false;
}, limit);
}
}
},
prepareTransition: function(el, callback) {
el.addEventListener('transitionend', removeClass);
function removeClass(evt) {
el.classList.remove('is-transitioning');
el.removeEventListener('transitionend', removeClass);
}
el.classList.add('is-transitioning');
el.offsetWidth; // check offsetWidth to force the style rendering
if (typeof callback === 'function') {
callback();
}
},
// _.compact from lodash
// Creates an array with all falsey values removed. The values `false`, `null`,
// `0`, `""`, `undefined`, and `NaN` are falsey.
// _.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
compact: function(array) {
var index = -1,
length = array == null ? 0 : array.length,
resIndex = 0,
result = [];
while (++index < length) {
var value = array[index];
if (value) {
result[resIndex++] = value;
}
}
return result;
},
serialize: function(form) {
var arr = [];
Array.prototype.slice.call(form.elements).forEach(function(field) {
if (
!field.name ||
field.disabled ||
['file', 'reset', 'submit', 'button'].indexOf(field.type) > -1
)
return;
if (field.type === 'select-multiple') {
Array.prototype.slice.call(field.options).forEach(function(option) {
if (!option.selected) return;
arr.push(
encodeURIComponent(field.name) +
'=' +
encodeURIComponent(option.value)
);
});
return;
}
if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked)
return;
arr.push(
encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value)
);
});
return arr.join('&');
}
};
// Init section function when it's visible, then disable observer
theme.initWhenVisible = function(options) {
var threshold = options.threshold ? options.threshold : 0;
var observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (typeof options.callback === 'function') {
options.callback();
observer.unobserve(entry.target);
}
}
});
}, {rootMargin: '0px 0px '+ threshold +'px 0px'});
observer.observe(options.element);
};
window.on = Element.prototype.on = theme.delegate.on;
window.off = Element.prototype.off = theme.delegate.off;
theme.a11y = {
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'
};
var focusableEls = options.container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex^="-"])');
var elArray = [].slice.call(focusableEls);
var focusableElements = elArray.filter(el => el.offsetParent !== null);
var firstFocusable = focusableElements[0];
var lastFocusable = focusableElements[focusableElements.length - 1];
if (!options.elementToFocus) {
options.elementToFocus = options.container;
}
options.container.setAttribute('tabindex', '-1');
options.elementToFocus.focus();
document.documentElement.off('focusin');
document.documentElement.on(eventsName.focusout, function() {
document.documentElement.off(eventsName.keydown);
});
document.documentElement.on(eventsName.focusin, function(evt) {
if (evt.target !== lastFocusable && evt.target !== firstFocusable) return;
document.documentElement.on(eventsName.keydown, function(evt) {
_manageFocus(evt);
});
});
function _manageFocus(evt) {
if (evt.keyCode !== 9) return;
if (evt.target === firstFocusable && evt.shiftKey) {
evt.preventDefault();
lastFocusable.focus();
}
}
},
removeTrapFocus: function(options) {
var eventName = options.namespace
? 'focusin.' + options.namespace
: 'focusin';
if (options.container) {
options.container.removeAttribute('tabindex');
}
document.documentElement.off(eventName);
},
lockMobileScrolling: function(namespace, element) {
var el = element ? element : document.documentElement;
document.documentElement.classList.add('lock-scroll');
el.on('touchmove' + namespace, function() {
return true;
});
},
unlockMobileScrolling: function(namespace, element) {
document.documentElement.classList.remove('lock-scroll');
var el = element ? element : document.documentElement;
el.off('touchmove' + namespace);
}
};
document.documentElement.on('keyup.tab', function(evt) {
if (evt.keyCode === 9) {
document.documentElement.classList.add('tab-outline');
document.documentElement.off('keyup.tab');
}
});
theme.Sections = function Sections() {
this.constructors = {};
this.instances = [];
document.addEventListener('shopify:section:load', this._onSectionLoad.bind(this));
document.addEventListener('shopify:section:unload', this._onSectionUnload.bind(this));
document.addEventListener('shopify:section:select', this._onSelect.bind(this));
document.addEventListener('shopify:section:deselect', this._onDeselect.bind(this));
document.addEventListener('shopify:block:select', this._onBlockSelect.bind(this));
document.addEventListener('shopify:block:deselect', this._onBlockDeselect.bind(this));
};
theme.Sections.prototype = Object.assign({}, theme.Sections.prototype, {
_createInstance: function(container, constructor, scope) {
var id = container.getAttribute('data-section-id');
var type = container.getAttribute('data-section-type');
constructor = constructor || this.constructors[type];
if (typeof constructor === 'undefined') {
return;
}
if (scope) {
var instanceExists = this._findInstance(id);
if (instanceExists) {
this._removeInstance(id);
}
}
var instance = Object.assign(new constructor(container), {
id: id,
type: type,
container: container
});
this.instances.push(instance);
},
_findInstance: function(id) {
for (var i = 0; i < this.instances.length; i++) {
if (this.instances[i].id === id) {
return this.instances[i];
}
}
},
_removeInstance: function(id) {
var i = this.instances.length;
var instance;
while(i--) {
if (this.instances[i].id === id) {
instance = this.instances[i];
this.instances.splice(i, 1);
break;
}
}
return instance;
},
_onSectionLoad: function(evt, subSection, subSectionId) {
if (window.AOS) { AOS.refreshHard() }
if (theme && theme.initGlobals) {
theme.initGlobals();
}
var container = subSection ? subSection : evt.target;
var section = subSection ? subSection : evt.target.querySelector('[data-section-id]');
if (!section) {
return;
}
this._createInstance(section);
var instance = subSection ? subSectionId : this._findInstance(evt.detail.sectionId);
var haveSubSections = container.querySelectorAll('[data-subsection]');
if (haveSubSections.length) {
this.loadSubSections(container);
}
if (instance && typeof instance.onLoad === 'function') {
instance.onLoad(evt);
}
setTimeout(function() {
window.dispatchEvent(new Event('scroll'));
}, 200);
},
_onSectionUnload: function(evt) {
this.instances = this.instances.filter(function(instance) {
var isEventInstance = instance.id === evt.detail.sectionId;
if (isEventInstance) {
if (typeof instance.onUnload === 'function') {
instance.onUnload(evt);
}
}
return !isEventInstance;
});
},
loadSubSections: function(scope) {
var sections = scope
? scope.querySelectorAll('[data-subsection]')
: document.querySelectorAll('[data-subsection]');
sections.forEach(el => {
this._onSectionLoad(null, el, el.dataset.sectionId);
});
if (window.AOS) { AOS.refreshHard() }
},
_onSelect: function(evt) {
var instance = this._findInstance(evt.detail.sectionId);
if (
typeof instance !== 'undefined' &&
typeof instance.onSelect === 'function'
) {
instance.onSelect(evt);
}
},
_onDeselect: function(evt) {
var instance = this._findInstance(evt.detail.sectionId);
if (
typeof instance !== 'undefined' &&
typeof instance.onDeselect === 'function'
) {
instance.onDeselect(evt);
}
},
_onBlockSelect: function(evt) {
var instance = this._findInstance(evt.detail.sectionId);
if (
typeof instance !== 'undefined' &&
typeof instance.onBlockSelect === 'function'
) {
instance.onBlockSelect(evt);
}
},
_onBlockDeselect: function(evt) {
var instance = this._findInstance(evt.detail.sectionId);
if (
typeof instance !== 'undefined' &&
typeof instance.onBlockDeselect === 'function'
) {
instance.onBlockDeselect(evt);
}
},
register: function(type, constructor, scope) {
this.constructors[type] = constructor;
var sections = document.querySelectorAll('[data-section-type="' + type + '"]');
if (scope) {
sections = scope.querySelectorAll('[data-section-type="' + type + '"]');
}
sections.forEach(
function(container) {
this._createInstance(container, constructor, scope);
}.bind(this)
);
},
reinit: function(section) {
for (var i = 0; i < this.instances.length; i++) {
var instance = this.instances[i];
if (instance['type'] === section) {
if (typeof instance.forceReload === 'function') {
instance.forceReload();
}
}
}
}
});
// theme.Slideshow handles all flickity based sliders
// Child navigation is only setup to work on product images
theme.Slideshow = (function() {
var classes = {
animateOut: 'animate-out',
isPaused: 'is-paused',
isActive: 'is-active'
};
var selectors = {
allSlides: '.slideshow__slide',
currentSlide: '.is-selected',
wrapper: '.slider-wrapper',
pauseButton: '.slideshow__pause'
};
var productSelectors = {
thumb: '.product__thumb-item:not(.hide)',
links: '.product__thumb-item:not(.hide) .product__thumb',
arrow: '.product__thumb-arrow'
};
var defaults = {
adaptiveHeight: false,
autoPlay: false,
avoidReflow: false,
childNav: null, // instead of asNavFor
childNavScroller: null, // element
childVertical: false,
fade: false,
initialIndex: 0,
pageDots: false,
pauseAutoPlayOnHover: false,
prevNextButtons: false,
rightToLeft: theme.config.rtl,
setGallerySize: true,
wrapAround: true
};
function slideshow(el, args) {
this.el = el;
this.args = Object.assign({}, defaults, args);
// Setup listeners as part of arguments
this.args.on = {
ready: this.init.bind(this),
change: this.slideChange.bind(this),
settle: this.afterChange.bind(this)
};
if (this.args.childNav) {
this.childNavEls = this.args.childNav.querySelectorAll(productSelectors.thumb);
this.childNavLinks = this.args.childNav.querySelectorAll(productSelectors.links);
this.arrows = this.args.childNav.querySelectorAll(productSelectors.arrow);
if (this.childNavLinks.length) {
this.initChildNav();
}
}
if (this.args.avoidReflow) {
avoidReflow(el);
}
this.slideshow = new Flickity(el, this.args);
// Reset dimensions on resize
window.on('resize', theme.utils.debounce(300, function() {
this.resize();
}.bind(this)));
// Set flickity-viewport height to first element to
// avoid awkward page reflows while initializing.
// Must be added in a `style` tag because element does not exist yet.
// Slideshow element must have an ID
function avoidReflow(el) {
if (!el.id) return;
var firstChild = el.firstChild;
while(firstChild != null && firstChild.nodeType == 3){ // skip TextNodes
firstChild = firstChild.nextSibling;
}
var style = document.createElement('style');
style.innerHTML = `#${el.id} .flickity-viewport{height:${firstChild.offsetHeight}px}`;
document.head.appendChild(style);
}
}
slideshow.prototype = Object.assign({}, slideshow.prototype, {
init: function(el) {
this.currentSlide = this.el.querySelector(selectors.currentSlide);
// Optional onInit callback
if (this.args.callbacks && this.args.callbacks.onInit) {
if (typeof this.args.callbacks.onInit === 'function') {
this.args.callbacks.onInit(this.currentSlide);
}
}
if (window.AOS) { AOS.refresh() }
},
slideChange: function(index) {
// Outgoing fade styles
if (this.args.fade && this.currentSlide) {
this.currentSlide.classList.add(classes.animateOut);
this.currentSlide.addEventListener('transitionend', function() {
this.currentSlide.classList.remove(classes.animateOut);
}.bind(this));
}
// Match index with child nav
if (this.args.childNav) {
this.childNavGoTo(index);
}
// Optional onChange callback
if (this.args.callbacks && this.args.callbacks.onChange) {
if (typeof this.args.callbacks.onChange === 'function') {
this.args.callbacks.onChange(index);
}
}
// Show/hide arrows depending on selected index
if (this.arrows && this.arrows.length) {
this.arrows[0].classList.toggle('hide', index === 0);
this.arrows[1].classList.toggle('hide', index === (this.childNavLinks.length - 1));
}
},
afterChange: function(index) {
// Remove all fade animation classes after slide is done
if (this.args.fade) {
this.el.querySelectorAll(selectors.allSlides).forEach(slide => {
slide.classList.remove(classes.animateOut);
});
}
this.currentSlide = this.el.querySelector(selectors.currentSlide);
// Match index with child nav (in case slider height changed first)
if (this.args.childNav) {
this.childNavGoTo(this.slideshow.selectedIndex);
}
},
destroy: function() {
if (this.args.childNav && this.childNavLinks.length) {
this.childNavLinks.forEach(a => {
a.classList.remove(classes.isActive);
});
}
this.slideshow.destroy();
},
_togglePause: function() {
if (this.pauseBtn.classList.contains(classes.isPaused)) {
this.pauseBtn.classList.remove(classes.isPaused);
this.slideshow.playPlayer();
} else {
this.pauseBtn.classList.add(classes.isPaused);
this.slideshow.pausePlayer();
}
},
resize: function() {
this.slideshow.resize();
},
play: function() {
this.slideshow.playPlayer();
},
pause: function() {
this.slideshow.pausePlayer();
},
goToSlide: function(i) {
this.slideshow.select(i);
},
setDraggable: function(enable) {
this.slideshow.options.draggable = enable;
this.slideshow.updateDraggable();
},
initChildNav: function() {
this.childNavLinks[this.args.initialIndex].classList.add('is-active');
// Setup events
this.childNavLinks.forEach((link, i) => {
// update data-index because image-set feature may be enabled
link.setAttribute('data-index', i);
link.addEventListener('click', function(evt) {
evt.preventDefault();
this.goToSlide(this.getChildIndex(evt.currentTarget))
}.bind(this));
link.addEventListener('focus', function(evt) {
this.goToSlide(this.getChildIndex(evt.currentTarget))
}.bind(this));
link.addEventListener('keydown', function(evt) {
if (evt.keyCode === 13) {
this.goToSlide(this.getChildIndex(evt.currentTarget))
}
}.bind(this));
});
// Setup optional arrows
if (this.arrows.length) {
this.arrows.forEach(arrow => {
arrow.addEventListener('click', this.arrowClick.bind(this));
});;
}
},
getChildIndex: function(target) {
return parseInt(target.dataset.index);
},
childNavGoTo: function(index) {
this.childNavLinks.forEach(a => {
a.classList.remove(classes.isActive);
});
var el = this.childNavLinks[index];
el.classList.add(classes.isActive);
if (!this.args.childNavScroller) {
return;
}
if (this.args.childVertical) {
var elTop = el.offsetTop;
this.args.childNavScroller.scrollTop = elTop - 100;
} else {
var elLeft = el.offsetLeft;
this.args.childNavScroller.scrollLeft = elLeft - 100;
}
if($(window).innerWidth() < 901){
var elLeft = el.offsetLeft;
this.args.childNavScroller.scrollLeft = elLeft - 100;
}
},
arrowClick: function(evt) {
if (evt.currentTarget.classList.contains('product__thumb-arrow--prev')) {
this.slideshow.previous();
} else {
this.slideshow.next();
}
}
});
return slideshow;
})();
function DOMready(callback) {
if (document.readyState != 'loading') callback();
else document.addEventListener('DOMContentLoaded', callback);
}
DOMready(function(){
theme.sections = new theme.Sections();
theme.sections.register('slideshow', theme.Slideshow);
document.dispatchEvent(new CustomEvent('page:loaded'));
});