· 5 years ago · May 03, 2020, 08:00 PM
1(function($) {
2 'use strict';
3
4 // StyleHatch Object
5 window.StyleHatch = window.StyleHatch || {};
6
7 /**
8 * Sections
9 * ---------------------------------------------------------------------------
10 * Constructors, instances and events for the Shopify Theme Editor
11 */
12 StyleHatch.Sections = function Sections() {
13 this.constructors = {};
14 this.instances = [];
15
16 $(document)
17 .on('shopify:section:load', this._onSectionLoad.bind(this))
18 .on('shopify:section:unload', this._onSectionUnload.bind(this))
19 .on('shopify:section:select', this._onSelect.bind(this))
20 .on('shopify:section:deselect', this._onDeselect.bind(this))
21 .on('shopify:section:reorder', this._onReorder.bind(this))
22 .on('shopify:block:select', this._onBlockSelect.bind(this))
23 .on('shopify:block:deselect', this._onBlockDeselect.bind(this));
24 };
25 /**
26 * Prototypes to extend sections
27 */
28 StyleHatch.Sections.prototype = $.extend({}, StyleHatch.Sections.prototype, {
29 _createInstance: function(container, constructor) {
30 var $container = $(container);
31 var id = $container.attr('data-section-id');
32 var type = $container.attr('data-section-type');
33
34 constructor = constructor || this.constructors[type];
35
36 if (typeof constructor === 'undefined') {
37 return;
38 }
39
40 var instance = $.extend(new constructor(container), {
41 id: id,
42 type: type,
43 container: container,
44 });
45
46 this.instances.push(instance);
47 },
48
49 _onSectionLoad: function(evt) {
50 var container = $('[data-section-id]', evt.target)[0];
51 if (container) {
52 this._createInstance(container);
53 }
54 },
55
56 _onSectionUnload: function(evt) {
57 var instance = slate.utils.findInstance(
58 this.instances,
59 'id',
60 evt.detail.sectionId
61 );
62
63 if (!instance) {
64 return;
65 }
66
67 if (typeof instance.onUnload === 'function') {
68 instance.onUnload(evt);
69 }
70
71 this.instances = slate.utils.removeInstance(
72 this.instances,
73 'id',
74 evt.detail.sectionId
75 );
76 },
77
78 _onSelect: function(evt) {
79 var instance = slate.utils.findInstance(
80 this.instances,
81 'id',
82 evt.detail.sectionId
83 );
84
85 if (instance && typeof instance.onSelect === 'function') {
86 instance.onSelect(evt);
87 }
88
89 if ($('body').hasClass('panel-open')) {
90 StyleHatch.closePanelMenu();
91 $('html, body').addClass('scroll-lock');
92 // Prevent theme editor issues
93 setTimeout(function() {
94 $('html, body').removeClass('scroll-lock');
95 $('html, body').animate(
96 {
97 scrollTop: instance.$container.offset().top, // - fixedOffset
98 },
99 600
100 );
101 }, 400);
102 }
103 },
104
105 _onDeselect: function(evt) {
106 var instance = slate.utils.findInstance(
107 this.instances,
108 'id',
109 evt.detail.sectionId
110 );
111
112 if (instance && typeof instance.onDeselect === 'function') {
113 instance.onDeselect(evt);
114 }
115
116 if ($('body').hasClass('panel-open')) {
117 StyleHatch.closePanelMenu();
118 }
119 },
120
121 _onReorder: function(evt) {
122 var instance = slate.utils.findInstance(
123 this.instances,
124 'id',
125 evt.detail.sectionId
126 );
127
128 if (instance && typeof instance.onReorder === 'function') {
129 instance.onReorder(evt);
130 }
131 },
132
133 _onBlockSelect: function(evt) {
134 var instance = slate.utils.findInstance(
135 this.instances,
136 'id',
137 evt.detail.sectionId
138 );
139
140 if (instance && typeof instance.onBlockSelect === 'function') {
141 instance.onBlockSelect(evt);
142 }
143 },
144
145 _onBlockDeselect: function(evt) {
146 var instance = slate.utils.findInstance(
147 this.instances,
148 'id',
149 evt.detail.sectionId
150 );
151
152 if (instance && typeof instance.onBlockDeselect === 'function') {
153 instance.onBlockDeselect(evt);
154 }
155 },
156
157 register: function(type, constructor) {
158 this.constructors[type] = constructor;
159
160 $('[data-section-type=' + type + ']').each(
161 function(index, container) {
162 this._createInstance(container, constructor);
163 }.bind(this)
164 );
165 },
166 });
167
168 /**
169 * Cache common selectors
170 */
171 StyleHatch.cacheSelectors = function() {
172 StyleHatch.cache = {
173 // General
174 $body: $('body'),
175 $html: $('html'),
176
177 // Util header
178 $util: $('header.util'),
179 $header: $('header.site-header'),
180 $siteNav: $('header.site-header ul.site-nav'),
181 $featuredCollection: $('.featured-collection'),
182 $instagramCollection: $('.instagram-collection'),
183
184 $addToCartForm: $('#AddToCartForm'),
185 $addToCartButton: $('#AddToCart'),
186 $cartButton: $('#CartButton'),
187
188 // Customer Pages
189 $recoverPasswordLink: $('#RecoverPassword'),
190 $hideRecoverPasswordLink: $('#HideRecoverPasswordLink'),
191 $recoverPasswordForm: $('#RecoverPasswordForm'),
192 $customerLoginForm: $('#CustomerLoginForm'),
193 $passwordResetSuccess: $('#ResetSuccess'),
194 };
195 };
196
197 StyleHatch.init = function() {
198 var doc = document.documentElement;
199 doc.setAttribute('data-useragent', navigator.userAgent);
200
201 StyleHatch.cacheSelectors();
202
203 // Set up width levels
204 StyleHatch.largeMobile = 700;
205
206 /**
207 * Set up sections
208 */
209 var sections = new StyleHatch.Sections();
210 // Fixed theme sections
211 sections.register('promos-section', StyleHatch.PromosSection);
212 sections.register('header-section', StyleHatch.HeaderSection);
213 sections.register('footer-section', StyleHatch.FooterSection);
214 // Index sections
215 sections.register('slideshow-section', StyleHatch.SlideshowSection);
216 sections.register('hero-video-section', StyleHatch.HeroVideoSection);
217 sections.register(
218 'featured-collection-section',
219 StyleHatch.FeaturedCollectionSection
220 );
221 sections.register(
222 'simple-collection-section',
223 StyleHatch.SimpleCollectionSection
224 );
225 sections.register('featured-text-section', StyleHatch.PageSection);
226 sections.register('custom-content-section', StyleHatch.PageSection);
227 sections.register('instagram-section', StyleHatch.InstagramSection);
228 sections.register('featured-blog-section', StyleHatch.GenericSection);
229 sections.register('map', StyleHatch.Maps);
230 // Template sections
231 sections.register('product-template', StyleHatch.Product);
232 sections.register('collection-template', StyleHatch.Collection);
233 sections.register('collection-list-template', StyleHatch.Collection);
234 sections.register('list-collections-template', StyleHatch.ListCollections);
235 sections.register('blog-template', StyleHatch.BlogArticle);
236 sections.register('article-template', StyleHatch.BlogArticle);
237 sections.register('password-template', StyleHatch.Password);
238 sections.register('cart-template', StyleHatch.Cart);
239
240 if (StyleHatch.currencyConverter) {
241 StyleHatch.CurrencyConverter.init();
242 }
243
244 if (StyleHatch.ajaxCartEnable) {
245 StyleHatch.AjaxCart.init();
246 }
247
248 StyleHatch.loginForms();
249 StyleHatch.videoLayout();
250 StyleHatch.initTemplates();
251 };
252
253 /**
254 * Section - Modules
255 * ---------------------------------------------------------------------------
256 * Set up core functionality for fixed global (all template) modules
257 */
258
259 /**
260 * Promos - header
261 */
262 StyleHatch.PromosSection = (function() {
263 function PromosSection(container) {
264 var $container = (this.$container = $(container));
265 var id = $container.attr('data-section-id');
266 StyleHatch.Promos.init();
267 }
268
269 return PromosSection;
270 })();
271 StyleHatch.PromosSection.prototype = $.extend(
272 {},
273 StyleHatch.PromosSection.prototype,
274 {
275 onUnload: function() {
276 StyleHatch.Promos.unload();
277 },
278 onBlockSelect: function(evt) {
279 StyleHatch.Promos.blockSelect(evt);
280 },
281 onBlockDeselect: function(evt) {
282 StyleHatch.Promos.blockDeselect(evt);
283 },
284 }
285 );
286 // Promos Class
287 StyleHatch.Promos = (function() {
288 var selectors = {
289 body: 'body',
290 page: '#page',
291 promos: '#shopify-section-promos',
292 promoBar: 'header.promo-bar',
293 bottomContainer: '.promo-bar-container.bottom',
294 popup: '.promo-popup',
295 };
296
297 var config = {};
298 config = {
299 scrollLock: false,
300 fixToZIndex: 992,
301 hideTimers: [],
302 slideSpeed: 400,
303 };
304
305 var cache = {};
306
307 function init() {
308 cacheSelectors();
309
310 config.scrollLock = cache.$promos.find('>*:first').data('scroll-lock');
311
312 initPromoBars();
313 initPopups();
314
315 StyleHatch.Header.rebuildFixTo();
316 }
317
318 function cacheSelectors() {
319 cache = {
320 $body: $(selectors.body),
321 $promos: $(selectors.promos),
322 $promosContainer: $(selectors.promos).find('>*:first'),
323 $promoBar: $(selectors.promoBar),
324 $bottomContainer: $(selectors.bottomContainer),
325 $popup: $(selectors.popup),
326 };
327 }
328
329 // Core functions
330 /*
331 * Promo bar
332 * Announcement bar
333 */
334 function initPromoBars() {
335 if (cache.$promoBar.length) {
336 // Loop through each
337 cache.$promoBar.each(function() {
338 var $promoBar = $(this);
339 var hideDelay = $promoBar.data('hide-delay');
340 var barPlacement = $promoBar.data('bar-placement');
341
342 // Check for errors
343 var $errors = $promoBar.find('div.errors');
344 if ($errors.length) {
345 $errors.prependTo($promoBar);
346 }
347
348 // Create and group together bottom bars
349 if (barPlacement == 'bottom') {
350 if (!cache.$bottomContainer.length) {
351 cache.$promosContainer.append(
352 '<div class="promo-bar-container bottom"></div>'
353 );
354 cache.$bottomContainer = $(selectors.bottomContainer);
355 }
356 $promoBar.appendTo(cache.$bottomContainer);
357
358 // Calculate height and offset bottom padding
359 cache.$bottomContainer.resize(function() {
360 var bottomHeight = $(this).height() + 'px';
361 cache.$body.css({
362 'margin-bottom': bottomHeight,
363 });
364 });
365 }
366
367 if ($promoBar.hasClass('signup-bar')) {
368 $promoBar.showPopup();
369 }
370
371 // Hide the bar after ms delay (hideDelay)
372 if (hideDelay !== 'no-delay') {
373 config.hideTimers.push(
374 setTimeout(function() {
375 if (!$promoBar.data('pause-hide')) {
376 $promoBar.promoSlideUp();
377 }
378 }, hideDelay)
379 );
380 }
381 });
382
383 destroyFixTo();
384 fixTo();
385 }
386 }
387 function destroyFixTo() {
388 if (cache.$promos.data('fixtoInstance')) {
389 cache.$promos.fixTo('destroy');
390 }
391 }
392 function refreshFixTo() {
393 if (cache.$promos.data('fixtoInstance')) {
394 cache.$promos.fixTo('refresh');
395 }
396 }
397 function fixTo() {
398 if (config.scrollLock) {
399 cache.$promos.fixTo(selectors.page, {
400 zIndex: config.fixToZIndex,
401 });
402 }
403 }
404
405 /*
406 * Popup
407 */
408 function initPopups() {
409 if (cache.$popup.length) {
410 // Loop through each
411 cache.$popup.each(function() {
412 var $popup = $(this);
413 var popupEnable = true,
414 showDelay = $popup.data('show-delay'),
415 homepageLimit = $popup.data('homepage-limit'),
416 visitorLimit = $popup.data('visitor-limit'),
417 isVisitor = $popup.data('visitor'),
418 showFor = $popup.data('show-for');
419
420 // Disable popup if "only enable for visitor" and customer
421 if (visitorLimit == true && isVisitor == false) {
422 popupEnable = false;
423 }
424
425 var $errors = $popup.find('.errors');
426 if ($errors.length) {
427 showDelay = 0;
428 }
429
430 var popupTimeout = setTimeout(function() {
431 var windowWidth = $(window).width();
432 switch (showFor) {
433 case 'mobile':
434 if (windowWidth <= StyleHatch.largeMobile) {
435 $popup.showPopup();
436 }
437 break;
438 case 'desktop':
439 if (windowWidth > StyleHatch.largeMobile) {
440 $popup.showPopup();
441 }
442 break;
443 case 'both':
444 $popup.showPopup();
445 break;
446 }
447 }, showDelay);
448 });
449 }
450 }
451
452 // Prototypes
453 /*
454 * Popups
455 * - showPopup
456 * - hidePopup
457 */
458 $.fn.extend({
459 showPopup: function(force) {
460 var $popup = $(this);
461 var popupEnable = true,
462 showDelay = $popup.data('show-delay'),
463 showAgainDelay = $popup.data('show-again-delay'),
464 homepageLimit = $popup.data('homepage-limit'),
465 visitorLimit = $popup.data('visitor-limit'),
466 isVisitor = $popup.data('visitor'),
467 showFor = $popup.data('show-for'),
468 type = $popup.data('type'),
469 id = $popup.data('id');
470
471 // Disable popup if "only enable for visitor" and customer
472 if (visitorLimit == true && isVisitor == false) {
473 popupEnable = false;
474 }
475
476 // Check to see if the cookie exists
477 var cookieName = 'popup-' + id;
478 if ($.cookie(cookieName)) {
479 popupEnable = false;
480 }
481
482 // Check for homepage limit
483 if (homepageLimit && !cache.$body.hasClass('template-index')) {
484 popupEnable = false;
485 }
486
487 if (window.self !== window.top && type == 'popup') {
488 popupEnable = false;
489 }
490
491 // Always show signup-bar popup if inside the editor
492 if (force) {
493 popupEnable = true;
494 }
495
496 // Check for errors to show the popup anyways
497 var $errors = $popup.find('.errors');
498 var formTags = getQueryString('contact%5Btags%5D');
499
500 if ($errors.length && formTags.includes('popup')) {
501 popupEnable = true;
502 $popup.find('input#email').addClass('errors');
503 }
504
505 if ($errors.length && formTags.includes('signup-bar')) {
506 popupEnable = true;
507 }
508
509 // Remove target=_blank on touch
510 if (Modernizr.touchevents) {
511 $popup.find('form').removeAttr('target');
512 }
513
514 if (popupEnable) {
515 if (type == 'popup') {
516 $.magnificPopup.open({
517 items: {
518 src: $popup,
519 type: 'inline',
520 showCloseBtn: false,
521 },
522 mainClass: 'mfp-slideup',
523 removalDelay: 300,
524 callbacks: {
525 close: function() {
526 $.cookie(cookieName, 'shown', {
527 expires: showAgainDelay,
528 path: '/',
529 });
530 },
531 },
532 });
533 }
534
535 if (type == 'signup-bar') {
536 if (force) {
537 $popup.addClass('visible force');
538 } else {
539 $popup.addClass('visible');
540 }
541 }
542
543 var $close = $popup.find('.icon-text');
544 $close.on('click', function(e) {
545 $popup.hidePopup();
546 e.preventDefault();
547 });
548
549 // On click subscribe button
550 var $form = $popup.find('form');
551 $form.on('submit', function(e) {
552 if (e.target.checkValidity()) {
553 $popup.hidePopup();
554 $(this).submit();
555 } else {
556 return false;
557 }
558 });
559 }
560 },
561 hidePopup: function() {
562 var $promos = $('#shopify-section-promos');
563 var $popup = $(this);
564 var type = $popup.data('type'),
565 id = $popup.data('id');
566
567 if (type == 'popup') {
568 $.magnificPopup.close();
569 }
570
571 if (type == 'signup-bar') {
572 var cookieName = 'popup-' + id;
573
574 $.cookie(cookieName, 'shown', {
575 expires: 60,
576 path: '/',
577 });
578
579 // close all signup bars
580 $('.promo-bar.signup-bar').each(function(i) {
581 $(this).slideUp({
582 duration: 400,
583 progress: function() {
584 StyleHatch.refreshFixTo();
585 },
586 complete: function() {
587 // one final refresh call
588 StyleHatch.refreshFixTo();
589 $(this).removeClass('visible force');
590 },
591 });
592 });
593 }
594 },
595 promoSlideUp: function() {
596 $(this).slideUp({
597 duration: config.slideSpeed,
598 progress: StyleHatch.refreshFixTo,
599 complete: StyleHatch.refreshFixTo,
600 });
601 },
602 promoSlideDown: function() {
603 $(this).slideDown({
604 duration: config.slideSpeed,
605 progress: StyleHatch.refreshFixTo,
606 complete: StyleHatch.refreshFixTo,
607 });
608 },
609 // Simulated versions for the customize theme menu
610 showMockPopup: function() {
611 var $promos = $('#shopify-section-promos');
612 if (!$('.mock-popup-container').length) {
613 $promos
614 .find('>*:first')
615 .append('<div class="mock-popup-container"></div>');
616 }
617 var $mockPopupContainer = $('.mock-popup-container');
618 var $popup = $(this);
619 $popup.appendTo($mockPopupContainer);
620
621 $mockPopupContainer.show();
622 $popup.show();
623 },
624 hideMockPopup: function() {
625 var $mockPopupContainer = $('.mock-popup-container');
626 var $popup = $(this);
627 $mockPopupContainer.hide();
628 $popup.hide();
629 },
630 });
631
632 function blockSelect(evt) {
633 var $block = $('#block-' + evt.detail.blockId);
634 var blockType = $block.data('type');
635
636 // close any open popup
637 $.magnificPopup.close();
638
639 // make sure each block type opens
640 switch (blockType) {
641 case 'announcement-bar':
642 // Promo bars
643 // Always show the bar when the block is selected
644 $block.promoSlideDown();
645 $block.attr('data-pause-hide', true);
646
647 break;
648 case 'popup':
649 $block.showMockPopup();
650 break;
651 case 'signup-bar':
652 $block.showPopup(true);
653 break;
654 }
655 StyleHatch.Header.rebuildFixTo();
656 StyleHatch.refreshFixTo();
657 }
658 function blockDeselect(evt) {
659 var $block = $('#block-' + evt.detail.blockId);
660 var blockType = $block.data('type');
661 var showFor = $block.data('show-for');
662 var windowWidth = $(window).width();
663
664 // make sure each block type closes (if it should)
665 switch (blockType) {
666 case 'announcement-bar':
667 var $promoBar = $block;
668 $promoBar.attr('data-pause-hide', false);
669
670 var hideDelay = $promoBar.data('hide-delay');
671 var barPlacement = $promoBar.data('bar-placement');
672 var homepageLimit = $promoBar.data('homepage-limit');
673
674 // Instantly hide any promo bar that was open for editing only (wrong size)
675 if (showFor == 'desktop' && windowWidth <= StyleHatch.largeMobile) {
676 $block.promoSlideUp();
677 } else if (
678 showFor == 'mobile' &&
679 windowWidth > StyleHatch.largeMobile
680 ) {
681 $block.promoSlideUp();
682 }
683
684 // Hide the bar after ms delay (hideDelay)
685 if (hideDelay !== 'no-delay') {
686 config.hideTimers.push(
687 setTimeout(function() {
688 if (!$promoBar.data('pause-hide')) {
689 $promoBar.promoSlideUp();
690 }
691 }, hideDelay)
692 );
693 }
694 break;
695 case 'popup':
696 $block.hideMockPopup();
697 break;
698
699 case 'signup-bar':
700 if (showFor == 'desktop' && windowWidth <= StyleHatch.largeMobile) {
701 $block.hidePopup();
702 StyleHatch.refreshFixTo();
703 }
704 if (showFor == 'mobile' && windowWidth > StyleHatch.largeMobile) {
705 $block.hidePopup();
706 StyleHatch.refreshFixTo();
707 }
708 break;
709 }
710 StyleHatch.Header.rebuildFixTo();
711 }
712 function unload() {
713 // Clear out timers
714 if (
715 typeof config.hideTimers == 'undefined' ||
716 !(config.hideTimers instanceof Array)
717 ) {
718 config.hideTimers = [];
719 } else {
720 for (var i = 0; i < config.hideTimers.length; i++) {
721 clearTimeout(config.hideTimers[i]);
722 }
723 config.hideTimers.length = 0;
724 }
725
726 // Clean up fixto
727 destroyFixTo();
728 StyleHatch.refreshFixTo();
729
730 // Misc
731 cache.$bottomContainer.remove();
732 $.magnificPopup.close();
733 }
734
735 return {
736 init: init,
737 unload: unload,
738 blockSelect: blockSelect,
739 blockDeselect: blockDeselect,
740 refreshFixTo: refreshFixTo,
741 };
742 })();
743
744 /**
745 * Header
746 */
747 StyleHatch.HeaderSection = (function() {
748 function HeaderSection(container) {
749 var $container = (this.$container = $(container));
750 var id = $container.attr('data-section-id');
751 StyleHatch.Header.init();
752 }
753
754 return HeaderSection;
755 })();
756 StyleHatch.HeaderSection.prototype = $.extend(
757 {},
758 StyleHatch.HeaderSection.prototype,
759 {
760 onUnload: function() {
761 StyleHatch.Header.unload();
762 },
763 }
764 );
765 // Header Class
766 StyleHatch.Header = (function() {
767 var selectors = {
768 htmlBody: 'html, body',
769 body: 'body',
770 page: '#page',
771 section: '#shopify-section-header',
772 promosSection: '#shopify-section-promos',
773 util: 'header.util',
774 header: 'header.site-header',
775 siteNav: 'header.site-header ul.site-nav',
776 menuLink: '.menu-link',
777 menuPanel: '#menu.panel',
778 menuOverlay: '.mobile-menu-overlay',
779 };
780
781 var config = {};
782 config = {
783 blurTimer: {},
784 blurTime: 2000,
785 slideSpeed: 300,
786 // Dropdowns
787 dropdownActiveClass: 'dropdown-hover',
788 subDropdownActiveClass: 'sub-dropdown-hover',
789 };
790
791 var cache = {};
792
793 function init() {
794 cacheSelectors();
795
796 // Util
797 initUtilHeader();
798 bindUtilHeaderEvents();
799 // Header
800 initHeader();
801 bindHeaderEvents();
802
803 rebuildFixTo();
804 }
805
806 function cacheSelectors() {
807 cache = {
808 $htmlBody: $(selectors.htmlBody),
809 $body: $(selectors.body),
810 $page: $(selectors.page),
811 $section: $(selectors.section),
812 $promosSection: $(selectors.promosSection),
813 $util: $(selectors.util),
814 $header: $(selectors.header),
815 $siteNav: $(selectors.siteNav),
816 // Dropdowns
817 $dropdownParent: $(selectors.siteNav).find('li.has-dropdown'),
818 $defaultLink: $(selectors.siteNav).find('> li:not(.has-dropdown)'),
819 $subMenuLinks: $(selectors.siteNav).find('li.has-dropdown a'),
820 $subDropdownParent: $(selectors.siteNav).find('li.has-sub-dropdown'),
821 // Util
822 $search: $(selectors.util).find('.search-wrapper'),
823 $searchLink: $(selectors.util).find('a.search'),
824 $searchClose: $(selectors.util).find('form.search-bar button'),
825 $searchInput: $(selectors.util).find('form.search-bar input'),
826 // Panel menu
827 $menuLink: $(selectors.menuLink),
828 $menuPanel: $(selectors.menuPanel),
829 $menuPanelDropdown: $(selectors.menuPanel).find('li.has-dropdown'),
830 $menuPanelSubDropdown: $(selectors.menuPanel).find(
831 'li.has-sub-dropdown'
832 ),
833 };
834 }
835
836 /*
837 * Util header
838 * ------------------------
839 */
840 function initUtilHeader() {
841 // Add overlay
842 if (!$(selectors.menuOverlay).length) {
843 cache.$section.append('<div class="mobile-menu-overlay"></div>');
844 cache.$menuOverlay = $(selectors.menuOverlay);
845 }
846 }
847 /*
848 * Search open/close
849 */
850 function openSearch() {
851 cache.$search.slideDown({
852 duration: config.slideSpeed,
853 progress: function() {
854 StyleHatch.refreshFixTo();
855 },
856 complete: function() {
857 StyleHatch.refreshFixTo();
858 },
859 });
860 cache.$searchInput.focus();
861 }
862 function closeSearch() {
863 cache.$searchInput.blur();
864 clearTimeout(config.blurTimer);
865 cache.$search.slideUp({
866 duration: config.slideSpeed,
867 progress: function() {
868 StyleHatch.refreshFixTo();
869 },
870 complete: function() {
871 StyleHatch.refreshFixTo();
872 },
873 });
874 }
875 /*
876 * Mobile panel open/close
877 */
878 function togglePanelMenu() {
879 if (cache.$body.hasClass('panel-open')) {
880 closePanelMenu();
881 } else {
882 openPanelMenu();
883 }
884 }
885 function openPanelMenu() {
886 cache.$htmlBody.addClass('panel-open');
887 window.scrollTo(0, 0);
888 cache.$menuPanel.attr('tabindex', '0');
889 cache.$menuPanel.focus();
890 }
891 function closePanelMenu() {
892 cache.$htmlBody.addClass('panel-open-transition');
893 cache.$htmlBody.removeClass('panel-open');
894 cache.$menuPanel.removeAttr('tabindex');
895
896 setTimeout(function() {
897 cache.$htmlBody.removeClass('panel-open-transition');
898 }, 400);
899 }
900 /*
901 * Events
902 */
903 function bindUtilHeaderEvents() {
904 // Search
905 cache.$searchLink.on('click.search', function(e) {
906 openSearch();
907 e.preventDefault();
908 });
909 cache.$searchClose.on('click.search', function(e) {
910 closeSearch();
911 e.preventDefault();
912 });
913 cache.$searchInput.on('blur.search', function(e) {
914 config.blurTimer = setTimeout(closeSearch, config.blurTime);
915 e.preventDefault();
916 });
917
918 // Mobile panel menu
919 cache.$menuLink.on('click.panel', function(e) {
920 togglePanelMenu();
921 e.preventDefault();
922 });
923 cache.$menuOverlay.on('click.panel', function(e) {
924 togglePanelMenu();
925 e.preventDefault();
926 });
927
928 // Collapsible panel navigation (subnav)
929 cache.$menuPanelDropdown.on('click.panelDropdown', function(e) {
930 // Slide up previous one(s)
931 cache.$menuPanelDropdown.find('ul.dropdown').slideUp();
932 cache.$menuPanelDropdown.find('> a').attr('aria-expanded', 'false');
933 cache.$menuPanelDropdown.removeClass('expanded');
934
935 cache.$menuPanelDropdown
936 .find('ul.dropdown')
937 .attr('aria-hidden', 'true');
938 cache.$menuPanelDropdown.find('ul.dropdown a').attr('tabindex', '-1');
939
940 // If it's not open slide down the menu
941 // and don't allow the click
942 if (
943 !$(this)
944 .find('ul.dropdown')
945 .is(':visible')
946 ) {
947 $(this)
948 .find('> a')
949 .attr('aria-expanded', 'true');
950 $(this)
951 .find('ul.dropdown')
952 .slideDown();
953 $(this)
954 .find('ul.dropdown')
955 .attr('aria-hidden', 'false');
956 $(this)
957 .find('ul.dropdown > li > a')
958 .attr('tabindex', '0');
959 $(this).addClass('expanded');
960 }
961 });
962
963 cache.$menuPanelDropdown
964 .find('> a')
965 .on('click.panelDropdown', function(e) {
966 if (
967 !$(this)
968 .closest('li')
969 .hasClass('expanded')
970 ) {
971 e.preventDefault();
972 }
973 });
974 cache.$menuPanelDropdown
975 .find('ul.dropdown li:not(.has-sub-dropdown) a')
976 .on('click.panelDropdown', function(e) {
977 e.stopPropagation();
978 });
979
980 // Collapsible panel navigation (sub-subnav)
981 cache.$menuPanelSubDropdown.on('click.panelDropdown', function(e) {
982 e.stopPropagation();
983 cache.$menuPanelSubDropdown.find('ul.sub-dropdown').slideUp();
984 cache.$menuPanelDropdown.find('> a').attr('aria-expanded', 'false');
985 cache.$menuPanelSubDropdown.removeClass('expanded');
986
987 cache.$menuPanelDropdown
988 .find('ul.sub-dropdown')
989 .attr('aria-hidden', 'true');
990 cache.$menuPanelDropdown
991 .find('ul.sub-dropdown a')
992 .attr('tabindex', '-1');
993
994 // If it's not open slide down the menu
995 // and don't allow the click
996 if (
997 !$(this)
998 .find('ul.sub-dropdown')
999 .is(':visible')
1000 ) {
1001 $(this)
1002 .find('> a')
1003 .attr('aria-expanded', 'true');
1004 $(this)
1005 .find('ul.sub-dropdown')
1006 .slideDown();
1007 $(this)
1008 .find('ul.sub-dropdown')
1009 .attr('aria-hidden', 'false');
1010 $(this)
1011 .find('ul.sub-dropdown > li > a')
1012 .attr('tabindex', '0');
1013 $(this).addClass('expanded');
1014 }
1015 });
1016
1017 cache.$menuPanelSubDropdown
1018 .find('> a')
1019 .on('click.panelDropdown', function(e) {
1020 if (
1021 !$(this)
1022 .closest('li')
1023 .hasClass('expanded')
1024 ) {
1025 e.preventDefault();
1026 }
1027 });
1028 cache.$menuPanelSubDropdown
1029 .find('ul.sub-dropdown a')
1030 .on('click.panelDropdown', function(e) {
1031 e.stopPropagation();
1032 });
1033
1034 // Promo resize
1035 cache.$promosSection.resize(StyleHatch.refreshFixTo);
1036 }
1037 function unbindUtilHeaderEvents() {
1038 // Search
1039 clearTimeout(config.blurTimer);
1040 cache.$searchLink.off('click.search');
1041 cache.$searchClose.off('click.search');
1042 cache.$searchInput.off('blur.search');
1043
1044 // Mobile panel menu
1045 cache.$menuLink.off('click.panel');
1046 //cache.$menuOverlay.off('click.panel');
1047
1048 // Collapsible panel navigation
1049 cache.$menuPanelDropdown.off('click.panelDropdown');
1050 cache.$menuPanelDropdown.find('> a').off('click.panelDropdown');
1051 cache.$menuPanelDropdown
1052 .find('ul.dropdown li:not(.has-sub-dropdown) a')
1053 .off('click.panelDropdown');
1054
1055 cache.$menuPanelSubDropdown.off('click.panelDropdown');
1056 cache.$menuPanelSubDropdown.find('> a').off('click.panelDropdown');
1057 cache.$menuPanelSubDropdown
1058 .find('ul.sub-dropdown a')
1059 .off('click.panelDropdown');
1060
1061 // Promo resize
1062 cache.$promosSection.removeResize(StyleHatch.refreshFixTo);
1063 }
1064
1065 /*
1066 * Header
1067 * ------------------------
1068 */
1069 function initHeader() {
1070 // Adds line breaks to really long subnav text links
1071 cache.$subMenuLinks.each(function() {
1072 var $link = $(this);
1073 var linkText = $link.text();
1074 var linkTextWrapped = wordWrapper(linkText, 24, '<br/>\n');
1075
1076 // Apply wrapped text
1077 // $link.html(linkTextWrapped);
1078 });
1079
1080 // Prevent Safari from reopening menu when browsing back
1081 hideDropdown(cache.$dropdownParent);
1082 }
1083 /*
1084 * Dropdown open / close
1085 */
1086 function showDropdown($el) {
1087 // Hide previous
1088 hideDropdown($('.' + config.dropdownActiveClass));
1089
1090 $el.addClass(config.dropdownActiveClass);
1091
1092 // Accessiblity
1093 $el.find('> a').attr('aria-expanded', 'true');
1094 $el.find('ul.dropdown').attr('aria-hidden', 'false');
1095 $el.find('ul.dropdown > li > a').attr('tabindex', '0');
1096
1097 var $dropdown = $el.find('ul.dropdown');
1098 if (!$dropdown.hasClass('dropdown--mega-menu')) {
1099 $dropdown.css({
1100 left: 'auto',
1101 });
1102 }
1103 var dropdownEnd = $dropdown.offset().left + $dropdown.outerWidth();
1104
1105 // Account for page edge padding
1106 var pageWidth = $(window).width() - 20;
1107
1108 // Without border
1109 var siteWidth = cache.$header.width();
1110 var logoOffset = cache.$header.find('.logo-nav-contain').offset().left;
1111
1112 if (siteWidth + 40 > $(window).width()) {
1113 siteWidth = $(window).width();
1114 logoOffset = -20;
1115 }
1116
1117 pageWidth = siteWidth + logoOffset + 1;
1118
1119 if (dropdownEnd > pageWidth) {
1120 if (!$dropdown.hasClass('dropdown--mega-menu')) {
1121 var rightEdge = '-' + (dropdownEnd - pageWidth) + 'px';
1122 $dropdown.css({
1123 left: rightEdge,
1124 });
1125 }
1126 }
1127
1128 setTimeout(function() {
1129 cache.$body.on('touchstart', function() {
1130 hideDropdown($el);
1131 });
1132 }, 250);
1133 }
1134 function hideDropdown($el) {
1135 $el.removeClass(config.dropdownActiveClass);
1136 cache.$body.off('touchstart');
1137
1138 // Accessiblity
1139 $el.find('> a').attr('aria-expanded', 'false');
1140 $el.find('ul.dropdown').attr('aria-hidden', 'true');
1141 $el.find('ul.dropdown > li > a').attr('tabindex', '-1');
1142 }
1143 // Sub dropdowns
1144 function showSubDropdown($el) {
1145 hideDropdown($('.' + config.subDropdownActiveClass));
1146
1147 $el.addClass(config.subDropdownActiveClass);
1148
1149 // Accessiblity
1150 $el.find('> a').attr('aria-expanded', 'true');
1151 $el.find('ul.sub-dropdown').attr('aria-hidden', 'false');
1152 $el.find('ul.sub-dropdown > li > a').attr('tabindex', '0');
1153
1154 // Check if partially in view
1155 //log($el.find('.sub-dropdown').offset().left, $el.find('.sub-dropdown').width());
1156
1157 // Show subdropdown to the left if there isn't enough room
1158 var dropdownOffsetEdge =
1159 $el.find('.sub-dropdown').offset().left +
1160 $el.find('.sub-dropdown').width();
1161 var windowWidth = $(window).width();
1162 if (dropdownOffsetEdge > windowWidth) {
1163 $el.addClass('alternate-align');
1164 } else {
1165 $el.removeClass('alternate-align');
1166 }
1167 }
1168 function hideSubDropdown($el) {
1169 $el.removeClass(config.subDropdownActiveClass);
1170 $el.removeClass('alternate-align');
1171 // Accessiblity
1172 $el.find('> a').attr('aria-expanded', 'false');
1173 $el.find('ul.sub-dropdown').attr('aria-hidden', 'true');
1174 $el.find('ul.sub-dropdown > li > a').attr('tabindex', '-1');
1175 }
1176 /*
1177 * Events
1178 */
1179 function bindHeaderEvents() {
1180 // Dropdown
1181 cache.$dropdownParent.on(
1182 'mouseenter.dropdown touchstart.dropdown focusin.dropdown',
1183 function(e) {
1184 var $el = $(this);
1185
1186 if (!$el.hasClass(config.dropdownActiveClass)) {
1187 e.preventDefault();
1188 showDropdown($el);
1189 }
1190 }
1191 );
1192 cache.$dropdownParent.on('mouseleave.dropdown', function() {
1193 hideDropdown($(this));
1194 });
1195 cache.$subMenuLinks.on('touchstart.dropdown', function(e) {
1196 // Prevent touchstart on body from firing instead of link
1197 e.stopImmediatePropagation();
1198 });
1199
1200 // Subdropdowns
1201 cache.$subDropdownParent.on(
1202 'mouseenter.subdropdown touchstart.subdropdown focusin.subdropdown',
1203 function(e) {
1204 var $el = $(this);
1205
1206 if (!$el.hasClass(config.subDropdownActiveClass)) {
1207 e.preventDefault();
1208 showSubDropdown($el);
1209 }
1210 }
1211 );
1212 cache.$subDropdownParent.on('mouseleave.subdropdown', function() {
1213 hideSubDropdown($(this));
1214 });
1215 cache.$subDropdownParent.on('touchstart.subdropdown', function(e) {
1216 // Prevent touchstart on body from firing instead of link
1217 e.stopImmediatePropagation();
1218 });
1219
1220 if ($('html').hasClass('touchevents')) {
1221 cache.$subDropdownParent.children('a').on('click', function(e) {
1222 var $el = $(this);
1223
1224 if (!$el.hasClass(config.subDropdownActiveClass)) {
1225 e.preventDefault();
1226 showSubDropdown($el);
1227 }
1228 });
1229 }
1230
1231 // Focus out detect tabbing outside of dropdown or subdropdown
1232 cache.$subMenuLinks.on('focusout.dropdown', function(e) {
1233 if (e.relatedTarget == null) {
1234 hideDropdown($('.' + config.dropdownActiveClass));
1235 } else {
1236 if (
1237 $(e.target).closest('li.has-dropdown')[0] !==
1238 $(e.relatedTarget).closest('li.has-dropdown')[0]
1239 ) {
1240 hideDropdown($('.' + config.dropdownActiveClass));
1241 }
1242 if (
1243 $(e.target).closest('li.has-sub-dropdown')[0] !==
1244 $(e.relatedTarget).closest('li.has-sub-dropdown')[0]
1245 ) {
1246 hideSubDropdown($('.' + config.subDropdownActiveClass));
1247 }
1248 }
1249 });
1250 }
1251 function unbindHeaderEvents() {
1252 // Dropdown
1253 cache.$dropdownParent.off(
1254 'mouseenter.dropdown touchstart.dropdown focusin.dropdown'
1255 );
1256 cache.$dropdownParent.off('mouseleave.dropdown');
1257 cache.$subMenuLinks.off('touchstart.dropdown');
1258
1259 // Subdropdowns
1260 cache.$subDropdownParent.off(
1261 'mouseenter.subdropdown touchstart.subdropdown focusin.subdropdown'
1262 );
1263 cache.$subDropdownParent.off('mouseleave.subdropdown');
1264 cache.$subDropdownParent.off('touchstart.subdropdown');
1265
1266 // Focus out detect tabbing outside of dropdown or subdropdown
1267 cache.$subMenuLinks.off('focusout.dropdown');
1268 }
1269
1270 /*
1271 * fixTo - Complete header
1272 */
1273 function createFixTo() {
1274 var $headerSection = cache.$section;
1275 var $promosSection = cache.$promosSection;
1276 var $header = cache.$header;
1277 var $util = cache.$util;
1278
1279 // Lock the util or header to the top on scroll
1280 var scrollLock = $header.data('scroll-lock');
1281 if (scrollLock == 'util' || scrollLock == 'header') {
1282 var mindElements = '';
1283 if ($promosSection.data('fixtoInstance')) {
1284 mindElements = '#shopify-section-promos';
1285 }
1286 $util.fixTo('#page', {
1287 zIndex: 991,
1288 mind: mindElements,
1289 });
1290 }
1291 if (scrollLock == 'header') {
1292 var mindElements = 'header.util';
1293 if ($promosSection.data('fixtoInstance')) {
1294 mindElements = 'header.util, #shopify-section-promos';
1295 }
1296 $headerSection.fixTo('#page', {
1297 zIndex: 990,
1298 mind: mindElements,
1299 });
1300 $headerSection.resize(function() {
1301 if ($(this).width() <= 700) {
1302 $headerSection.fixTo('stop');
1303 } else {
1304 $headerSection.fixTo('start');
1305 }
1306 });
1307 }
1308 }
1309 function destroyFixTo() {
1310 // Destroy header locks
1311 var $fixToElements = $('header.util, #shopify-section-header').filter(
1312 function() {
1313 return $(this).data('fixtoInstance');
1314 }
1315 );
1316 if ($fixToElements.length) {
1317 $fixToElements.fixTo('destroy');
1318 }
1319 }
1320 function rebuildFixTo() {
1321 cacheSelectors();
1322 destroyFixTo();
1323 createFixTo();
1324 }
1325
1326 function unload() {
1327 // Util
1328 closePanelMenu();
1329 unbindUtilHeaderEvents();
1330 // Header
1331 unbindHeaderEvents();
1332 destroyFixTo();
1333 }
1334
1335 return {
1336 init: init,
1337 unload: unload,
1338 openSearch: openSearch,
1339 closeSearch: closeSearch,
1340 togglePanelMenu: togglePanelMenu,
1341 openPanelMenu: openPanelMenu,
1342 closePanelMenu: closePanelMenu,
1343 rebuildFixTo: rebuildFixTo,
1344 };
1345 })();
1346
1347 /**
1348 * Footer
1349 * Global section
1350 */
1351 StyleHatch.FooterSection = (function() {
1352 function FooterSection(container) {
1353 var $container = (this.$container = $(container));
1354 var id = $container.attr('data-section-id');
1355 StyleHatch.Footer.init($container);
1356 }
1357
1358 return FooterSection;
1359 })();
1360 StyleHatch.FooterSection.prototype = $.extend(
1361 {},
1362 StyleHatch.FooterSection.prototype,
1363 {
1364 onUnload: function(evt) {
1365 StyleHatch.Footer.unload(evt);
1366 },
1367 }
1368 );
1369 // Footer class
1370 StyleHatch.Footer = (function() {
1371 function init($container) {
1372 var $nestedMenu = $container.find('ul.nested-menu');
1373 $nestedMenu.initNestedMenu();
1374 }
1375 function unload(evt) {
1376 $nestedMenu.destroyNestedMenu();
1377 }
1378
1379 return {
1380 init: init,
1381 unload: unload,
1382 };
1383 })();
1384
1385 /**
1386 * Slideshow
1387 */
1388 StyleHatch.SlideshowSection = (function() {
1389 function SlideshowSection(container) {
1390 var $container = (this.$container = $(container));
1391 var id = $container.attr('data-section-id');
1392 StyleHatch.Slideshow.init($container);
1393 }
1394
1395 return SlideshowSection;
1396 })();
1397 StyleHatch.SlideshowSection.prototype = $.extend(
1398 {},
1399 StyleHatch.SlideshowSection.prototype,
1400 {
1401 onUnload: function(evt) {
1402 StyleHatch.Slideshow.unload(evt);
1403 },
1404 onBlockSelect: function(evt) {
1405 StyleHatch.Slideshow.blockSelect(evt);
1406 },
1407 onBlockDeselect: function(evt) {
1408 StyleHatch.Slideshow.blockDeselect(evt);
1409 },
1410 }
1411 );
1412 // Slideshow Class
1413 StyleHatch.Slideshow = (function() {
1414 // Initialization
1415 function init($container) {
1416 var $carousel = $container.find('.slideshow-carousel');
1417
1418 // Preload mobile or desktop slide images based on width
1419 var mobileWidth = 700;
1420 var $slideItem = $carousel.find('.slide__item');
1421 $(window).on('resize', function() {
1422 $slideItem.each(function(i) {
1423 var $responsiveImg;
1424 if ($(window).width() > mobileWidth) {
1425 $responsiveImg = $(this).find('img.slide__image-desktop');
1426 } else {
1427 $responsiveImg = $(this).find('img.slide__image-mobile');
1428 if ($responsiveImg.length < 1) {
1429 $responsiveImg = $(this).find('img.slide__image-desktop');
1430 }
1431 }
1432
1433 if ($responsiveImg.hasClass('lazymanual')) {
1434 $responsiveImg
1435 .attr('src', $responsiveImg.attr('data-preload'))
1436 .removeAttr('data-preload');
1437 $responsiveImg.removeClass('lazymanual').addClass('lazyload');
1438 }
1439 });
1440 });
1441 $(window).trigger('resize');
1442
1443 var flickityOptions = $carousel.data('flickity-options');
1444 // Pass options from data attribute object
1445 $carousel.flickity(flickityOptions);
1446 $carousel
1447 .parent()
1448 .find('.flickity-page-dots.placeholder')
1449 .remove();
1450
1451 // Load YouTube videos
1452 var $videoSlides = $container.find('.slide__item-video');
1453 $videoSlides.each(function() {
1454 var $slideVideo = $(this).find('.slide__item-image');
1455 var videoId = $(this).data('video-id');
1456 var autoplayMobile = $(this).data('mobile-autoplay');
1457 var playsinline = 0;
1458 if (autoplayMobile) {
1459 playsinline = 1;
1460 }
1461
1462 $slideVideo.YTPlayer({
1463 fitToBackground: false,
1464 videoId: videoId,
1465 repeat: true,
1466 mute: true,
1467 playerVars: {
1468 rel: 0,
1469 mute: 1,
1470 playsinline: playsinline,
1471 autoplay: 1,
1472 },
1473 callback: function() {
1474 $(window).trigger('resize');
1475 },
1476 });
1477 });
1478 }
1479
1480 function blockSelect(evt) {
1481 var $block = $('#block-' + evt.detail.blockId);
1482 var $carousel = $block.closest('.slideshow-carousel');
1483 var slideIndex = $block.data('slide-index');
1484 // Pause flickity and select the current block
1485 $carousel.flickity('pausePlayer');
1486 $carousel.flickity('select', slideIndex, true, true);
1487 // Lazyload all images
1488 $carousel
1489 .find('img')
1490 .removeClass('.lazymanual')
1491 .addClass('lazyload');
1492 }
1493 function blockDeselect(evt) {
1494 var $block = $('#block-' + evt.detail.blockId);
1495 var $carousel = $block.closest('.slideshow-carousel');
1496 // Unpause player
1497 $carousel.flickity('unpausePlayer');
1498 }
1499 // Unload
1500 function unload(evt) {
1501 var $section = $('.slideshow-' + evt.detail.sectionId);
1502 var $carousel = $section.find('.slideshow-carousel');
1503 // Destroy flickity to be rebuilt
1504 $carousel.flickity('destroy');
1505
1506 // Destroy YouTube
1507 var $slideVideo = $section.find('.slide__item-video .slide__item-image');
1508 // Destroy
1509 $slideVideo
1510 .removeData('yt-init')
1511 .removeData('ytPlayer')
1512 .removeClass('loaded');
1513
1514 $(window).off('resize.YTplayer' + $slideVideo.ID);
1515 $(window).off('scroll.YTplayer' + $slideVideo.ID);
1516
1517 $slideVideo.$body = null;
1518 $slideVideo.$node = null;
1519 $slideVideo.$YTPlayerString = null;
1520 $slideVideo.player = null;
1521 }
1522
1523 // Public methods
1524 return {
1525 init: init,
1526 unload: unload,
1527 blockSelect: blockSelect,
1528 blockDeselect: blockDeselect,
1529 };
1530 })();
1531
1532 /**
1533 * Hero Video
1534 */
1535 StyleHatch.HeroVideoSection = (function() {
1536 function HeroVideoSection(container) {
1537 var $container = (this.$container = $(container));
1538 var id = $container.attr('data-section-id');
1539
1540 StyleHatch.HeroVideo.init($container);
1541 }
1542
1543 return HeroVideoSection;
1544 })();
1545 StyleHatch.HeroVideoSection.prototype = $.extend(
1546 {},
1547 StyleHatch.HeroVideoSection.prototype,
1548 {
1549 onUnload: function(evt) {
1550 StyleHatch.HeroVideo.unload(evt);
1551 },
1552 }
1553 );
1554 // Slideshow Class
1555 StyleHatch.HeroVideo = (function() {
1556 // Initialization
1557 function init($container) {
1558 var $heroVideo = $container.find('.wrapper');
1559 var videoId = $container.data('video-id');
1560 var autoplayMobile = $container.data('mobile-autoplay');
1561 var playsinline = 0;
1562 if (autoplayMobile) {
1563 playsinline = 1;
1564 }
1565 $heroVideo.removeData('ytPlayer');
1566
1567 $heroVideo.YTPlayer({
1568 fitToBackground: false,
1569 videoId: videoId,
1570 repeat: true,
1571 mute: true,
1572 playerVars: {
1573 rel: 0,
1574 mute: 1,
1575 playsinline: playsinline,
1576 autoplay: 1,
1577 },
1578 });
1579 }
1580
1581 // Unload
1582 function unload(evt) {
1583 var $section = $('.slideshow-' + evt.detail.sectionId);
1584 var $heroVideo = $section.find('.wrapper');
1585 // Destroy
1586 $heroVideo
1587 .removeData('yt-init')
1588 .removeData('ytPlayer')
1589 .removeClass('loaded');
1590 $heroVideo.find('.ytplayer-container .ytplayer-shield').remove();
1591 }
1592
1593 // Public methods
1594 return {
1595 init: init,
1596 unload: unload,
1597 };
1598 })();
1599
1600 /**
1601 * Maps
1602 */
1603 StyleHatch.Maps = (function() {
1604 var config = {
1605 zoom: 14,
1606 };
1607 var apiStatus = null;
1608 var apiKey = null;
1609 var apiKeyReset = false;
1610 var mapsToLoad = [];
1611
1612 var mapStyles = {
1613 standard: [],
1614 silver: [
1615 {
1616 elementType: 'geometry',
1617 stylers: [
1618 {
1619 color: '#f5f5f5',
1620 },
1621 ],
1622 },
1623 {
1624 elementType: 'labels.icon',
1625 stylers: [
1626 {
1627 visibility: 'off',
1628 },
1629 ],
1630 },
1631 {
1632 elementType: 'labels.text.fill',
1633 stylers: [
1634 {
1635 color: '#616161',
1636 },
1637 ],
1638 },
1639 {
1640 elementType: 'labels.text.stroke',
1641 stylers: [
1642 {
1643 color: '#f5f5f5',
1644 },
1645 ],
1646 },
1647 {
1648 featureType: 'administrative.land_parcel',
1649 elementType: 'labels.text.fill',
1650 stylers: [
1651 {
1652 color: '#bdbdbd',
1653 },
1654 ],
1655 },
1656 {
1657 featureType: 'poi',
1658 elementType: 'geometry',
1659 stylers: [
1660 {
1661 color: '#eeeeee',
1662 },
1663 ],
1664 },
1665 {
1666 featureType: 'poi',
1667 elementType: 'labels.text.fill',
1668 stylers: [
1669 {
1670 color: '#757575',
1671 },
1672 ],
1673 },
1674 {
1675 featureType: 'poi.park',
1676 elementType: 'geometry',
1677 stylers: [
1678 {
1679 color: '#e5e5e5',
1680 },
1681 ],
1682 },
1683 {
1684 featureType: 'poi.park',
1685 elementType: 'labels.text.fill',
1686 stylers: [
1687 {
1688 color: '#9e9e9e',
1689 },
1690 ],
1691 },
1692 {
1693 featureType: 'road',
1694 elementType: 'geometry',
1695 stylers: [
1696 {
1697 color: '#ffffff',
1698 },
1699 ],
1700 },
1701 {
1702 featureType: 'road.arterial',
1703 elementType: 'labels.text.fill',
1704 stylers: [
1705 {
1706 color: '#757575',
1707 },
1708 ],
1709 },
1710 {
1711 featureType: 'road.highway',
1712 elementType: 'geometry',
1713 stylers: [
1714 {
1715 color: '#dadada',
1716 },
1717 ],
1718 },
1719 {
1720 featureType: 'road.highway',
1721 elementType: 'labels.text.fill',
1722 stylers: [
1723 {
1724 color: '#616161',
1725 },
1726 ],
1727 },
1728 {
1729 featureType: 'road.local',
1730 elementType: 'labels.text.fill',
1731 stylers: [
1732 {
1733 color: '#9e9e9e',
1734 },
1735 ],
1736 },
1737 {
1738 featureType: 'transit.line',
1739 elementType: 'geometry',
1740 stylers: [
1741 {
1742 color: '#e5e5e5',
1743 },
1744 ],
1745 },
1746 {
1747 featureType: 'transit.station',
1748 elementType: 'geometry',
1749 stylers: [
1750 {
1751 color: '#eeeeee',
1752 },
1753 ],
1754 },
1755 {
1756 featureType: 'water',
1757 elementType: 'geometry',
1758 stylers: [
1759 {
1760 color: '#c9c9c9',
1761 },
1762 ],
1763 },
1764 {
1765 featureType: 'water',
1766 elementType: 'labels.text.fill',
1767 stylers: [
1768 {
1769 color: '#9e9e9e',
1770 },
1771 ],
1772 },
1773 ],
1774 retro: [
1775 {
1776 elementType: 'geometry',
1777 stylers: [
1778 {
1779 color: '#ebe3cd',
1780 },
1781 ],
1782 },
1783 {
1784 elementType: 'labels.text.fill',
1785 stylers: [
1786 {
1787 color: '#523735',
1788 },
1789 ],
1790 },
1791 {
1792 elementType: 'labels.text.stroke',
1793 stylers: [
1794 {
1795 color: '#f5f1e6',
1796 },
1797 ],
1798 },
1799 {
1800 featureType: 'administrative',
1801 elementType: 'geometry.stroke',
1802 stylers: [
1803 {
1804 color: '#c9b2a6',
1805 },
1806 ],
1807 },
1808 {
1809 featureType: 'administrative.land_parcel',
1810 elementType: 'geometry.stroke',
1811 stylers: [
1812 {
1813 color: '#dcd2be',
1814 },
1815 ],
1816 },
1817 {
1818 featureType: 'administrative.land_parcel',
1819 elementType: 'labels.text.fill',
1820 stylers: [
1821 {
1822 color: '#ae9e90',
1823 },
1824 ],
1825 },
1826 {
1827 featureType: 'landscape.natural',
1828 elementType: 'geometry',
1829 stylers: [
1830 {
1831 color: '#dfd2ae',
1832 },
1833 ],
1834 },
1835 {
1836 featureType: 'poi',
1837 elementType: 'geometry',
1838 stylers: [
1839 {
1840 color: '#dfd2ae',
1841 },
1842 ],
1843 },
1844 {
1845 featureType: 'poi',
1846 elementType: 'labels.text.fill',
1847 stylers: [
1848 {
1849 color: '#93817c',
1850 },
1851 ],
1852 },
1853 {
1854 featureType: 'poi.park',
1855 elementType: 'geometry.fill',
1856 stylers: [
1857 {
1858 color: '#a5b076',
1859 },
1860 ],
1861 },
1862 {
1863 featureType: 'poi.park',
1864 elementType: 'labels.text.fill',
1865 stylers: [
1866 {
1867 color: '#447530',
1868 },
1869 ],
1870 },
1871 {
1872 featureType: 'road',
1873 elementType: 'geometry',
1874 stylers: [
1875 {
1876 color: '#f5f1e6',
1877 },
1878 ],
1879 },
1880 {
1881 featureType: 'road.arterial',
1882 elementType: 'geometry',
1883 stylers: [
1884 {
1885 color: '#fdfcf8',
1886 },
1887 ],
1888 },
1889 {
1890 featureType: 'road.highway',
1891 elementType: 'geometry',
1892 stylers: [
1893 {
1894 color: '#f8c967',
1895 },
1896 ],
1897 },
1898 {
1899 featureType: 'road.highway',
1900 elementType: 'geometry.stroke',
1901 stylers: [
1902 {
1903 color: '#e9bc62',
1904 },
1905 ],
1906 },
1907 {
1908 featureType: 'road.highway.controlled_access',
1909 elementType: 'geometry',
1910 stylers: [
1911 {
1912 color: '#e98d58',
1913 },
1914 ],
1915 },
1916 {
1917 featureType: 'road.highway.controlled_access',
1918 elementType: 'geometry.stroke',
1919 stylers: [
1920 {
1921 color: '#db8555',
1922 },
1923 ],
1924 },
1925 {
1926 featureType: 'road.local',
1927 elementType: 'labels.text.fill',
1928 stylers: [
1929 {
1930 color: '#806b63',
1931 },
1932 ],
1933 },
1934 {
1935 featureType: 'transit.line',
1936 elementType: 'geometry',
1937 stylers: [
1938 {
1939 color: '#dfd2ae',
1940 },
1941 ],
1942 },
1943 {
1944 featureType: 'transit.line',
1945 elementType: 'labels.text.fill',
1946 stylers: [
1947 {
1948 color: '#8f7d77',
1949 },
1950 ],
1951 },
1952 {
1953 featureType: 'transit.line',
1954 elementType: 'labels.text.stroke',
1955 stylers: [
1956 {
1957 color: '#ebe3cd',
1958 },
1959 ],
1960 },
1961 {
1962 featureType: 'transit.station',
1963 elementType: 'geometry',
1964 stylers: [
1965 {
1966 color: '#dfd2ae',
1967 },
1968 ],
1969 },
1970 {
1971 featureType: 'water',
1972 elementType: 'geometry.fill',
1973 stylers: [
1974 {
1975 color: '#b9d3c2',
1976 },
1977 ],
1978 },
1979 {
1980 featureType: 'water',
1981 elementType: 'labels.text.fill',
1982 stylers: [
1983 {
1984 color: '#92998d',
1985 },
1986 ],
1987 },
1988 ],
1989 dark: [
1990 {
1991 elementType: 'geometry',
1992 stylers: [
1993 {
1994 color: '#212121',
1995 },
1996 ],
1997 },
1998 {
1999 elementType: 'labels.icon',
2000 stylers: [
2001 {
2002 visibility: 'off',
2003 },
2004 ],
2005 },
2006 {
2007 elementType: 'labels.text.fill',
2008 stylers: [
2009 {
2010 color: '#757575',
2011 },
2012 ],
2013 },
2014 {
2015 elementType: 'labels.text.stroke',
2016 stylers: [
2017 {
2018 color: '#212121',
2019 },
2020 ],
2021 },
2022 {
2023 featureType: 'administrative',
2024 elementType: 'geometry',
2025 stylers: [
2026 {
2027 color: '#757575',
2028 },
2029 ],
2030 },
2031 {
2032 featureType: 'administrative.country',
2033 elementType: 'labels.text.fill',
2034 stylers: [
2035 {
2036 color: '#9e9e9e',
2037 },
2038 ],
2039 },
2040 {
2041 featureType: 'administrative.land_parcel',
2042 stylers: [
2043 {
2044 visibility: 'off',
2045 },
2046 ],
2047 },
2048 {
2049 featureType: 'administrative.locality',
2050 elementType: 'labels.text.fill',
2051 stylers: [
2052 {
2053 color: '#bdbdbd',
2054 },
2055 ],
2056 },
2057 {
2058 featureType: 'poi',
2059 elementType: 'labels.text.fill',
2060 stylers: [
2061 {
2062 color: '#757575',
2063 },
2064 ],
2065 },
2066 {
2067 featureType: 'poi.park',
2068 elementType: 'geometry',
2069 stylers: [
2070 {
2071 color: '#181818',
2072 },
2073 ],
2074 },
2075 {
2076 featureType: 'poi.park',
2077 elementType: 'labels.text.fill',
2078 stylers: [
2079 {
2080 color: '#616161',
2081 },
2082 ],
2083 },
2084 {
2085 featureType: 'poi.park',
2086 elementType: 'labels.text.stroke',
2087 stylers: [
2088 {
2089 color: '#1b1b1b',
2090 },
2091 ],
2092 },
2093 {
2094 featureType: 'road',
2095 elementType: 'geometry.fill',
2096 stylers: [
2097 {
2098 color: '#2c2c2c',
2099 },
2100 ],
2101 },
2102 {
2103 featureType: 'road',
2104 elementType: 'labels.text.fill',
2105 stylers: [
2106 {
2107 color: '#8a8a8a',
2108 },
2109 ],
2110 },
2111 {
2112 featureType: 'road.arterial',
2113 elementType: 'geometry',
2114 stylers: [
2115 {
2116 color: '#373737',
2117 },
2118 ],
2119 },
2120 {
2121 featureType: 'road.highway',
2122 elementType: 'geometry',
2123 stylers: [
2124 {
2125 color: '#3c3c3c',
2126 },
2127 ],
2128 },
2129 {
2130 featureType: 'road.highway.controlled_access',
2131 elementType: 'geometry',
2132 stylers: [
2133 {
2134 color: '#4e4e4e',
2135 },
2136 ],
2137 },
2138 {
2139 featureType: 'road.local',
2140 elementType: 'labels.text.fill',
2141 stylers: [
2142 {
2143 color: '#616161',
2144 },
2145 ],
2146 },
2147 {
2148 featureType: 'transit',
2149 elementType: 'labels.text.fill',
2150 stylers: [
2151 {
2152 color: '#757575',
2153 },
2154 ],
2155 },
2156 {
2157 featureType: 'water',
2158 elementType: 'geometry',
2159 stylers: [
2160 {
2161 color: '#000000',
2162 },
2163 ],
2164 },
2165 {
2166 featureType: 'water',
2167 elementType: 'labels.text.fill',
2168 stylers: [
2169 {
2170 color: '#3d3d3d',
2171 },
2172 ],
2173 },
2174 ],
2175 night: [
2176 {
2177 elementType: 'geometry',
2178 stylers: [
2179 {
2180 color: '#242f3e',
2181 },
2182 ],
2183 },
2184 {
2185 elementType: 'labels.text.fill',
2186 stylers: [
2187 {
2188 color: '#746855',
2189 },
2190 ],
2191 },
2192 {
2193 elementType: 'labels.text.stroke',
2194 stylers: [
2195 {
2196 color: '#242f3e',
2197 },
2198 ],
2199 },
2200 {
2201 featureType: 'administrative.locality',
2202 elementType: 'labels.text.fill',
2203 stylers: [
2204 {
2205 color: '#d59563',
2206 },
2207 ],
2208 },
2209 {
2210 featureType: 'poi',
2211 elementType: 'labels.text.fill',
2212 stylers: [
2213 {
2214 color: '#d59563',
2215 },
2216 ],
2217 },
2218 {
2219 featureType: 'poi.park',
2220 elementType: 'geometry',
2221 stylers: [
2222 {
2223 color: '#263c3f',
2224 },
2225 ],
2226 },
2227 {
2228 featureType: 'poi.park',
2229 elementType: 'labels.text.fill',
2230 stylers: [
2231 {
2232 color: '#6b9a76',
2233 },
2234 ],
2235 },
2236 {
2237 featureType: 'road',
2238 elementType: 'geometry',
2239 stylers: [
2240 {
2241 color: '#38414e',
2242 },
2243 ],
2244 },
2245 {
2246 featureType: 'road',
2247 elementType: 'geometry.stroke',
2248 stylers: [
2249 {
2250 color: '#212a37',
2251 },
2252 ],
2253 },
2254 {
2255 featureType: 'road',
2256 elementType: 'labels.text.fill',
2257 stylers: [
2258 {
2259 color: '#9ca5b3',
2260 },
2261 ],
2262 },
2263 {
2264 featureType: 'road.highway',
2265 elementType: 'geometry',
2266 stylers: [
2267 {
2268 color: '#746855',
2269 },
2270 ],
2271 },
2272 {
2273 featureType: 'road.highway',
2274 elementType: 'geometry.stroke',
2275 stylers: [
2276 {
2277 color: '#1f2835',
2278 },
2279 ],
2280 },
2281 {
2282 featureType: 'road.highway',
2283 elementType: 'labels.text.fill',
2284 stylers: [
2285 {
2286 color: '#f3d19c',
2287 },
2288 ],
2289 },
2290 {
2291 featureType: 'transit',
2292 elementType: 'geometry',
2293 stylers: [
2294 {
2295 color: '#2f3948',
2296 },
2297 ],
2298 },
2299 {
2300 featureType: 'transit.station',
2301 elementType: 'labels.text.fill',
2302 stylers: [
2303 {
2304 color: '#d59563',
2305 },
2306 ],
2307 },
2308 {
2309 featureType: 'water',
2310 elementType: 'geometry',
2311 stylers: [
2312 {
2313 color: '#17263c',
2314 },
2315 ],
2316 },
2317 {
2318 featureType: 'water',
2319 elementType: 'labels.text.fill',
2320 stylers: [
2321 {
2322 color: '#515c6d',
2323 },
2324 ],
2325 },
2326 {
2327 featureType: 'water',
2328 elementType: 'labels.text.stroke',
2329 stylers: [
2330 {
2331 color: '#17263c',
2332 },
2333 ],
2334 },
2335 ],
2336 aubergine: [
2337 {
2338 elementType: 'geometry',
2339 stylers: [
2340 {
2341 color: '#1d2c4d',
2342 },
2343 ],
2344 },
2345 {
2346 elementType: 'labels.text.fill',
2347 stylers: [
2348 {
2349 color: '#8ec3b9',
2350 },
2351 ],
2352 },
2353 {
2354 elementType: 'labels.text.stroke',
2355 stylers: [
2356 {
2357 color: '#1a3646',
2358 },
2359 ],
2360 },
2361 {
2362 featureType: 'administrative.country',
2363 elementType: 'geometry.stroke',
2364 stylers: [
2365 {
2366 color: '#4b6878',
2367 },
2368 ],
2369 },
2370 {
2371 featureType: 'administrative.land_parcel',
2372 elementType: 'labels.text.fill',
2373 stylers: [
2374 {
2375 color: '#64779e',
2376 },
2377 ],
2378 },
2379 {
2380 featureType: 'administrative.province',
2381 elementType: 'geometry.stroke',
2382 stylers: [
2383 {
2384 color: '#4b6878',
2385 },
2386 ],
2387 },
2388 {
2389 featureType: 'landscape.man_made',
2390 elementType: 'geometry.stroke',
2391 stylers: [
2392 {
2393 color: '#334e87',
2394 },
2395 ],
2396 },
2397 {
2398 featureType: 'landscape.natural',
2399 elementType: 'geometry',
2400 stylers: [
2401 {
2402 color: '#023e58',
2403 },
2404 ],
2405 },
2406 {
2407 featureType: 'poi',
2408 elementType: 'geometry',
2409 stylers: [
2410 {
2411 color: '#283d6a',
2412 },
2413 ],
2414 },
2415 {
2416 featureType: 'poi',
2417 elementType: 'labels.text.fill',
2418 stylers: [
2419 {
2420 color: '#6f9ba5',
2421 },
2422 ],
2423 },
2424 {
2425 featureType: 'poi',
2426 elementType: 'labels.text.stroke',
2427 stylers: [
2428 {
2429 color: '#1d2c4d',
2430 },
2431 ],
2432 },
2433 {
2434 featureType: 'poi.park',
2435 elementType: 'geometry.fill',
2436 stylers: [
2437 {
2438 color: '#023e58',
2439 },
2440 ],
2441 },
2442 {
2443 featureType: 'poi.park',
2444 elementType: 'labels.text.fill',
2445 stylers: [
2446 {
2447 color: '#3C7680',
2448 },
2449 ],
2450 },
2451 {
2452 featureType: 'road',
2453 elementType: 'geometry',
2454 stylers: [
2455 {
2456 color: '#304a7d',
2457 },
2458 ],
2459 },
2460 {
2461 featureType: 'road',
2462 elementType: 'labels.text.fill',
2463 stylers: [
2464 {
2465 color: '#98a5be',
2466 },
2467 ],
2468 },
2469 {
2470 featureType: 'road',
2471 elementType: 'labels.text.stroke',
2472 stylers: [
2473 {
2474 color: '#1d2c4d',
2475 },
2476 ],
2477 },
2478 {
2479 featureType: 'road.highway',
2480 elementType: 'geometry',
2481 stylers: [
2482 {
2483 color: '#2c6675',
2484 },
2485 ],
2486 },
2487 {
2488 featureType: 'road.highway',
2489 elementType: 'geometry.stroke',
2490 stylers: [
2491 {
2492 color: '#255763',
2493 },
2494 ],
2495 },
2496 {
2497 featureType: 'road.highway',
2498 elementType: 'labels.text.fill',
2499 stylers: [
2500 {
2501 color: '#b0d5ce',
2502 },
2503 ],
2504 },
2505 {
2506 featureType: 'road.highway',
2507 elementType: 'labels.text.stroke',
2508 stylers: [
2509 {
2510 color: '#023e58',
2511 },
2512 ],
2513 },
2514 {
2515 featureType: 'transit',
2516 elementType: 'labels.text.fill',
2517 stylers: [
2518 {
2519 color: '#98a5be',
2520 },
2521 ],
2522 },
2523 {
2524 featureType: 'transit',
2525 elementType: 'labels.text.stroke',
2526 stylers: [
2527 {
2528 color: '#1d2c4d',
2529 },
2530 ],
2531 },
2532 {
2533 featureType: 'transit.line',
2534 elementType: 'geometry.fill',
2535 stylers: [
2536 {
2537 color: '#283d6a',
2538 },
2539 ],
2540 },
2541 {
2542 featureType: 'transit.station',
2543 elementType: 'geometry',
2544 stylers: [
2545 {
2546 color: '#3a4762',
2547 },
2548 ],
2549 },
2550 {
2551 featureType: 'water',
2552 elementType: 'geometry',
2553 stylers: [
2554 {
2555 color: '#0e1626',
2556 },
2557 ],
2558 },
2559 {
2560 featureType: 'water',
2561 elementType: 'labels.text.fill',
2562 stylers: [
2563 {
2564 color: '#4e6d70',
2565 },
2566 ],
2567 },
2568 ],
2569 };
2570
2571 var errors = {
2572 addressNoResults: StyleHatch.Strings.addressNoResults,
2573 addressQueryLimit: StyleHatch.Strings.addressQueryLimit,
2574 addressError: StyleHatch.Strings.addressError,
2575 authError: StyleHatch.Strings.authError,
2576 };
2577
2578 var selectors = {
2579 section: '[data-section-type="maps"]',
2580 map: '[data-map]',
2581 mapOverlay: '[data-map-overlay]',
2582 };
2583
2584 var classes = {
2585 mapError: 'map-selection--load-error',
2586 errorMsg: 'map-section__errors errors text-center',
2587 };
2588
2589 // Global Google function for auth errors
2590 window.gm_authFailure = function() {
2591 if (!Shopify.designMode) {
2592 log('Google Maps authentication error', window.google, apiStatus);
2593 return;
2594 }
2595
2596 $(selectors.section).addClass(classes.mapError);
2597 $(selectors.map).remove();
2598 $(selectors.mapOverlay).after(
2599 '<div class="' +
2600 classes.errorMsg +
2601 '">' +
2602 StyleHatch.Strings.authError +
2603 '</div>'
2604 );
2605 };
2606
2607 function Map(container) {
2608 this.$container = $(container);
2609 this.$map = this.$container.find(selectors.map);
2610 this.key = this.$map.data('api-key');
2611
2612 if (this.key != apiKey) {
2613 apiKey = this.key;
2614 apiStatus = null;
2615 apiKeyReset = true;
2616 }
2617
2618 if (typeof this.key === 'undefined') {
2619 return;
2620 }
2621
2622 if (apiStatus === 'loaded') {
2623 this.createMap();
2624 } else {
2625 mapsToLoad.push(this);
2626
2627 if (apiStatus !== 'loading') {
2628 apiStatus = 'loading';
2629 if (typeof window.google === 'undefined' || apiKeyReset) {
2630 $.getScript(
2631 'https://maps.googleapis.com/maps/api/js?key=' + this.key
2632 ).then(function() {
2633 apiStatus = 'loaded';
2634 apiKeyReset = false;
2635 initAllMaps();
2636 });
2637 }
2638 }
2639 }
2640 }
2641
2642 function initAllMaps() {
2643 $.each(mapsToLoad, function(index, instance) {
2644 instance.createMap();
2645 });
2646 }
2647
2648 function geolocate($map) {
2649 var deferred = $.Deferred();
2650 var geocoder = new google.maps.Geocoder();
2651 var address = $map.data('address-setting');
2652
2653 geocoder.geocode({address: address}, function(results, status) {
2654 if (status !== google.maps.GeocoderStatus.OK) {
2655 deferred.reject(status);
2656 }
2657
2658 deferred.resolve(results);
2659 });
2660
2661 return deferred;
2662 }
2663
2664 Map.prototype = $.extend({}, Map.prototype, {
2665 createMap: function() {
2666 var $map = this.$map;
2667 var mapStyle = $map.data('map-style');
2668
2669 return geolocate($map)
2670 .then(
2671 function(results) {
2672 var mapOptions = {
2673 zoom: config.zoom,
2674 center: results[0].geometry.location,
2675 draggable: false,
2676 clickableIcons: false,
2677 scrollwheel: false,
2678 disableDoubleClickZoom: true,
2679 disableDefaultUI: true,
2680 styles: mapStyles[mapStyle],
2681 };
2682
2683 var map = (this.map = new google.maps.Map($map[0], mapOptions));
2684 var center = (this.center = map.getCenter());
2685
2686 //eslint-disable-next-line no-unused-vars
2687 var marker = new google.maps.Marker({
2688 map: map,
2689 position: map.getCenter(),
2690 });
2691
2692 google.maps.event.addDomListener(
2693 window,
2694 'resize',
2695 slate.utils.debounce(function() {
2696 google.maps.event.trigger(map, 'resize');
2697 map.setCenter(center);
2698 $map.removeAttr('style');
2699 }, 250)
2700 );
2701 }.bind(this)
2702 )
2703 .fail(function() {
2704 var errorMessage;
2705
2706 switch (status) {
2707 case 'ZERO_RESULTS':
2708 errorMessage = errors.addressNoResults;
2709 break;
2710 case 'OVER_QUERY_LIMIT':
2711 errorMessage = errors.addresQueryLimit;
2712 break;
2713 case 'REQUEST_DENIED':
2714 errorMessage = errors.authError;
2715 break;
2716 default:
2717 errorMessage = errors.addressError;
2718 break;
2719 }
2720
2721 // Show errors only to merchant in the editor.
2722 if (Shopify.designMode) {
2723 $map
2724 .parent()
2725 .addClass(classes.mapError)
2726 .append(
2727 '<div class="' +
2728 classes.errorMsg +
2729 '">' +
2730 errorMessage +
2731 '</div>'
2732 );
2733 }
2734 });
2735 },
2736
2737 onUnload: function() {
2738 if (this.$map.length === 0) {
2739 return;
2740 }
2741 google.maps.event.clearListeners(this.map, 'resize');
2742 },
2743 });
2744
2745 return Map;
2746 })();
2747
2748 /**
2749 * Page Section - any section that loads pages
2750 */
2751 StyleHatch.PageSection = (function() {
2752 function PageSection(container) {
2753 var $container = (this.$container = $(container));
2754 var id = $container.attr('data-section-id');
2755 StyleHatch.Page.init($container);
2756 }
2757
2758 return PageSection;
2759 })();
2760 // Featured Text Class
2761 StyleHatch.Page = (function() {
2762 // Initialization
2763 function init($container) {
2764 // Apply fitVids
2765 $container.fitVids();
2766 // Possibly check for oembed data
2767 }
2768
2769 // Unload
2770 function unload(evt) {
2771 //log('unload Page');
2772 }
2773
2774 // Public methods
2775 return {
2776 init: init,
2777 unload: unload,
2778 };
2779 })();
2780
2781 /**
2782 * Sections - Collection
2783 * ---------------------------------------------------------------------------
2784 * Feature collection
2785 */
2786 StyleHatch.FeaturedCollectionSection = (function() {
2787 function FeaturedCollectionSection(container) {
2788 var $container = (this.$container = $(container));
2789 var id = $container.attr('data-section-id');
2790 $container.productBox();
2791
2792 if (StyleHatch.currencyConverter) {
2793 StyleHatch.CurrencyConverter.init();
2794 }
2795 }
2796
2797 return FeaturedCollectionSection;
2798 })();
2799 StyleHatch.FeaturedCollectionSection.prototype = $.extend(
2800 {},
2801 StyleHatch.FeaturedCollectionSection.prototype,
2802 {
2803 onUnload: function(evt) {
2804 var $container = $('#section-' + evt.detail.sectionId);
2805 var id = $container.attr('data-section-id');
2806 $container.destroyProductBox();
2807 },
2808 }
2809 );
2810
2811 StyleHatch.SimpleCollectionSection = (function() {
2812 function SimpleCollectionSection(container) {
2813 var $container = (this.$container = $(container));
2814 var id = $container.attr('data-section-id');
2815 $container.productBox();
2816
2817 if (StyleHatch.currencyConverter) {
2818 StyleHatch.CurrencyConverter.init();
2819 }
2820 }
2821
2822 return SimpleCollectionSection;
2823 })();
2824 StyleHatch.SimpleCollectionSection.prototype = $.extend(
2825 {},
2826 StyleHatch.SimpleCollectionSection.prototype,
2827 {
2828 onUnload: function(evt) {
2829 var $container = $('#section-' + evt.detail.sectionId);
2830 var id = $container.attr('data-section-id');
2831
2832 $container.destroyProductBox();
2833 },
2834 }
2835 );
2836
2837 /**
2838 * Instagram - instagram-section
2839 */
2840
2841 StyleHatch.instagrams = {};
2842 StyleHatch.InstagramSection = (function() {
2843 function InstagramSection(container) {
2844 var $container = (this.$container = $(container));
2845 var id = $container.attr('data-section-id');
2846 var instagram = (this.instagram = '#Instagram-' + id);
2847
2848 // Only support IE9 and above
2849 if ($('html').hasClass('lt-ie9')) {
2850 return false;
2851 }
2852
2853 var $shotContainer = $container.find('.instagram-container');
2854 var $profileLink = $container.find('header a.button, footer a.button');
2855 var imageCount = $container.data('image-count');
2856 var accessToken = $container.data('instagram-access-token');
2857 var apiURL =
2858 'https://api.instagram.com/v1/users/self/media/recent/?access_token=' +
2859 accessToken +
2860 '&count=20';
2861 // Set how long the localStorage is valid for 12 hours
2862 var expireTime = 1000 * 60 * 60 * 12; // ms * s * m * 12 hours
2863
2864 // Helper Text
2865 var addAccessToken = StyleHatch.Strings.instagramAddToken;
2866 var invalidAccessToken = StyleHatch.Strings.instagramInvalidToken;
2867 var rateLimitAccessToken = StyleHatch.Strings.instagramRateLimitToken;
2868
2869 // AJAX call to load Instagram API data
2870 var getData = function() {
2871 // Check if access token exists
2872 if (accessToken) {
2873 $.ajax({
2874 url: apiURL,
2875 dataType: 'jsonp',
2876 timeout: 5000,
2877 success: function(data) {
2878 switch (data.meta.code) {
2879 case 400:
2880 if (Shopify.designMode) {
2881 $shotContainer.attr('data-helper-text', invalidAccessToken);
2882 storeWithExpiration.set(accessToken, data, expireTime);
2883 $container.show();
2884 }
2885 break;
2886 case 429:
2887 if (Shopify.designMode) {
2888 $shotContainer.attr(
2889 'data-helper-text',
2890 rateLimitAccessToken
2891 );
2892 $container.show();
2893 }
2894 break;
2895 default:
2896 loadImages(data);
2897 storeWithExpiration.set(accessToken, data, expireTime);
2898 }
2899 },
2900 });
2901 } else {
2902 if (Shopify.designMode) {
2903 // Show helper with details of adding token
2904 $shotContainer.attr('data-helper-text', addAccessToken);
2905 $container.show();
2906 }
2907 }
2908 };
2909
2910 // Load in all the recent media in the Instagram data
2911 var loadImages = function(data) {
2912 // Check to ensure valid data
2913 if (data.data) {
2914 if (data.data.length < imageCount) {
2915 var dataCount = data.data.length;
2916 var settingsCount = imageCount;
2917 var lastIndex = settingsCount - (settingsCount - dataCount);
2918 imageCount = data.data.length;
2919
2920 // Remove the extra holders
2921 $container.find('.box').each(function(i) {
2922 if (i >= lastIndex) {
2923 $(this).hide();
2924 }
2925 });
2926 }
2927 for (var i = 0; i < imageCount; i++) {
2928 var images = data.data[i].images,
2929 // Thumbnail
2930 thumbnail = images.thumbnail.url,
2931 thumbnailWidth = images.thumbnail.width,
2932 // Low Resolution
2933 low_resolution = images.low_resolution.url,
2934 low_resolutionWidth = images.low_resolution.width,
2935 // Standard Resolution
2936 standard_resolution = images.standard_resolution.url,
2937 standard_resolutionWidth = images.standard_resolution.width;
2938
2939 var link = data.data[i].link;
2940 var likes = data.data[i].likes.count;
2941 var comments = data.data[i].comments.count;
2942
2943 var caption = '';
2944 if (data.data[i].caption) {
2945 caption = data.data[i].caption.text;
2946 }
2947
2948 var $shot = $container.find('.box-' + i);
2949 var $shotImageContainer = $shot.find('figure > a');
2950 var $shotLinks = $shot.find('figure > a, li > a');
2951 var $shotCaption = $shot.find('figcaption > p');
2952 var $shotLikes = $shot.find('a.likes span.label');
2953 var $shotComments = $shot.find('a.comments span.label');
2954
2955 // Add image
2956 $shotImageContainer.html('<div class="card__image lazyload">');
2957 var $shotImage = $shotImageContainer.find('.card__image');
2958 $shotImage.attr('data-sizes', 'auto');
2959 $shotImage.attr(
2960 'data-bgset',
2961 thumbnail +
2962 ' ' +
2963 thumbnailWidth +
2964 'w, ' +
2965 low_resolution +
2966 ' ' +
2967 low_resolutionWidth +
2968 'w, ' +
2969 standard_resolution +
2970 ' ' +
2971 standard_resolutionWidth +
2972 'w'
2973 );
2974
2975 // Set shot data
2976 $shotLinks.attr('href', link);
2977 $shotCaption.html(caption);
2978 $shotLikes.text(likes);
2979 $shotComments.text(comments);
2980 }
2981 var instagramFeed =
2982 'https://www.instagram.com/' + data.data[0].user.username;
2983 $profileLink.attr('href', instagramFeed);
2984 $profileLink.attr('target', '_blank');
2985
2986 $container.show();
2987 $shotContainer.addClass('loaded');
2988 }
2989 };
2990
2991 // localStorage - check to see if the api data exists and is current
2992 if (storeWithExpiration.get(accessToken) && !Shopify.designMode) {
2993 var data = storeWithExpiration.get(accessToken);
2994 loadImages(data);
2995 } else {
2996 getData();
2997 }
2998
2999 StyleHatch.instagrams[instagram] = this;
3000 //$container.show();
3001 }
3002
3003 return InstagramSection;
3004 })();
3005 StyleHatch.InstagramSection.prototype = $.extend(
3006 {},
3007 StyleHatch.InstagramSection.prototype,
3008 {
3009 onUnload: function() {
3010 delete StyleHatch.instagrams[this.instagram];
3011 },
3012 }
3013 );
3014
3015 /**
3016 * GenericSection
3017 */
3018 StyleHatch.GenericSection = (function() {
3019 function GenericSection(container) {
3020 var $container = (this.$container = $(container));
3021 var id = $container.attr('data-section-id');
3022
3023 StyleHatch.cacheSelectors();
3024 }
3025
3026 return GenericSection;
3027 })();
3028
3029 /**
3030 * Section - Templates
3031 * ---------------------------------------------------------------------------
3032 * Set up core functionality for template based sections
3033 */
3034
3035 // Product Class
3036 StyleHatch.Product = (function() {
3037 function Product(container) {
3038 var $container = (this.$container = $(container));
3039 var sectionId = $container.attr('data-section-id');
3040
3041 this.settings = {
3042 // Breakpoints from src/stylesheets/global/variables.scss.liquid
3043 enableHistoryState: $container.data('enable-history-state') || false,
3044 enableSwatch: $container.data('enable-swatch') || false,
3045 imageSize: '394x',
3046 imageZoomSize: null,
3047 namespace: '.product-' + sectionId,
3048 sectionId: sectionId,
3049 zoomEnabled: false,
3050 lightboxEnabled: false,
3051 productImageLightboxData: [],
3052 };
3053
3054 this.selectors = {
3055 addToCartForm: '#AddToCartForm-' + sectionId,
3056 addToCart: '#AddToCart-' + sectionId,
3057 addToCartText: '#AddToCartText-' + sectionId,
3058 comparePrice: '#ComparePrice-' + sectionId,
3059 originalPrice: '#ProductPrice-' + sectionId,
3060 SKU: '.variant-sku',
3061 originalSelectorId: '#ProductSelect-' + sectionId,
3062 productFeaturedImage: '#FeaturedImage-' + sectionId,
3063 productImageWrap: '.featured-container-' + sectionId,
3064 productPrices: '.product-single__price-' + sectionId,
3065 productThumbImages: '.product-single__thumbnail--' + sectionId,
3066 productPhoto: '#ProductPhoto-' + sectionId,
3067 productImage: '#ProductImage-' + sectionId,
3068 productThumbs: '#ProductThumbs-' + sectionId,
3069 quantityWrap: '.quantity-' + sectionId,
3070 quantity: '.quantity-select-' + sectionId,
3071 cartError: '.cart-error-' + sectionId,
3072 singleOptionSelector: '.single-option-selector-' + sectionId,
3073 cartButtons: '#CartButtons-' + sectionId,
3074 paymentButtonContainer: '#PaymentButtonContainer-' + sectionId,
3075 productSizeGuideLink: 'a.product-size-guide-' + sectionId,
3076 productSizeGuideContent: '#product-size-guide-content-' + sectionId,
3077 };
3078
3079 this._initSlider();
3080
3081 // Stop parsing if we don't have the product json script tag when loading
3082 // section in the Theme Editor
3083 if (!$('#ProductJson-' + sectionId).html()) {
3084 return;
3085 }
3086
3087 this.productSingleObject = JSON.parse(
3088 document.getElementById('ProductJson-' + sectionId).innerHTML
3089 );
3090
3091 this.settings.zoomEnabled = $(this.selectors.productImageWrap).hasClass(
3092 'featured-zoom'
3093 );
3094 if (Modernizr.objectfit) {
3095 this.settings.lightboxEnabled = $(this.selectors.productImageWrap).data(
3096 'lightbox'
3097 );
3098 }
3099 $container.productBox();
3100
3101 if (StyleHatch.currencyConverter) {
3102 StyleHatch.CurrencyConverter.init();
3103 }
3104
3105 this._initVariants();
3106 this._initQuanitySelect();
3107
3108 if ($(this.selectors.productSizeGuideLink).length) {
3109 $(this.selectors.productSizeGuideLink).magnificPopup({
3110 items: {
3111 src: $(this.selectors.productSizeGuideContent),
3112 type: 'inline',
3113 },
3114 });
3115 }
3116 }
3117
3118 Product.prototype = $.extend({}, Product.prototype, {
3119 _initVariants: function() {
3120 var options = {
3121 $container: this.$container,
3122 enableHistoryState:
3123 this.$container.data('enable-history-state') || false,
3124 enableSwatch: this.$container.data('enable-swatch'),
3125 singleOptionSelector: this.selectors.singleOptionSelector,
3126 originalSelectorId: this.selectors.originalSelectorId,
3127 product: this.productSingleObject,
3128 };
3129 this.optionsMap = {};
3130
3131 this.variants = new slate.Variants(options);
3132
3133 this.$container.on(
3134 'variantChange' + this.settings.namespace,
3135 this._updateAddToCart.bind(this)
3136 );
3137 this.$container.on(
3138 'variantImageChange' + this.settings.namespace,
3139 this._updateImages.bind(this)
3140 );
3141 this.$container.on(
3142 'variantPriceChange' + this.settings.namespace,
3143 this._updatePrice.bind(this)
3144 );
3145 this.$container.on(
3146 'variantSKUChange' + this.settings.namespace,
3147 this._updateSKU.bind(this)
3148 );
3149
3150 if (options.enableSwatch) {
3151 this._linkOptionSelectors(this.productSingleObject);
3152 this.$container.on(
3153 'variantChange' + this.settings.namespace,
3154 this._updateSwatches.bind(this)
3155 );
3156 }
3157 },
3158
3159 _initQuanitySelect: function() {
3160 // Quantity Selector
3161 var $quantitySelect = $(this.selectors.quantity);
3162 $quantitySelect.each(function() {
3163 var $el = $(this);
3164 var $quantityDown = $el.find('.adjust-minus');
3165 var $quantityUp = $el.find('.adjust-plus');
3166 var $quantity = $el.find('input.quantity');
3167
3168 var quantity = $quantity.val();
3169
3170 $quantityDown.on('click', function(e) {
3171 quantity = $quantity.val();
3172 if (quantity > 1) {
3173 quantity--;
3174 $quantity.val(quantity);
3175 }
3176 e.preventDefault();
3177 });
3178
3179 $quantityUp.on('click', function(e) {
3180 quantity = $quantity.val();
3181 quantity++;
3182 $quantity.val(quantity);
3183
3184 e.preventDefault();
3185 });
3186 });
3187 },
3188
3189 _initSlider: function() {
3190 var $imageSlider = this.$container.find('.product-image--slider');
3191 if ($imageSlider.length) {
3192 var sliderOptions = $imageSlider.data('flickity-options');
3193 var enabled = $imageSlider.data('slider-enabled');
3194 var zoomEnabled = $imageSlider.data('zoom');
3195 var lightboxEnabled = $imageSlider.data('lightbox');
3196 var $initial = $imageSlider.find('[data-initial-image]');
3197 var initialIndex = $initial
3198 .parent()
3199 .find('.product-image--cell')
3200 .index($initial);
3201 sliderOptions.initialIndex = initialIndex;
3202 if (!enabled) {
3203 sliderOptions.draggable = false;
3204 sliderOptions.selectedAttraction = 1;
3205 sliderOptions.friction = 1;
3206 }
3207
3208 $initial.removeAttr('data-initial-image');
3209 $imageSlider.flickity(sliderOptions);
3210
3211 if (!Modernizr.touchevents && zoomEnabled) {
3212 var $easyzoom = $('.product-image--cell').easyZoom();
3213 $imageSlider
3214 .on('dragStart.flickity', function(event) {
3215 var $zoom = $(event.currentTarget).find('.easyzoom-flyout');
3216 $zoom.addClass('hidden');
3217 })
3218 .on('dragEnd.flickity', function(event) {
3219 var $zoom = $(event.currentTarget).find('.easyzoom-flyout');
3220 $zoom.removeClass('hidden');
3221 });
3222 }
3223
3224 $imageSlider.on('dragStart.flickity', function(event, pointer) {
3225 document.ontouchmove = function(e) {
3226 e.preventDefault();
3227 };
3228 });
3229 $imageSlider.on('dragEnd.flickity', function(event, pointer) {
3230 document.ontouchmove = function(e) {
3231 return true;
3232 };
3233 });
3234 $imageSlider.find('a').on('click', function(e) {
3235 e.preventDefault();
3236 });
3237
3238 if (lightboxEnabled && Modernizr.objectfit) {
3239 $imageSlider.on('staticClick.flickity', function() {
3240 $imageSlider.flickity('toggleFullscreen');
3241 });
3242 $imageSlider.on('fullscreenChange.flickity', function(
3243 event,
3244 isFullscreen
3245 ) {
3246 if (isFullscreen) {
3247 $imageSlider.parent().addClass('is-fullscreen');
3248 } else {
3249 $imageSlider.parent().removeClass('is-fullscreen');
3250 }
3251 });
3252 }
3253 }
3254
3255 // Flickity thumb slider
3256 var $thumbSlider = this.$container.find('.product-thumb--slider');
3257 if ($thumbSlider.length) {
3258 var sliderOptions = $thumbSlider.data('flickity-options');
3259 var enabled = $thumbSlider.data('slider-enabled');
3260
3261 // If the grouped thumbnails option is turned on
3262 if (enabled) {
3263 var $initial = $thumbSlider.find('[data-initial-image]');
3264 var initialIndex = $initial
3265 .parent()
3266 .find('.product-thumb--cell')
3267 .index($initial);
3268 sliderOptions.initialIndex = initialIndex;
3269
3270 $initial.removeAttr('data-initial-image');
3271 $thumbSlider.flickity(sliderOptions);
3272 $thumbSlider.find('a').on('click', function(e) {
3273 e.preventDefault();
3274 });
3275 $thumbSlider.on('dragStart.flickity', function(event, pointer) {
3276 document.ontouchmove = function(e) {
3277 e.preventDefault();
3278 };
3279 });
3280 $thumbSlider.on('dragEnd.flickity', function(event, pointer) {
3281 document.ontouchmove = function(e) {
3282 return true;
3283 };
3284 });
3285 } else {
3286 var $initial = $thumbSlider.find('[data-initial-image]');
3287
3288 $initial.addClass('is-nav-selected');
3289 $initial.removeAttr('data-initial-image');
3290 $thumbSlider.find('a').on('click', function(e) {
3291 var $thumb = $(this);
3292 var $cell = $thumb.parent();
3293 var id = $cell.data('image-id');
3294 var selector = '[data-image-id="' + id + '"]';
3295 $imageSlider.flickity('selectCell', selector, false, true);
3296
3297 $cell
3298 .parent()
3299 .find('.' + 'is-nav-selected')
3300 .removeClass('is-nav-selected');
3301 $cell.addClass('is-nav-selected');
3302 e.preventDefault();
3303 });
3304
3305 // On slide change update thumbnail
3306 $imageSlider.on('change.flickity', function(event, index) {
3307 var $slider = $(this);
3308 var $cell = $slider.find('.product-image--cell').eq(index);
3309 var imageId = $cell.data('image-id');
3310
3311 $thumbSlider
3312 .find('.' + 'is-nav-selected')
3313 .removeClass('is-nav-selected');
3314 var $activeThumb = $thumbSlider.find(
3315 '[data-image-id="' + imageId + '"]'
3316 );
3317 $activeThumb.addClass('is-nav-selected');
3318 });
3319 }
3320
3321 // IE fix for object-fit
3322 if (!Modernizr.objectfit) {
3323 $thumbSlider.find('.product-thumb--cell a').each(function() {
3324 var $container = $(this);
3325 var imgUrl = $container.find('img').prop('src');
3326
3327 $container.css({
3328 'background-image': 'url(' + imgUrl + ')',
3329 });
3330
3331 $container.addClass('fallback-object-fit');
3332 });
3333 }
3334 }
3335
3336 // Basic non flickity thumbnails
3337 var $thumbs = this.$container.find('.thumbnails');
3338 if ($thumbs.length) {
3339 $thumbs.find('a').on('click', function(e) {
3340 var $thumb = $(this);
3341 var id = $thumb.data('image-id');
3342 var selector = '[data-image-id="' + id + '"]';
3343 $imageSlider.flickity('selectCell', selector, false, true);
3344
3345 $thumb
3346 .parent()
3347 .parent()
3348 .find('.' + 'active')
3349 .removeClass('active');
3350 $thumb.addClass('active');
3351
3352 e.preventDefault();
3353 });
3354
3355 // On slide change update thumbnail
3356 $imageSlider.on('change.flickity', function(event, index) {
3357 var $slider = $(this);
3358 var $cell = $slider.find('.product-image--cell').eq(index);
3359 var imageId = $cell.data('image-id');
3360
3361 $thumbs.find('.' + 'active').removeClass('active');
3362 var $activeThumb = $thumbs.find(
3363 '[data-image-id="' + imageId + '"]'
3364 );
3365 $activeThumb.addClass('active');
3366
3367 var $thumbsList = $thumbs.find('[data-productthumbs]');
3368 if ($thumbsList.height() > $thumbs.height()) {
3369 if ($thumbs.data('enable-group') && $thumbs.is(':visible')) {
3370 setTimeout(function() {
3371 $activeThumb.scrollIntoView();
3372 }, 200);
3373 }
3374 }
3375 });
3376
3377 // Resize thumbnail container on slider height change
3378 if ($thumbs.data('enable-group')) {
3379 $thumbs.css({
3380 'overflow-y': 'scroll',
3381 position: 'relative',
3382 });
3383
3384 $imageSlider.resize(function() {
3385 $thumbs.height(
3386 $(this)
3387 .find('.flickity-viewport')
3388 .height()
3389 );
3390 });
3391
3392 var $thumbsList = $thumbs.find('[data-productthumbs]');
3393
3394 setTimeout(function() {
3395 if ($thumbsList.height() > $thumbs.height()) {
3396 $thumbs.find('a.active').scrollIntoView();
3397 }
3398 }, 200);
3399
3400 $thumbs.find('li').each(function(i) {
3401 $(this)
3402 .delay(i * 100)
3403 .fadeTo(200, 1);
3404 });
3405 }
3406 }
3407 },
3408
3409 _linkOptionSelectors: function(product) {
3410 // Building our mapping object
3411 for (var i = 0; i < product.variants.length; i++) {
3412 var variant = product.variants[i];
3413 if (variant.available) {
3414 // Gathering values for the 1st option group
3415 this.optionsMap['root'] = this.optionsMap['root'] || [];
3416 this.optionsMap['root'].push(variant.option1);
3417 this.optionsMap['root'] = $.unique(this.optionsMap['root']);
3418 // Gathering values for the 2nd option group
3419 if (product.options.length > 1) {
3420 var key = variant.option1;
3421 this.optionsMap[key] = this.optionsMap[key] || [];
3422 this.optionsMap[key].push(variant.option2);
3423 this.optionsMap[key] = $.unique(this.optionsMap[key]);
3424 }
3425 // Gathering values for the 3rd option group
3426 if (product.options.length === 3) {
3427 var key = variant.option1 + ' / ' + variant.option2;
3428 this.optionsMap[key] = this.optionsMap[key] || [];
3429 this.optionsMap[key].push(variant.option3);
3430 this.optionsMap[key] = $.unique(this.optionsMap[key]);
3431 }
3432 }
3433 }
3434
3435 // Update options right away
3436 this._updateOptionsInSelector(0);
3437 if (product.options.length > 1) this._updateOptionsInSelector(1);
3438 if (product.options.length === 3) this._updateOptionsInSelector(2);
3439 },
3440
3441 _updateOptionsInSelector: function(selectorIndex) {
3442 switch (selectorIndex) {
3443 case 0:
3444 var key = 'root';
3445 var selector = $('.single-option-radio:eq(0)', this.selectors.addToCartForm);
3446 break;
3447 case 1:
3448 var key = $('input:checked', this.selectors.addToCartForm + ' .single-option-radio:eq(0)').val();
3449 var selector = $('.single-option-radio:eq(1)', this.selectors.addToCartForm);
3450 break;
3451 case 2:
3452 var key = $('input:checked', this.selectors.addToCartForm + ' .single-option-radio:eq(0)').val();
3453 key +=
3454 ' / ' + $('input:checked', this.selectors.addToCartForm + ' .single-option-radio:eq(1)').val();
3455 var selector = $('.single-option-radio:eq(2)', this.selectors.addToCartForm);
3456 break;
3457 }
3458
3459 var initialValue = $('input:checked', selector).val();
3460 var availableOptions = this.optionsMap[key];
3461
3462 var optionIndex = selectorIndex + 1;
3463 $(
3464 '.radio-wrapper[data-option-index="' +
3465 optionIndex +
3466 '"] input.single-option-selector__radio', this.selectors.addToCartForm
3467 ).each(function () {
3468 if ($.inArray($(this).val(), availableOptions) !== -1) {
3469 $(this)
3470 .parent()
3471 .removeClass('soldout');
3472 } else {
3473 $(this)
3474 .parent()
3475 .addClass('soldout');
3476 }
3477 });
3478
3479 var $optionGroup = $(
3480 '.radio-wrapper[data-option-index="' + optionIndex + '"]', this.selectors.addToCartForm
3481 );
3482 var $selectedSwatch = $optionGroup.find('input:checked').parent();
3483
3484 if ($selectedSwatch.hasClass('soldout')) {
3485 var $availableSwatch = $optionGroup
3486 // .find('.swatch-container:not(.soldout)')
3487 // .eq(0);
3488 if ($availableSwatch.length > 0) {
3489 // $availableSwatch.find('input').trigger('click');
3490 }
3491 }
3492 },
3493
3494 _updateAddToCart: function(evt) {
3495 var variant = evt.variant;
3496 var dynamicCheckout = $(this.selectors.addToCartForm).data(
3497 'dynamic-checkout'
3498 );
3499
3500 if (variant) {
3501 $(this.selectors.cartError).hide();
3502 $(this.selectors.productPrices)
3503 .removeClass('visibility-hidden')
3504 .attr('aria-hidden', 'true');
3505
3506 if (variant.available) {
3507 $(this.selectors.addToCart)
3508 .removeClass('disabled')
3509 .prop('disabled', false);
3510 $(this.selectors.addToCartText).text(StyleHatch.Strings.addToCart);
3511 $(this.selectors.quantityWrap).show();
3512 if (dynamicCheckout) {
3513 $(this.selectors.cartButtons).addClass('cart-buttons__enabled');
3514 }
3515 } else {
3516 // The variant doesn't exist, disable submit button and change the text.
3517 // This may be an error or notice that a specific variant is not available.
3518 $(this.selectors.addToCart)
3519 .addClass('disabled')
3520 .prop('disabled', true);
3521 $(this.selectors.addToCartText).text(StyleHatch.Strings.soldOut);
3522 $(this.selectors.quantityWrap).hide();
3523 if (dynamicCheckout) {
3524 $(this.selectors.cartButtons).removeClass(
3525 'cart-buttons__enabled'
3526 );
3527 }
3528 }
3529 } else {
3530 $(this.selectors.addToCart)
3531 .addClass('disabled')
3532 .prop('disabled', true);
3533 $(this.selectors.addToCartText).text(StyleHatch.Strings.soldOut);
3534 $(this.selectors.productPrices)
3535 .addClass('visibility-hidden')
3536 .attr('aria-hidden', 'false');
3537 $(this.selectors.quantityWrap).hide();
3538 if (dynamicCheckout) {
3539 $(this.selectors.cartButtons).removeClass('cart-buttons__enabled');
3540 }
3541 }
3542 },
3543
3544 _updateSwatches: function(evt) {
3545 var currentVariant = evt.variant;
3546 var $swatch = $(evt.currentTarget).find('[type=radio]');
3547 var $radioWrapper = $(evt.currentTarget).find('.radio-wrapper');
3548 var Product = this;
3549
3550 // Update options right away
3551 this._updateOptionsInSelector(0);
3552 if (Product.productSingleObject.options.length > 1)
3553 this._updateOptionsInSelector(1);
3554 if (Product.productSingleObject.options.length === 3)
3555 this._updateOptionsInSelector(2);
3556
3557 $radioWrapper.each(function() {
3558 var $radioWrapper = $(this);
3559 var currentOption = 'option' + $radioWrapper.data('option-index');
3560 var $labelValue = $radioWrapper.find(
3561 '.single-option-radio__label--value'
3562 );
3563
3564 if ($labelValue.length) {
3565 var value = currentVariant[currentOption];
3566 $labelValue.text(value);
3567 }
3568 });
3569 },
3570
3571 _updateImages: function(evt) {
3572 var variant = evt.variant;
3573
3574 var sizedImgUrl = theme.Images.getSizedImageUrl(
3575 variant.featured_image.src,
3576 this.settings.imageSize
3577 );
3578 var zoomSizedImgUrl;
3579
3580 if (this.settings.zoomEnabled) {
3581 zoomSizedImgUrl = theme.Images.getSizedImageUrl(
3582 variant.featured_image.src,
3583 this.settings.imageZoomSize
3584 );
3585 }
3586
3587 var $thumbnail = $(
3588 this.selectors.productThumbImages +
3589 '[data-image-id="' +
3590 variant.featured_image.id +
3591 '"]'
3592 );
3593
3594 // Slider
3595 var $imageSlider = this.$container.find('.product-image--slider');
3596 if ($imageSlider.length) {
3597 var selector = '[data-image-id="' + variant.featured_image.id + '"]';
3598 $imageSlider.flickity('selectCell', selector, false, true);
3599 }
3600 },
3601
3602 _updatePrice: function(evt) {
3603 var variant = evt.variant;
3604
3605 // Update the product price
3606 $(this.selectors.originalPrice).html(
3607 theme.Currency.formatMoney(variant.price, StyleHatch.currencyFormat)
3608 );
3609
3610 // Apply any currency conversions
3611 if (StyleHatch.currencyConverter) {
3612 // clear out previous currency attributes
3613 removeDataAttributes($(this.selectors.originalPrice));
3614 Currency.convertAll(
3615 StyleHatch.shopCurrency,
3616 $('[name=currencies]').val()
3617 );
3618 $('.selected-currency').text(Currency.currentCurrency);
3619 }
3620
3621 // Update and show the product's compare price if necessary
3622 if (variant.compare_at_price > variant.price) {
3623 $(this.selectors.comparePrice)
3624 .find('span.money')
3625 .html(
3626 theme.Currency.formatMoney(
3627 variant.compare_at_price,
3628 StyleHatch.currencyFormat
3629 )
3630 );
3631 $(this.selectors.comparePrice).show();
3632 } else {
3633 $(this.selectors.comparePrice).hide();
3634 }
3635 },
3636
3637 _updateSKU: function(evt) {
3638 var variant = evt.variant;
3639
3640 // Update the sku
3641 $(this.selectors.SKU).html(variant.sku);
3642 },
3643
3644 onUnload: function() {
3645 this.$container.off(this.settings.namespace);
3646 // destroys on close
3647 $.magnificPopup.close();
3648 if (this.settings.zoomEnabled) {
3649 _destroyZoom($(this.selectors.productImageWrap));
3650 }
3651 if (StyleHatch.ajaxCartEnable) {
3652 StyleHatch.AjaxCart.unload();
3653 }
3654 this.$container.destroyProductBox();
3655 },
3656 });
3657
3658 function _enableZoom($el) {
3659 var $easyzoom = $el.easyZoom();
3660 }
3661
3662 function _destroyZoom($el) {
3663 var easyZoomApi = $el.easyZoom().data('easyZoom');
3664 easyZoomApi.teardown();
3665 }
3666
3667 return Product;
3668 })();
3669
3670 // Collection (template) Class
3671 StyleHatch.Collection = (function() {
3672 var constants = {
3673 SORT_BY: 'sort_by',
3674 DEFAULT_SORT: 'title-ascending',
3675 VIEW: 'view',
3676 };
3677
3678 var selectors = {
3679 sortSelection: '#SortBy',
3680 defaultSort: '.sort-by__default-sort',
3681 viewChange: '.change-view',
3682 advancedFilter: '.advanced-filter a',
3683 filterCollection: '.mobile-aside-container > a.button.simple',
3684 mobileAside: '.mobile-aside-container aside',
3685 productBox: '.box.product .image-table',
3686 nestedMenu: 'ul.nested-menu',
3687 };
3688
3689 function Collection(container) {
3690 var $container = (this.$container = $(container));
3691 var sectionId = $container.attr('data-section-id');
3692
3693 this.$sortSelect = $(selectors.sortSelection, $container);
3694 this.defaultSort = this._getDefaultSortValue();
3695
3696 this.$viewButton = $(selectors.viewChange);
3697
3698 this.$sortSelect.on('change', this._onSortChange.bind(this));
3699 this.$viewButton.on('click', this._onViewChange);
3700
3701 this.$productbox = $(selectors.productBox, $container);
3702
3703 this._initSidebar();
3704 this._initAdvancedTags();
3705
3706 $container.productBox();
3707
3708 if (StyleHatch.currencyConverter) {
3709 StyleHatch.CurrencyConverter.init();
3710 }
3711 }
3712
3713 Collection.prototype = $.extend({}, Collection.prototype, {
3714 _onSortChange: function(evt) {
3715 var query = '';
3716
3717 this.sort = this._getSortValue();
3718
3719 if (this.sort !== this.defaultSort) {
3720 query = [constants.SORT_BY + '=' + this.sort];
3721 }
3722
3723 var url = document.URL;
3724 var hasParams = url.indexOf('?') > -1;
3725
3726 if (hasParams) {
3727 document.location.href = replaceUrlParam(
3728 url,
3729 constants.SORT_BY,
3730 this.sort
3731 );
3732 } else {
3733 var search = (document.location.search = query.length
3734 ? '?' + query
3735 : '');
3736 document.location.href = this.$filterSelect.val() + search;
3737 }
3738 },
3739 _getSortValue: function() {
3740 return this.$sortSelect.val() || this.defaultSort;
3741 },
3742 _getDefaultSortValue: function() {
3743 return (
3744 $(selectors.defaultSort, this.$container).val() ||
3745 constants.DEFAULT_SORT
3746 );
3747 },
3748 _onViewChange: function(evt) {
3749 var query = '';
3750
3751 var view = $(this).data('view');
3752 var url = document.URL;
3753 var hasParams = url.indexOf('?') > -1;
3754
3755 if (hasParams) {
3756 window.location = replaceUrlParam(url, 'view', view);
3757 } else {
3758 window.location = url + '?view=' + view;
3759 }
3760
3761 evt.preventDefault();
3762 },
3763 _initSidebar: function() {
3764 $(selectors.filterCollection).on('click', function(e) {
3765 $(selectors.mobileAside).slideToggle();
3766 e.preventDefault();
3767 });
3768
3769 this.$container.find(selectors.nestedMenu).initNestedMenu();
3770 },
3771 _initAdvancedTags: function() {
3772 var $filters = $(selectors.advancedFilter),
3773 $tag,
3774 tagGroup,
3775 tagHandle,
3776 $activeTagInGroup;
3777
3778 $filters.on('click', function(e) {
3779 $tag = $(this).parent();
3780 tagGroup = $tag.data('group');
3781 tagHandle = $tag.data('handle');
3782 $activeTagInGroup = $('.active[data-group="' + tagGroup + '"]');
3783
3784 // If the tag clicked is not already active and its group contains an active tag
3785 // we will swap tag within the group
3786 if (!$tag.hasClass('active') && $activeTagInGroup.size()) {
3787 e.preventDefault();
3788 location.href = location.href
3789 // swap tag
3790 .replace($activeTagInGroup.data('handle'), tagHandle)
3791 // go back to page 1
3792 .replace(/(&page=\d+)|(page=\d+&)|(\?page=\d+$)/, '');
3793 }
3794 });
3795 },
3796 onUnload: function() {
3797 this.$sortSelect.off('change');
3798 this.$viewButton.off('click');
3799 $(selectors.advancedFilter).off('click');
3800 this.$container.destroyProductBox();
3801 this.$container.find(selectors.nestedMenu).destroyNestedMenu();
3802 },
3803 });
3804
3805 return Collection;
3806 })();
3807
3808 // List collections (template) Class
3809 StyleHatch.ListCollections = (function() {
3810 var selectors = {
3811 productBox: '.box .image-table',
3812 };
3813
3814 function ListCollections(container) {
3815 var $container = (this.$container = $(container));
3816 var sectionId = $container.attr('data-section-id');
3817 var layout = $container.data('layout');
3818 var $cardImage = $container.find('.card__image');
3819
3820 this.$productbox = $(selectors.productBox, $container);
3821
3822 if (layout == 'preview') {
3823 $container.productBox();
3824 }
3825
3826 if (StyleHatch.currencyConverter) {
3827 StyleHatch.CurrencyConverter.init();
3828 }
3829 }
3830
3831 ListCollections.prototype = $.extend({}, ListCollections.prototype, {
3832 onUnload: function() {
3833 $container.destroyProductBox();
3834 },
3835 });
3836
3837 return ListCollections;
3838 })();
3839
3840 // Blog and Article (template) Class
3841 StyleHatch.BlogArticle = (function() {
3842 var selectors = {
3843 filterCollection: '.mobile-aside-container > a.button.simple',
3844 mobileAside: '.mobile-aside-container aside',
3845 nestedMenu: 'ul.nested-menu',
3846 };
3847
3848 function BlogArticle(container) {
3849 var $container = (this.$container = $(container));
3850 var sectionId = $container.attr('data-section-id');
3851 this._initSidebar();
3852 StyleHatch.videoLayout();
3853 }
3854
3855 BlogArticle.prototype = $.extend({}, BlogArticle.prototype, {
3856 _initSidebar: function() {
3857 $(selectors.filterCollection).on('click', function(e) {
3858 $(selectors.mobileAside).slideToggle();
3859 e.preventDefault();
3860 });
3861
3862 this.$container.find(selectors.nestedMenu).initNestedMenu();
3863 },
3864 onUnload: function() {
3865 $(selectors.filterCollection).off('click');
3866 this.$container.find(selectors.nestedMenu).destroyNestedMenu();
3867 },
3868 });
3869
3870 return BlogArticle;
3871 })();
3872
3873 // Password (template) Class
3874 StyleHatch.Password = (function() {
3875 function Password(container) {
3876 var $container = (this.$container = $(container));
3877 var sectionId = $container.attr('data-section-id');
3878
3879 var $loginForm = $('#login_form');
3880
3881 $('.login-popup').magnificPopup({
3882 type: 'inline',
3883 midClick: true,
3884 mainClass: 'mfp-fade',
3885 closeBtnInside: false,
3886 callbacks: {
3887 afterClose: function() {
3888 $('a').blur();
3889 $loginForm.find('.errors').remove();
3890 },
3891 },
3892 });
3893
3894 // On MailChimp form submit
3895 $('#mc-embedded-subscribe-form').on('submit', function() {
3896 $('p.signup-message').hide();
3897 $('p.thanks-message').show();
3898 $(this)
3899 .find('.input-row')
3900 .hide();
3901 });
3902
3903 // If error in password form
3904 if ($loginForm.find('.errors').length > 0) {
3905 $('.login-popup').magnificPopup('open');
3906 }
3907 }
3908
3909 Password.prototype = $.extend({}, Password.prototype, {
3910 onUnload: function() {
3911 // destroys on close
3912 $.magnificPopup.close();
3913 $('#mc-embedded-subscribe-form').off('submit');
3914 },
3915 });
3916
3917 return Password;
3918 })();
3919
3920 // Cart (template) Class
3921 StyleHatch.Cart = (function() {
3922 function Cart(container) {
3923 var $container = (this.$container = $(container));
3924 var sectionId = $container.attr('data-section-id');
3925 StyleHatch.quantitySelect();
3926 }
3927
3928 Cart.prototype = $.extend({}, Cart.prototype, {
3929 onUnload: function() {},
3930 });
3931
3932 return Cart;
3933 })();
3934
3935 /**
3936 * Classes - complex functionality
3937 * ---------------------------------------------------------------------------
3938 * AjaxCart
3939 * Currency
3940 */
3941
3942 /*
3943 * Add product to cart without page refresh
3944 */
3945 StyleHatch.AjaxCart = (function() {
3946 var selectors = {
3947 body: 'body',
3948 util: 'header.util',
3949 cartPreview: 'header.util .cart-preview',
3950 addToCartForm: '[data-AddToCartForm] > form',
3951 addToCartButton: '[data-AddToCartForm]',
3952 cartButton: '[data-CartButton]',
3953 cartCount: '#CartCount',
3954 cartCost: '#CartCost',
3955 };
3956 var config = {
3957 addURL: '/cart/add.js',
3958 cartURL: '/cart.js',
3959 clearURL: '/cart/clear.js',
3960 };
3961 var cache = {};
3962 function cacheSelectors() {
3963 cache = {
3964 $body: $(selectors.body),
3965 $util: $(selectors.util),
3966 $cartPreview: $(selectors.cartPreview),
3967 $addToCartForm: $(selectors.addToCartForm),
3968 $addToCartButton: $(selectors.addToCartButton),
3969 $cartButton: $(selectors.cartButton),
3970 $cartCount: $(selectors.cartCount),
3971 $cartCost: $(selectors.cartCost),
3972 };
3973 }
3974
3975 function init() {
3976 cacheSelectors();
3977 bindEvents();
3978 }
3979
3980 function submitCart($form) {
3981 var $form = $form;
3982 var $cartError = $form.find('.cart-error');
3983 // Change button to added to cart and disabled
3984 var cartButtonText = $form.find('[data-AddToCartText]').html();
3985 var cartButtonAddedText = $form
3986 .find('[data-AddToCartText]')
3987 .attr('data-added');
3988 var cartButtonAddingText = $form
3989 .find('[data-AddToCartText]')
3990 .attr('data-adding');
3991
3992 $form
3993 .find('[data-AddToCart]')
3994 .addClass('added')
3995 .prop('disabled', true);
3996 $form.find('[data-AddToCartText]').html(cartButtonAddingText);
3997
3998 $cartError.hide();
3999
4000 $.post(
4001 config.addURL,
4002 $form.serialize(),
4003 function(data) {
4004 // Last product added data
4005 var productData = data;
4006
4007 // Get the data from the cart for totals
4008 $.get(
4009 config.cartURL,
4010 function(data) {
4011 var cartData = data;
4012
4013 // Update cart button count & price
4014 updateCartButton(cartData);
4015
4016 // Show the recent item added to the cart
4017 showCartPreview(productData, cartData);
4018
4019 // Change cart button text back
4020 // Auto hide after 6000ms
4021 var resetCartButton;
4022 resetCartButton = setTimeout(function() {
4023 $form
4024 .find('[data-AddToCart]')
4025 .removeClass('added')
4026 .prop('disabled', false);
4027 $form.find('[data-AddToCartText]').html(cartButtonText);
4028 }, 500);
4029 },
4030 'json'
4031 );
4032 },
4033 'text'
4034 ).error(function(data) {
4035 if (typeof data != 'undefined' && typeof data.status != 'undefined') {
4036 var responseText = JSON.parse(data.responseText);
4037 $cartError.html(
4038 '<strong>' +
4039 responseText.message +
4040 ':</strong> <em>' +
4041 responseText.description +
4042 '<em>'
4043 );
4044 $cartError.slideDown();
4045 }
4046 // manually submit the form
4047 // $form.addClass('noAJAX');
4048 // $form.submit();
4049 // Change cart button text back
4050 // Auto hide after 6000ms
4051 var resetCartButton;
4052 resetCartButton = setTimeout(function() {
4053 $form
4054 .find('[data-AddToCart]')
4055 .removeClass('added')
4056 .prop('disabled', false);
4057 $form.find('[data-AddToCartText]').html(cartButtonText);
4058 }, 500);
4059 });
4060 return false;
4061 }
4062 function clearCart() {
4063 $.post(config.clearURL);
4064 }
4065 function updateCartButton(cartData) {
4066 var $cartButton = cache.$cartButton;
4067 var $cartCount = cache.$cartCount;
4068 var $cartCost = cache.$cartCost;
4069
4070 var itemCount = cartData.item_count;
4071 var totalPrice = theme.Currency.formatMoney(
4072 cartData.total_price,
4073 StyleHatch.currencyFormat
4074 );
4075
4076 $cartCount.text(itemCount);
4077 $cartCost.removeClass('money');
4078 $cartCost.html('<span class="money">' + totalPrice + '</span>');
4079
4080 // If Currency convertor has been added
4081 if (StyleHatch.currencyConverter) {
4082 Currency.convertAll(
4083 StyleHatch.shopCurrency,
4084 jQuery('[name=currencies]').val()
4085 );
4086 }
4087 }
4088 function showCartPreview(productData, cartData) {
4089 var $util = cache.$util;
4090 var $cartPreview = cache.$cartPreview;
4091
4092 clearTimeout(cache.hideCartPreview);
4093 cache.$cartPreview.hide();
4094
4095 // Cart Data
4096 var itemCount = cartData.item_count;
4097 var totalPrice = theme.Currency.formatMoney(
4098 cartData.total_price,
4099 StyleHatch.currencyFormat
4100 );
4101
4102 // Recent Added Product Data
4103 var productData = JSON.parse(productData);
4104 var productTitle = productData.product_title;
4105 var productVariant = productData.variant_options;
4106 var productImage = productData.image;
4107 var productURL = productData.url;
4108 var productPrice = theme.Currency.formatMoney(
4109 productData.price,
4110 StyleHatch.currencyFormat
4111 );
4112 var productQuantity = productData.quantity;
4113 var productTotal = theme.Currency.formatMoney(
4114 productData.line_price,
4115 StyleHatch.currencyFormat
4116 );
4117
4118 // Set Product Details
4119 var $productImage = $cartPreview.find('.product-image').empty();
4120 $productImage.append(
4121 '<img src="' + productImage + '" alt="' + productTitle + '">'
4122 );
4123 $productImage.attr('href', productURL);
4124
4125 var $productTitle = $cartPreview.find('.product-title');
4126 $productTitle.html(productTitle);
4127 $productTitle.attr('href', productURL);
4128
4129 var $productVarient = $cartPreview.find('.product-variant').empty();
4130 $.each(productVariant, function() {
4131 var variantStr = this;
4132 if (variantStr.toLowerCase().indexOf('default title') < 0) {
4133 $productVarient.show();
4134 $productVarient.append('<li>' + variantStr + '</li>');
4135 } else {
4136 $productVarient.hide();
4137 }
4138 });
4139
4140 var $productPrice = $cartPreview.find('.product-price');
4141 $productPrice.removeClass('money');
4142 $productPrice.html('<span class="money">' + productPrice + '</span>');
4143
4144 // Set Cart Totals
4145 var $itemCount = $cartPreview.find('.item-count');
4146 $itemCount.text(itemCount);
4147
4148 if (itemCount > 1) {
4149 $cartPreview.find('.count.plural').show();
4150 $cartPreview.find('.count.singular').hide();
4151 } else {
4152 $cartPreview.find('.count.plural').hide();
4153 $cartPreview.find('.count.singular').show();
4154 }
4155
4156 var $totalPrice = $cartPreview.find('.total-price');
4157 $totalPrice.html('<span class="money">' + totalPrice + '</span>');
4158
4159 var utilHeight = $util.height();
4160 $cartPreview.css({
4161 top: utilHeight,
4162 });
4163
4164 // Fade in the preview
4165 $cartPreview.fadeIn(300);
4166
4167 // Auto hide after 6000ms
4168 cache.hideCartPreview = setTimeout(function() {
4169 $cartPreview.fadeOut(300);
4170 }, 6000);
4171
4172 $cartPreview.find('a.continue-shopping').on('click', function(e) {
4173 $cartPreview.fadeOut(300);
4174 e.preventDefault();
4175 });
4176
4177 // If Currency convertor has been added
4178 if (StyleHatch.currencyConverter) {
4179 Currency.convertAll(
4180 StyleHatch.shopCurrency,
4181 jQuery('[name=currencies]').val()
4182 );
4183 }
4184 }
4185
4186 /*
4187 * Events
4188 */
4189 function bindEvents() {
4190 cache.$addToCartForm.each(function() {
4191 $(this).on('submit', function(e) {
4192 var $form = $(this);
4193 submitCart($form);
4194 e.preventDefault();
4195 });
4196 });
4197 }
4198 function unbindEvents() {
4199 cache.$addToCartForm.off('submit');
4200 }
4201
4202 function unload() {
4203 unbindEvents();
4204 clearTimeout(cache.hideCartPreview);
4205 cache.$cartPreview.hide();
4206 }
4207
4208 return {
4209 init: init,
4210 clearCart: clearCart,
4211 unload: unload,
4212 };
4213 })();
4214
4215 /*
4216 * Currency converter
4217 */
4218 StyleHatch.CurrencyConverter = (function() {
4219 var selectors = {
4220 body: 'body',
4221 money: 'span.money',
4222 };
4223 var config = {
4224 enabled: false,
4225 };
4226 var cache = {};
4227 function cacheSelectors() {
4228 cache = {
4229 $body: $(selectors.body),
4230 $money: $(selectors.money),
4231 };
4232 }
4233
4234 // Initialization
4235 function init() {
4236 cacheSelectors();
4237 convert();
4238 }
4239
4240 // Methods
4241 function convert() {
4242 // Get current currency based on cookie
4243 config.cookieCurrency = Currency.cookie.read();
4244
4245 // Make sure customer account pages have proper spans
4246 $('span.money span.money').each(function() {
4247 $(this)
4248 .parents(selectors.money)
4249 .removeClass('money');
4250 });
4251
4252 /* Saving the current price */
4253 cache.$money.each(function() {
4254 if (Currency.currentCurrency !== '') {
4255 $(this).attr(
4256 'data-currency-' + Currency.currentCurrency,
4257 $(this).html()
4258 );
4259 } else {
4260 $(this).attr(
4261 'data-currency-' + StyleHatch.shopCurrency,
4262 $(this).html()
4263 );
4264 }
4265 });
4266
4267 // If there's no cookie.
4268 if (config.cookieCurrency == null) {
4269 if (StyleHatch.shopCurrency !== StyleHatch.defaultCurrency) {
4270 Currency.convertAll(
4271 StyleHatch.shopCurrency,
4272 StyleHatch.defaultCurrency
4273 );
4274 } else {
4275 Currency.currentCurrency = StyleHatch.defaultCurrency;
4276 }
4277 } else if (
4278 $('[name=currencies]').size() &&
4279 $(
4280 '[name=currencies] option[value=' + config.cookieCurrency + ']'
4281 ).size() === 0
4282 ) {
4283 // If the cookie value does not correspond to any value in the currency dropdown.
4284 Currency.currentCurrency = StyleHatch.shopCurrency;
4285 Currency.cookie.write(StyleHatch.shopCurrency);
4286 } else if (config.cookieCurrency === StyleHatch.shopCurrency) {
4287 Currency.currentCurrency = StyleHatch.shopCurrency;
4288 } else {
4289 Currency.convertAll(StyleHatch.shopCurrency, config.cookieCurrency);
4290 }
4291
4292 // On currency change
4293 $('[name=currencies]')
4294 .val(Currency.currentCurrency)
4295 .change(function() {
4296 var newCurrency = $(this).val();
4297 Currency.convertAll(Currency.currentCurrency, newCurrency);
4298 $('.selected-currency').text(Currency.currentCurrency);
4299
4300 // If the currency is the store currency, hide the cart disclaimer
4301 if ($('p.currency-at-checkout').length > 0) {
4302 if (Currency.currentCurrency == StyleHatch.shopCurrency) {
4303 $('p.currency-at-checkout').hide();
4304 } else {
4305 $('p.currency-at-checkout').show();
4306 }
4307 }
4308 });
4309
4310 // Update selected currency
4311 $('[name=currencies]')
4312 .val(Currency.currentCurrency)
4313 .change();
4314 }
4315
4316 // Public methods
4317 return {
4318 init: init,
4319 config: config,
4320 convert: convert,
4321 };
4322 })();
4323
4324 /**
4325 * Slate & Theme Functionality
4326 * ------------------------------------------------------------------------------
4327 */
4328 window.theme = window.theme || {};
4329 window.slate = window.slate || {};
4330
4331 /**
4332 * Image Helper Functions
4333 * -----------------------------------------------------------------------------
4334 * A collection of functions that help with basic image operations.
4335 *
4336 */
4337 theme.Images = (function() {
4338 /**
4339 * Preloads an image in memory and uses the browsers cache to store it until needed.
4340 *
4341 * @param {Array} images - A list of image urls
4342 * @param {String} size - A shopify image size attribute
4343 */
4344
4345 function preload(images, size) {
4346 if (typeof images === 'string') {
4347 images = [images];
4348 }
4349
4350 for (var i = 0; i < images.length; i++) {
4351 var image = images[i];
4352 this.loadImage(this.getSizedImageUrl(image, size));
4353 }
4354 }
4355
4356 /**
4357 * Loads and caches an image in the browsers cache.
4358 * @param {string} path - An image url
4359 */
4360 function loadImage(path) {
4361 new Image().src = path;
4362 }
4363
4364 /**
4365 * Swaps the src of an image for another OR returns the imageURL to the callback function
4366 * @param image
4367 * @param element
4368 * @param callback
4369 */
4370 function switchImage(image, element, callback) {
4371 var size = this.imageSize(element.src);
4372 var imageUrl = this.getSizedImageUrl(image.src, size);
4373
4374 if (callback) {
4375 callback(imageUrl, image, element); // eslint-disable-line callback-return
4376 } else {
4377 element.src = imageUrl;
4378 }
4379 }
4380
4381 /**
4382 * +++ Useful
4383 * Find the Shopify image attribute size
4384 *
4385 * @param {string} src
4386 * @returns {null}
4387 */
4388 function imageSize(src) {
4389 var match = src.match(
4390 /.+_((?:pico|icon|thumb|small|compact|medium|large|grande)|\d{1,4}x\d{0,4}|x\d{1,4})[_\.@]/
4391 );
4392
4393 if (match !== null) {
4394 return match[1];
4395 } else {
4396 return null;
4397 }
4398 }
4399
4400 /**
4401 * +++ Useful
4402 * Adds a Shopify size attribute to a URL
4403 *
4404 * @param src
4405 * @param size
4406 * @returns {*}
4407 */
4408 function getSizedImageUrl(src, size) {
4409 if (size == null) {
4410 return src;
4411 }
4412
4413 if (size === 'master') {
4414 return this.removeProtocol(src);
4415 }
4416
4417 var match = src.match(
4418 /\.(jpg|jpeg|gif|png|bmp|bitmap|tiff|tif)(\?v=\d+)?$/i
4419 );
4420
4421 if (match != null) {
4422 var prefix = src.split(match[0]);
4423 var suffix = match[0];
4424
4425 return this.removeProtocol(prefix[0] + '_' + size + suffix);
4426 }
4427
4428 return null;
4429 }
4430
4431 function removeProtocol(path) {
4432 return path.replace(/http(s)?:/, '');
4433 }
4434
4435 return {
4436 preload: preload,
4437 loadImage: loadImage,
4438 switchImage: switchImage,
4439 imageSize: imageSize,
4440 getSizedImageUrl: getSizedImageUrl,
4441 removeProtocol: removeProtocol,
4442 };
4443 })();
4444
4445 /**
4446 * Currency Helpers
4447 * -----------------------------------------------------------------------------
4448 * A collection of useful functions that help with currency formatting
4449 *
4450 * Current contents
4451 * - formatMoney - Takes an amount in cents and returns it as a formatted dollar value.
4452 *
4453 * Alternatives
4454 * - Accounting.js - http://openexchangerates.github.io/accounting.js/
4455 *
4456 */
4457
4458 theme.Currency = (function() {
4459 var moneyFormat = '${{amount}}'; // eslint-disable-line camelcase
4460
4461 function formatMoney(cents, format) {
4462 if (typeof cents === 'string') {
4463 cents = cents.replace('.', '');
4464 }
4465 var value = '';
4466 var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
4467 var formatString = format || moneyFormat;
4468
4469 function formatWithDelimiters(number, precision, thousands, decimal) {
4470 precision = slate.utils.defaultTo(precision, 2);
4471 thousands = slate.utils.defaultTo(thousands, ',');
4472 decimal = slate.utils.defaultTo(decimal, '.');
4473
4474 if (isNaN(number) || number == null) {
4475 return 0;
4476 }
4477
4478 number = (number / 100.0).toFixed(precision);
4479
4480 var parts = number.split('.');
4481 var dollarsAmount = parts[0].replace(
4482 /(\d)(?=(\d\d\d)+(?!\d))/g,
4483 '$1' + thousands
4484 );
4485 var centsAmount = parts[1] ? decimal + parts[1] : '';
4486
4487 return dollarsAmount + centsAmount;
4488 }
4489
4490 switch (formatString.match(placeholderRegex)[1]) {
4491 case 'amount':
4492 value = formatWithDelimiters(cents, 2);
4493 break;
4494 case 'amount_no_decimals':
4495 value = formatWithDelimiters(cents, 0);
4496 break;
4497 case 'amount_with_comma_separator':
4498 value = formatWithDelimiters(cents, 2, '.', ',');
4499 break;
4500 case 'amount_no_decimals_with_comma_separator':
4501 value = formatWithDelimiters(cents, 0, '.', ',');
4502 break;
4503 case 'amount_no_decimals_with_space_separator':
4504 value = formatWithDelimiters(cents, 0, ' ');
4505 break;
4506 }
4507
4508 return formatString.replace(placeholderRegex, value);
4509 }
4510
4511 return {
4512 formatMoney: formatMoney,
4513 };
4514 })();
4515
4516 /**
4517 * Utility helpers
4518 * -----------------------------------------------------------------------------
4519 * A collection of useful functions for dealing with arrays and objects
4520 *
4521 * @namespace utils
4522 */
4523
4524 slate.utils = {
4525 /**
4526 * Return an object from an array of objects that matches the provided key and value
4527 *
4528 * @param {array} array - Array of objects
4529 * @param {string} key - Key to match the value against
4530 * @param {string} value - Value to get match of
4531 */
4532 findInstance: function(array, key, value) {
4533 for (var i = 0; i < array.length; i++) {
4534 if (array[i][key] === value) {
4535 return array[i];
4536 }
4537 }
4538 },
4539
4540 /**
4541 * Remove an object from an array of objects by matching the provided key and value
4542 *
4543 * @param {array} array - Array of objects
4544 * @param {string} key - Key to match the value against
4545 * @param {string} value - Value to get match of
4546 */
4547 removeInstance: function(array, key, value) {
4548 var i = array.length;
4549 while (i--) {
4550 if (array[i][key] === value) {
4551 array.splice(i, 1);
4552 break;
4553 }
4554 }
4555
4556 return array;
4557 },
4558
4559 /**
4560 * _.compact from lodash
4561 * Remove empty/false items from array
4562 * Source: https://github.com/lodash/lodash/blob/master/compact.js
4563 *
4564 * @param {array} array
4565 */
4566 compact: function(array) {
4567 var index = -1;
4568 var length = array == null ? 0 : array.length;
4569 var resIndex = 0;
4570 var result = [];
4571
4572 while (++index < length) {
4573 var value = array[index];
4574 if (value) {
4575 result[resIndex++] = value;
4576 }
4577 }
4578 return result;
4579 },
4580
4581 /**
4582 * _.defaultTo from lodash
4583 * Checks `value` to determine whether a default value should be returned in
4584 * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
4585 * or `undefined`.
4586 * Source: https://github.com/lodash/lodash/blob/master/defaultTo.js
4587 *
4588 * @param {*} value - Value to check
4589 * @param {*} defaultValue - Default value
4590 * @returns {*} - Returns the resolved value
4591 */
4592 defaultTo: function(value, defaultValue) {
4593 return value == null || value !== value ? defaultValue : value;
4594 },
4595
4596 /**
4597 * _.debounce from underscore
4598 * Returns a function, that, as long as it continues to be invoked, will not
4599 * be triggered. The function will be called after it stops being called for
4600 * N milliseconds. If `immediate` is passed, trigger the function on the
4601 * leading edge, instead of the trailing.
4602 *
4603 * @param {*} func - Function to call
4604 * @param {*} wait - ms delay (250)
4605 * @param {*} immediate - bool
4606 */
4607
4608 debounce: function(func, wait, immediate) {
4609 var timeout;
4610 return function() {
4611 var context = this,
4612 args = arguments;
4613 var later = function() {
4614 timeout = null;
4615 if (!immediate) func.apply(context, args);
4616 };
4617 var callNow = immediate && !timeout;
4618 clearTimeout(timeout);
4619 timeout = setTimeout(later, wait);
4620 if (callNow) func.apply(context, args);
4621 };
4622 },
4623 };
4624
4625 /**
4626 * Variant Selection scripts
4627 * ------------------------------------------------------------------------------
4628 *
4629 * Handles change events from the variant inputs in any `cart/add` forms that may
4630 * exist. Also updates the master select and triggers updates when the variants
4631 * price or image changes.
4632 *
4633 * @namespace variants
4634 */
4635
4636 slate.Variants = (function() {
4637 /**
4638 * Variant constructor
4639 *
4640 * @param {object} options - Settings from `product.js`
4641 */
4642 function Variants(options) {
4643 this.$container = options.$container;
4644 this.product = options.product;
4645 this.singleOptionSelector = options.singleOptionSelector;
4646 this.originalSelectorId = options.originalSelectorId;
4647 this.enableHistoryState = options.enableHistoryState;
4648 this.enableSwatch = options.enableSwatch;
4649 this.currentVariant = this._getVariantFromOptions();
4650
4651 $(this.singleOptionSelector, this.$container).on(
4652 'change',
4653 this._onSelectChange.bind(this)
4654 );
4655 }
4656
4657 Variants.prototype = $.extend({}, Variants.prototype, {
4658 /**
4659 * Get the currently selected options from add-to-cart form. Works with all
4660 * form input elements.
4661 *
4662 * @return {array} options - Values of currently selected variants
4663 */
4664 _getCurrentOptions: function() {
4665 var currentOptions = $.map(
4666 $(this.singleOptionSelector, this.$container),
4667 function(element) {
4668 var $element = $(element);
4669 var type = $element.attr('type');
4670 var currentOption = {};
4671
4672 if (type === 'radio' || type === 'checkbox') {
4673 if ($element[0].checked) {
4674 currentOption.value = $element.val();
4675 currentOption.index = $element.data('index');
4676
4677 return currentOption;
4678 } else {
4679 return false;
4680 }
4681 } else {
4682 currentOption.value = $element.val();
4683 currentOption.index = $element.data('index');
4684
4685 return currentOption;
4686 }
4687 }
4688 );
4689
4690 // remove any unchecked input values if using radio buttons or checkboxes
4691 currentOptions = slate.utils.compact(currentOptions);
4692
4693 return currentOptions;
4694 },
4695
4696 /**
4697 * Find variant based on selected values.
4698 *
4699 * @param {array} selectedValues - Values of variant inputs
4700 * @return {object || undefined} found - Variant object from product.variants
4701 */
4702 _getVariantFromOptions: function() {
4703 var selectedValues = this._getCurrentOptions();
4704 var variants = this.product.variants;
4705 var found = false;
4706
4707 variants.forEach(function(variant) {
4708 var satisfied = true;
4709 var options = variant.options;
4710
4711 selectedValues.forEach(function(option) {
4712 if (satisfied) {
4713 satisfied = option.value === variant[option.index];
4714 }
4715 });
4716
4717 if (satisfied) {
4718 found = variant;
4719 }
4720 });
4721
4722 return found || null;
4723 },
4724
4725 /**
4726 * Event handler for when a variant input changes.
4727 */
4728 _onSelectChange: function() {
4729 var variant = this._getVariantFromOptions();
4730
4731 this.$container.trigger({
4732 type: 'variantChange',
4733 variant: variant,
4734 });
4735
4736 if (!variant) {
4737 return;
4738 }
4739
4740 this._updateMasterSelect(variant);
4741 this._updateImages(variant);
4742 this._updatePrice(variant);
4743 this._updateSKU(variant);
4744 this.currentVariant = variant;
4745
4746 if (this.enableHistoryState) {
4747 this._updateHistoryState(variant);
4748 }
4749 },
4750
4751 /**
4752 * Trigger event when variant image changes
4753 *
4754 * @param {object} variant - Currently selected variant
4755 * @return {event} variantImageChange
4756 */
4757 _updateImages: function(variant) {
4758 var variantImage = variant.featured_image || {};
4759 var currentVariantImage = this.currentVariant.featured_image || {};
4760
4761 if (
4762 !variant.featured_image ||
4763 variantImage.src === currentVariantImage.src
4764 ) {
4765 return;
4766 }
4767
4768 this.$container.trigger({
4769 type: 'variantImageChange',
4770 variant: variant,
4771 });
4772 },
4773
4774 /**
4775 * Trigger event when variant price changes.
4776 *
4777 * @param {object} variant - Currently selected variant
4778 * @return {event} variantPriceChange
4779 */
4780 _updatePrice: function(variant) {
4781 if (
4782 variant.price === this.currentVariant.price &&
4783 variant.compare_at_price === this.currentVariant.compare_at_price
4784 ) {
4785 return;
4786 }
4787
4788 this.$container.trigger({
4789 type: 'variantPriceChange',
4790 variant: variant,
4791 });
4792 },
4793
4794 /**
4795 * Trigger event when variant sku changes.
4796 *
4797 * @param {object} variant - Currently selected variant
4798 * @return {event} variantSKUChange
4799 */
4800 _updateSKU: function(variant) {
4801 if (variant.sku === this.currentVariant.sku) {
4802 return;
4803 }
4804
4805 this.$container.trigger({
4806 type: 'variantSKUChange',
4807 variant: variant,
4808 });
4809 },
4810
4811 /**
4812 * Update history state for product deeplinking
4813 *
4814 * @param {variant} variant - Currently selected variant
4815 * @return {k} [description]
4816 */
4817 _updateHistoryState: function(variant) {
4818 if (!history.replaceState || !variant) {
4819 return;
4820 }
4821
4822 var newurl =
4823 window.location.protocol +
4824 '//' +
4825 window.location.host +
4826 window.location.pathname +
4827 '?variant=' +
4828 variant.id;
4829 window.history.replaceState({path: newurl}, '', newurl);
4830 },
4831
4832 /**
4833 * Update hidden master select of variant change
4834 *
4835 * @param {variant} variant - Currently selected variant
4836 */
4837 _updateMasterSelect: function(variant) {
4838 $(this.originalSelectorId, this.$container)[0].value = variant.id;
4839 },
4840 });
4841
4842 return Variants;
4843 })();
4844
4845 /**
4846 * Global functionality
4847 * ---------------------------------------------------------------------------
4848 */
4849
4850 /*
4851 * Basic plugins to handle responsive product images
4852 */
4853 $.fn.extend({
4854 // product grid item click events
4855 productBox: function() {
4856 var $productBox = $(this).find('.box.product figure');
4857 $productBox.on('click', function(e) {
4858 // go to product URL unless clicking on vendor link
4859 if (
4860 $(e.target).is('.vendor') ||
4861 $(e.target)
4862 .parent()
4863 .is('.vendor')
4864 ) {
4865 // log('is vendor');
4866 } else if (
4867 $(e.target).is('.product-swatches__li') ||
4868 $(e.target).closest('.product-swatches__li').length != 0
4869 ) {
4870 var $swatchList = $(e.target).closest('.product-swatches');
4871 var $swatchListItem = $(e.target).closest('.product-swatches__li');
4872 var $swatchLink = $swatchListItem.find('a');
4873 var variantImage = $swatchLink.data('variant-image');
4874 var variantImagePattern = $swatchLink.data('variant-image-pattern');
4875 var variantUrl = $swatchLink.data('variant-url');
4876
4877 var $productCard = $swatchListItem
4878 .closest('.box.product')
4879 .find('a.product_card');
4880 var $productImage = $productCard.find(
4881 '.product_card__image:not(.alt)'
4882 );
4883
4884 if (variantUrl !== '') {
4885 $productCard.attr('href', variantUrl);
4886 }
4887
4888 if (variantImage !== '') {
4889 $productImage
4890 .attr('src', variantImage)
4891 .attr('data-fallback', variantImage)
4892 .attr('srcset', '')
4893 .attr('data-srcset', '')
4894 .attr('data-src', variantImagePattern);
4895 }
4896
4897 $('.product-swatches__link--selected', $swatchList).removeClass(
4898 'product-swatches__link--selected'
4899 );
4900 $swatchLink.addClass('product-swatches__link--selected');
4901
4902 e.preventDefault();
4903 } else {
4904 e.preventDefault();
4905 var productURL = $(this)
4906 .find('a')
4907 .attr('href');
4908 // Open link in new window for tabs
4909 if (e.shiftKey || e.ctrlKey || e.metaKey) {
4910 window.open(productURL, '_blank');
4911 } else {
4912 window.location = productURL;
4913 }
4914 }
4915 });
4916 // IE fix for object-fit
4917 if (!Modernizr.objectfit) {
4918 $productBox.find('.product_card__image-wrapper').each(function() {
4919 var $container = $(this),
4920 $altImg = $container.find('.product_card__image.alt'),
4921 imgUrl = $container.find('img').prop('src');
4922 $altImg.hide();
4923 $container.addClass('ie-fallback lazyload');
4924 });
4925 }
4926 var $productSwatch = $(this).find(
4927 '.box.product a.product-swatches__link'
4928 );
4929
4930 // Preload product images associated with swatch on hover for faster switching
4931 $productSwatch.on('mouseenter', function(e) {
4932 StyleHatch.productSwatchPreload = StyleHatch.productSwatchPreload || [];
4933 var $link = $(e.target).closest('a.product-swatches__link');
4934 var preloadImage = $link.data('variant-image');
4935
4936 if (preloadImage !== '') {
4937 if (StyleHatch.productSwatchPreload.indexOf(preloadImage) < 0) {
4938 StyleHatch.productSwatchPreload.push(preloadImage);
4939 var image = new Image();
4940 image.src = preloadImage;
4941 }
4942 }
4943 });
4944 },
4945 // remove product box
4946 destroyProductBox: function() {
4947 var $productBox = $(this).find('.box.product figure');
4948 $productBox.off('click');
4949 },
4950
4951 // Nested menu
4952 initNestedMenu: function() {
4953 var $nestedNav = $(this);
4954 var $nestedLink = $nestedNav.find('a[aria-haspopup="true"]');
4955
4956 var _closeCurrentChild = function($menu) {
4957 var $expandedItem = $menu.find('li.has-dropdown.expanded');
4958 if ($expandedItem.length > 0) {
4959 $expandedItem.removeClass('expanded');
4960
4961 var $expandedItemLink = $expandedItem.find('> a');
4962 $expandedItemLink.attr('aria-expanded', 'false');
4963
4964 var $dropdown = $expandedItem.find('> ul.dropdown');
4965 $dropdown.attr('aria-hidden', 'true');
4966 $dropdown.slideUp();
4967
4968 var $dropdownLinks = $dropdown.find('a');
4969 $dropdownLinks.attr('tabindex', '-1');
4970 _closeCurrentGrandchild($menu);
4971 }
4972 };
4973 var _closeCurrentGrandchild = function($menu) {
4974 var $expandedItem = $menu.find('li.has-sub-dropdown.expanded');
4975 if ($expandedItem.length > 0) {
4976 $expandedItem.removeClass('expanded');
4977
4978 var $expandedItemLink = $expandedItem.find('> a');
4979 $expandedItemLink.attr('aria-expanded', 'false');
4980
4981 var $dropdown = $expandedItem.find('> ul.sub-dropdown');
4982 $dropdown.attr('aria-hidden', 'true');
4983 $dropdown.slideUp();
4984
4985 var $dropdownLinks = $dropdown.find('a');
4986 $dropdownLinks.attr('tabindex', '-1');
4987 _closeCurrentGrandchild($menu);
4988 }
4989 };
4990
4991 $nestedLink.on('click', function(e) {
4992 var $el = $(this);
4993 var $parentItem = $el.parent();
4994 var $dropdown = $parentItem.find('> ul');
4995 var $dropdownLinks = $parentItem.find('> ul > li > a');
4996 var $menu = $el.closest('ul.nested-menu');
4997
4998 if ($el.attr('aria-expanded') !== 'true') {
4999 e.preventDefault();
5000
5001 if ($parentItem.hasClass('has-dropdown')) {
5002 // child level
5003 _closeCurrentChild($menu);
5004 } else {
5005 // grandchild level
5006 _closeCurrentGrandchild($menu);
5007 }
5008
5009 // Element changes
5010 $el.attr('aria-expanded', 'true');
5011
5012 // Parent changes
5013 $parentItem.addClass('expanded');
5014
5015 // Dropdown changes
5016 $dropdown.attr('aria-hidden', 'false');
5017 $dropdownLinks.attr('tabindex', '0');
5018 $dropdown.slideDown();
5019 }
5020 });
5021 },
5022 destroyNestedMenu: function() {
5023 var $nestedNav = $(this);
5024 var $nestedLink = $nestedNav.find('a[aria-haspopup="true"]');
5025 $nestedLink.off('click');
5026 },
5027 });
5028 /*
5029 * Refresh all fixTo elements
5030 * - called when elements slide in/out
5031 */
5032 StyleHatch.refreshFixTo = function() {
5033 StyleHatch.Promos.refreshFixTo();
5034 var $fixToElements = $('*').filter(function() {
5035 return $(this).data('fixtoInstance');
5036 });
5037 // Only refresh the ones _running
5038 $fixToElements.each(function(i) {
5039 if ($(this).data('fixto-instance')._running) {
5040 $(this).fixTo('refresh');
5041 }
5042 });
5043 };
5044 // Apply fitvids
5045 StyleHatch.videoLayout = function() {
5046 $('.rte').fitVids();
5047 var $table = $('.rte').find('table');
5048 $table.wrap('<div class="table-wrapper"></div>');
5049 };
5050 // Customer account logins
5051 StyleHatch.loginForms = function() {
5052 function showRecoverPasswordForm() {
5053 StyleHatch.cache.$recoverPasswordForm.show();
5054 StyleHatch.cache.$customerLoginForm.hide();
5055 }
5056
5057 function hideRecoverPasswordForm() {
5058 StyleHatch.cache.$recoverPasswordForm.hide();
5059 StyleHatch.cache.$customerLoginForm.show();
5060 }
5061
5062 StyleHatch.cache.$recoverPasswordLink.on('click', function(evt) {
5063 evt.preventDefault();
5064 showRecoverPasswordForm();
5065 StyleHatch.updateHash('recover');
5066 });
5067
5068 StyleHatch.cache.$hideRecoverPasswordLink.on('click', function(evt) {
5069 evt.preventDefault();
5070 hideRecoverPasswordForm();
5071 StyleHatch.updateHash();
5072 });
5073
5074 // Allow deep linking to recover password form
5075 if (StyleHatch.getHash() == '#recover') {
5076 showRecoverPasswordForm();
5077 }
5078 };
5079
5080 // Template specific initalization
5081 StyleHatch.initTemplates = function() {
5082 var $body = StyleHatch.cache.$body;
5083
5084 // Grab the template name from the body
5085 var template = $body.data('template');
5086
5087 // Execute specific functionality
5088 switch (template) {
5089 case 'addresses':
5090 StyleHatch.initCustomerAddressTemplate();
5091 break;
5092
5093 default:
5094 //log('Template: Default');
5095 }
5096 };
5097 // Customer Address Page
5098 StyleHatch.initCustomerAddressTemplate = function() {
5099 if (StyleHatch.addressJSValidation) {
5100 var $submit = $('.customer-address input[type="submit"]');
5101
5102 $submit.on('click', function(e) {
5103 var $form = $(this).closest('form');
5104
5105 // Required fields
5106 var $lastName = $form.find('input[name="address[last_name]"]');
5107 var $address1 = $form.find('input[name="address[address1]"]');
5108 var $city = $form.find('input[name="address[city]"]');
5109 var $country = $form.find('select[name="address[country]"]');
5110 var $province = $form.find('select[name="address[province]"]');
5111 var $zip = $form.find('input[name="address[zip]"]');
5112
5113 if (!$lastName.val()) {
5114 $lastName.addClass('required');
5115 }
5116 if (!$address1.val()) {
5117 $address1.addClass('required');
5118 }
5119 if (!$city.val()) {
5120 $city.addClass('required');
5121 }
5122 if ($country.val() == '---') {
5123 $country.addClass('required');
5124 }
5125
5126 // Check to see if province is showing
5127 if ($province.closest('.input-row').is(':visible')) {
5128 if (
5129 !$province.val() ||
5130 $province.val() == '---' ||
5131 $province.val() == ''
5132 ) {
5133 $province.addClass('required');
5134 }
5135 }
5136
5137 if (!$zip.val()) {
5138 $zip.addClass('required');
5139 }
5140
5141 // Check for focus to clear required
5142 var $required = $form.find('input.required, select.required');
5143 $required.on('focus', function() {
5144 $(this).removeClass('required');
5145 });
5146
5147 // If any required inputs are still here prevent submission
5148 if ($required.length > 0) {
5149 $form
5150 .find('div.errors')
5151 .parent()
5152 .show();
5153 e.preventDefault();
5154 } else {
5155 $form
5156 .find('div.errors')
5157 .parent()
5158 .hide();
5159 }
5160 });
5161 }
5162 };
5163 // Utilities
5164 StyleHatch.updateHash = function(hash) {
5165 if (hash) {
5166 window.location.hash = '#' + hash;
5167 $('#' + hash)
5168 .attr('tabindex', -1)
5169 .focus();
5170 } else {
5171 window.location.hash = '';
5172 }
5173 };
5174 StyleHatch.getHash = function() {
5175 return window.location.hash;
5176 };
5177 // Still used by cart page
5178 StyleHatch.quantitySelect = function() {
5179 // Quantity Selector
5180 var $quantitySelect = $('.quantity-select');
5181 $quantitySelect.each(function() {
5182 var $el = $(this);
5183 var $quantityDown = $el.find('.adjust-minus');
5184 var $quantityUp = $el.find('.adjust-plus');
5185 var $quantity = $el.find('input.quantity');
5186
5187 var quantity = $quantity.val();
5188
5189 $quantityDown.on('click', function(e) {
5190 quantity = $quantity.val();
5191 if (quantity > 1) {
5192 quantity--;
5193 $quantity.val(quantity);
5194 }
5195 e.preventDefault();
5196 });
5197
5198 $quantityUp.on('click', function(e) {
5199 quantity = $quantity.val();
5200 quantity++;
5201 $quantity.val(quantity);
5202
5203 e.preventDefault();
5204 });
5205 });
5206 };
5207 // Reset passwords (store accounts)
5208 StyleHatch.resetPasswordSuccess = function() {
5209 StyleHatch.cache.$passwordResetSuccess.show();
5210 };
5211
5212 /**
5213 * Primary Initialization
5214 * ---------------------------------------------------------------------------
5215 */
5216 $(document).ready(function() {
5217 StyleHatch.init();
5218 });
5219})(jq223);