PageSourceSearch

https://www.bootbarn.com/on/demandware.static/Sites-bootbarn_us-Site/-/default/v1790237465852/js/app.js

js bootbarn.com collected 2026-09-24 10:07:55 UTC 1,734,304 bytes, 47,111 lines download raw bytes

1(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2'use strict';
3
4var _ = require('lodash');
5var AddressVerificationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper');
6var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
7
8var options;
9var dataBuffer;
10var templates;
11
12function initialize(optionsParameter, addressData) {
13	options = $.extend({
14		successCallback: null,
15		errorCallback: null
16	}, optionsParameter);
17	dataBuffer = $.extend({}, addressData);
18
19	if (addressData && addressData.$field && addressData.$form) {
20		templates = {};
21		templates.container = _.template($('#template-address-autocomplete-container').html(), TemplateConstants.Lodash.Settings);
22		templates.address = _.template($('#template-address-autocomplete-item').html(), TemplateConstants.Lodash.Settings);
23
24		var addressSuggestionsHtml = '';
25		if (addressData && addressData.InquiryResult && addressData.InquiryResult.suggestions && addressData.InquiryResult.suggestions.length > 0) {
26			for (var addressSuggestionKey in addressData.InquiryResult.suggestions) {
27				var addressSuggestion = addressData.InquiryResult.suggestions[addressSuggestionKey];
28				addressSuggestionsHtml += templates.address({ Address: buildAddressHtmlFromAddressObject(AddressVerificationHelper.BuildAddressObjectFromAddressAutocompleteSuggestion(addressSuggestion)) });
29			}
30		}
31
32		var $fieldContainer = addressData.$field.parent();
33		if ($fieldContainer.find('.address-autocomplete-dialog').length > 0) {
34			$fieldContainer.find('.address-autocomplete-dialog').remove();
35		}
36
37		var addressSuggestionsContainerHtml = templates.container({ Addresses: addressSuggestionsHtml });
38		$fieldContainer.prepend(addressSuggestionsContainerHtml);
39
40		addressData.$form.off('click.AddressAutocompleteSelect').on('click.AddressAutocompleteSelect', '.address-autocomplete-dialog li', function(event) {
41			var $this = $(this);
42
43			addressData.$form.find('.address-autocomplete-dialog').remove();
44			$(document).trigger('SetAddressFromAddressAutocomplete', {
45				$form: addressData.$form,
46				$field: addressData.$field,
47				Address: AddressVerificationHelper.BuildAddressObjectFromAddressAutocompleteSuggestion(addressData.InquiryResult.suggestions[$this.index()])
48			});
49		});
50
51		$(document).off('click.AddressAutocompleteClose').on('click.AddressAutocompleteClose', function(event) {
52			var $target = $(event.currentTarget);
53
54			if ($target.closest('.address-autocomplete-dialog').length == 0) {
55				$('.address-autocomplete-dialog').remove();
56			}
57		});
58	}
59}
60
61function buildAddressHtmlFromAddressObject(addressObject) {
62	var addressArray = [];
63	if (addressObject.PrimaryLine) {
64		addressArray[addressArray.length] = addressObject.PrimaryLine;
65	}
66	if (addressObject.City || addressObject.StateCode || addressObject.PostalCode) {
67		var addressLine = '';
68		if (addressObject.City) {
69			addressLine += addressObject.City + ', ';
70		}
71		if (addressObject.StateCode) {
72			addressLine += addressObject.StateCode;
73			if (addressObject.PostalCode) {
74				addressLine += ' ';
75			}
76		}
77		if (addressObject.PostalCode) {
78			addressLine += addressObject.PostalCode;
79		}
80
81		addressArray[addressArray.length] = addressLine;
82	}
83
84	return addressArray.join('<br/>');
85}
86
87exports.initialize = initialize;
88},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper":112,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"lodash":133}],2:[function(require,module,exports){
89'use strict';
90
91var _ = require('lodash');
92var dialog = require('../../dialog');
93var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
94
95var options;
96var dataBuffer;
97var templates;
98
99function initialize(optionsParameter, addressType, addressData) {
100	options = $.extend({
101		successCallback: null,
102		continueCallback: null,
103		errorCallback: null
104	}, optionsParameter);
105	dataBuffer = $.extend({}, addressData);
106	templates = {};
107	templates.address = _.template($('#template-address-verification-address').html(), TemplateConstants.Lodash.Settings);
108
109	dialog.open({
110		html: $('#dialog-address-verification').html(),
111		options: {
112			dialogClass: 'dialog-address-verification',
113			width: 320,
114			draggable: false,
115			resizable: false,
116			open: function(event, ui) {
117				var $this = $(this);
118				var $addresses = $this.find('.addresses');
119				$addresses.html('');
120				$this.closest('.ui-dialog').attr('status', dataBuffer.Status);
121
122				var addressRecommendedHtml;
123				if (dataBuffer.Status == 'deliverable' && !dataBuffer.Match) {
124					addressRecommendedHtml = templates.address({
125						AddressType: addressType,
126						Action: 'recommended',
127						Class: 'selected',
128						Checked: 'checked',
129						Header: 'Recommended Address',
130						Address: buildAddressHtmlFromAddressObject(dataBuffer.AddressVerified)
131					});
132				}
133
134				var addressEnteredHtml;
135				addressEnteredHtml = templates.address({
136					AddressType: addressType,
137					Action: 'entered',
138					Class: !addressRecommendedHtml ? 'selected' : '',
139					Checked: !addressRecommendedHtml ? 'checked' : '',
140					Header: 'Address as Entered',
141					Address: buildAddressHtmlFromAddressObject(dataBuffer.AddressOriginal)
142				});
143
144				if (addressRecommendedHtml) {
145					$addresses.append(addressRecommendedHtml);
146				}
147				if (addressEnteredHtml) {
148					$addresses.append(addressEnteredHtml);
149				}
150
151				$this.find('.address-type').text(addressType);
152				$this.find('h3:visible').focus(); // ADA compliance - add focus to first element in modal to help screen readers
153				
154				$this.off('click.AddressSelect').on('click.AddressSelect', '.address-container .radio-large', function(event) {
155					event.preventDefault();
156					var $addressContainer = $(this).closest('.address-container');
157
158					$this.find('.address-container').removeClass('selected');
159					$addressContainer.addClass('selected');
160					$addressContainer.find('input').prop('checked', true);
161				});
162
163				$this.off('click.AddressUse').on('click.AddressUse', '.address-use', function(event) {
164					event.preventDefault();
165					var addressToUse = $(this).closest('.address-container').find('input').val();
166					var addressObject;
167					if (addressToUse == 'recommended') {
168						addressObject = dataBuffer.AddressVerified;
169					} else if (addressToUse == 'entered') {
170						addressObject = dataBuffer.AddressOriginal;
171					}
172
173					if (addressObject) {
174						$(document).trigger('SetAddressFromAddressVerification', $.extend(addressObject, { $form: options.$form, AddressType: addressType }));
175					}
176					$this.dialog('close');
177					if (options.successCallback && typeof options.successCallback == 'function') {
178						options.successCallback(options.$form);
179					}
180				});
181
182				$this.off('click.AddressContinue').on('click.AddressContinue', '.address-continue', function(event) {
183					event.preventDefault();
184					$(document).trigger('SetAddressFromAddressVerification', $.extend(dataBuffer.AddressOriginal, { $form: options.$form, AddressType: addressType }));
185					$this.dialog('close');
186					if (options.continueCallback && typeof options.continueCallback == 'function') {
187						options.continueCallback(options.$form);
188					}
189				});
190				
191				$this.off('click.AddressEdit').on('click.AddressEdit', '.address-edit', function(event) {
192					event.preventDefault();
193					$this.dialog('close');
194					var $focusElement = $('#billingAddressForm').find('input[name="dwfrm_billingaddress_address1"]');
195					if (addressType == 'shipping') {
196						$focusElement = $('#shippingAddressForm').find('input[name="dwfrm_shippingaddress_address1"]');
197					}
198					$focusElement.focus();
199				});
200            },
201            close: function(event, ui) {
202				var $this = $(this);
203				$this.off('click.SelectAddress');
204				$this.off('click.UseEnteredAddress');
205				$this.off('click.UseRecommendedAddress');
206				$this.off('click.EditShippingAddress');
207            }
208		}
209	});
210}
211
212function buildAddressHtmlFromAddressObject(addressObject) {
213	var addressArray = [];
214	if (addressObject.PrimaryLine) {
215		addressArray[addressArray.length] = addressObject.PrimaryLine;
216	}
217	if (addressObject.SecondaryLine) {
218		addressArray[addressArray.length] = addressObject.SecondaryLine;
219	}
220	if (addressObject.City || addressObject.StateCode || addressObject.PostalCode) {
221		var addressLine = '';
222		if (addressObject.City) {
223			addressLine += addressObject.City + ', ';
224		}
225		if (addressObject.StateCode) {
226			addressLine += addressObject.StateCode;
227			if (addressObject.PostalCode) {
228				addressLine += ' ';
229			}
230		}
231		if (addressObject.PostalCode) {
232			addressLine += addressObject.PostalCode;
233		}
234
235		addressArray[addressArray.length] = addressLine;
236	}
237
238	return addressArray.join('<br/>');
239}
240
241exports.initialize = initialize;
242},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../dialog":36,"lodash":133}],3:[function(require,module,exports){
243'use strict';
244
245var progress = require('./progress'),
246    util = require('./util');
247
248var currentRequests = [];
249
250/**
251 * @function
252 * @description Ajax request to get json response
253 * @param {Boolean} async  Asynchronous or not
254 * @param {String} url URI for the request
255 * @param {Object} data Name/Value pair data request
256 * @param {Function} callback  Callback function to be called
257 */
258var getJson = function (options) {
259    options.url = util.toAbsoluteUrl(options.url);
260    // return if no url exists or url matches a current request
261    if (!options.url || currentRequests[options.url]) {
262        return;
263    }
264
265    currentRequests[options.url] = true;
266
267    // make the server call
268    $.ajax({
269        dataType: 'json',
270        url: options.url,
271        async: (typeof options.async === 'undefined' || options.async === null) ? true : options.async,
272        data: options.data || {}
273    })
274    // success
275    .done(function (response) {
276        if (options.callback) {
277            options.callback(response);
278        }
279    })
280    // failed
281    .fail(function (xhr, textStatus) {
282        if (textStatus === 'parsererror') {
283            window.alert(Resources.BAD_RESPONSE);
284        }
285        if (options.callback) {
286            options.callback(null);
287        }
288    })
289    // executed on success or fail
290    .always(function () {
291        // remove current request from hash
292        if (currentRequests[options.url]) {
293            delete currentRequests[options.url];
294        }
295    });
296};
297/**
298 * @function
299 * @description ajax request to load html response in a given container
300 * @param {String} url URI for the request
301 * @param {Object} data Name/Value pair data request
302 * @param {Function} callback  Callback function to be called
303 * @param {Object} target Selector or element that will receive content
304 */
305var load = function (options) {
306    options.url = util.toAbsoluteUrl(options.url);
307    // return if no url exists or url matches a current request
308    if (!options.url || currentRequests[options.url]) {
309        return;
310    }
311
312    currentRequests[options.url] = true;
313
314    // make the server call
315    $.ajax({
316        dataType: 'html',
317        url: util.appendParamToURL(options.url, 'format', 'ajax'),
318        data: options.data,
319        xhrFields: {
320            withCredentials: true
321        }
322    })
323    .done(function (response) {
324        // success
325        if (options.target) {
326            $(options.target).empty().html(response);
327        }
328        if (options.callback) {
329            options.callback(response);
330        }
331    })
332    .fail(function (xhr, textStatus) {
333        // failed
334        if (textStatus === 'parsererror') {
335            window.alert(Resources.BAD_RESPONSE);
336        }
337        options.callback(null, textStatus);
338    })
339    .always(function () {
340        progress.hide();
341        // remove current request from hash
342        if (currentRequests[options.url]) {
343            delete currentRequests[options.url];
344        }
345    });
346};
347
348exports.getJson = getJson;
349exports.load = load;
350
351},{"./progress":86,"./util":102}],4:[function(require,module,exports){
352'use strict';
353
354/**
355 * @function
356 * @description Initializes the alert widget
357 */
358exports.init = function(options) {
359    var type = options.type ? options.type : 'alert-primary';
360    var timer = options.timer ? options.timer : 5000;
361    var alertElement = '<div class="alert ' + type + '"><div class="alert-content">' + options.content + '</div></div>';
362
363    $('body').append(alertElement).fadeIn('slow');
364
365    if (timer >= 0) {
366        var alertTimer = setTimeout(function() {
367            $('.alert').fadeOut('slow').remove();
368        }, timer);
369    }
370
371    $('body').on('click', '.alert', function() {
372        $(this).fadeOut('slow').remove();
373    });
374};
375
376},{}],5:[function(require,module,exports){
377/**
378 *    (c) 2009-2014 Demandware Inc.
379 *    Subject to standard usage terms and conditions
380 *    For all details and documentation:
381 *    https://bitbucket.com/demandware/sitegenesis
382 */
383
384'use strict';
385
386var countries = require('./countries'),
387    dialog = require('./dialog'),
388    minicart = require('./minicart'),
389    page = require('./page'),
390    rating = require('./rating'),
391    searchplaceholder = require('./searchplaceholder'),
392    //tooltip = require('./tooltip'),
393    util = require('./util'),
394    validator = require('./validator'),
395    tls = require('./tls'),
396  	TPromise = require('promise'),
397  	ajax = require('./ajax'),
398  	rotate3d = require('./productRotate3d.js'),
399    count_down = require('./countdown'),
400    lazyloadPageElements = require('./lazyloadPageElements'),
401    notification = require('./components/Notification'),
402    navigation = require('./components/NavigationController'),
403    emailForm = require('./components/EmailForm'),
404	slickHelpers = require('./slickSliderHelpers'),
405	quantityStepper = require('./quantityStepper'),
406	wishlist = require('./wishlist'),
407	loginDialog = require('./loginDialog'),
408	register = require('./register'),
409	alert = require('./alert'),
410	Globale = require('./components/Globale'),
411	searchbar = require('./searchbar'),
412	sliders = require('./sliders'),
413	cordialTagging = require('./components/CordialTagging');
414
415var login = {};
416if (window.SiteID === 'bootbarn_workforce_us') {
417	login = require('../../../app_storefront_workforce/cartridge/js/login');
418} else {
419	login = require('./login');
420}
421
422var EmailSignup = require('./components/EmailSignup');
423var giftCardBalanceInquiryHelper = require('./helpers/GiftCardBalanceInquiryHelper');
424var GoogleTagManagerClient = require('../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
425var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
426
427// if jQuery has not been loaded, load from google cdn
428if (!window.jQuery) {
429    var s = document.createElement('script');
430    s.setAttribute('src', 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js');
431    s.setAttribute('type', 'text/javascript');
432    document.getElementsByTagName('head')[0].appendChild(s);
433}
434
435require('./jquery-ext')();
436require('./cookieprivacy')();
437require('./captcha')();
438require('./watermark')();
439
440var dataBuffer = {};
441var siteName;
442
443var $sliderMobile;
444var $navSecondary;
445var $navUtilities;
446var $navUl;
447var $topBannerBar;
448var $topBannerBarInner;
449var $navigation;
450var $topBannerInnerLinks;
451var $selectSort;
452var $secondaryHead;
453var $searchResultContainer;
454var $productContainerImage;
455var $productDetail;
456var $sectionCheckout;
457var $mainInnerAlt;
458var $sectionErrorFigure;
459var $sectionErrorBox;
460var $footerMenuItems;
461var $promoMessages;
462
463function initializeEvents() {
464	notification.initialize();
465	var controlKeys = ['8', '13', '46', '45', '36', '35', '38', '37', '40', '39'];
466	var $win = $(window);
467	var $doc = $(document);
468	$doc.on('app.resize', function () {
469		resizeDeviceHelper();
470	});
471	$win.on('load resize', function () {
472		resizeDeviceHelper();
473	});
474	
475	$(document).off('CategoryHeaderSliders.Initialize').on('CategoryHeaderSliders.Initialize', function(event) {
476		sliders.initializeCategorySliders('.cat-header-content.cat-header-slider:not(.slick-initialized)');
477	});
478
479	$(document).off('PostAjaxAuthenticationActions.Initialize').on('PostAjaxAuthenticationActions.Initialize', function(event, options) {
480		var data = options.results;
481		var isAccountDialog = options.isAccountDialog;
482		if (isAccountDialog) {
483			var customerFirstName = data.customerFirstName ? ', ' + data.customerFirstName + '.' : '.';
484			var alertMessage;
485			if (options.displayMessage == 'login') {
486				alertMessage = 'Welcome back' + customerFirstName;
487			}
488			if (options.displayMessage == 'register') {
489				alertMessage = 'Thanks for creating an account' + customerFirstName;
490			}
491			
492			if (SitePreferences.WISHLIST_ENABLED) { // Future-forward: in case we ever implement login/register dialogs other than with wishlist
493				/**** Wishlist icon updates ****/
494				var wishlistItems = data.controllerResults && data.controllerResults.allWishlistItems ? data.controllerResults.allWishlistItems : data.allWishlistItems ? data.allWishlistItems : null;
495				var returnedResult = data.controllerResults && data.controllerResults.result ? data.controllerResults.result : data.result ? data.result : null;
496				if (wishlistItems != null) {
497					window.localStorage.WishlistProductIDs = JSON.stringify(wishlistItems);
498					wishlist.renderAllWishlistButtons();
499					if (returnedResult == 'complete') {
500						alertMessage += ' This item is already saved to your Wishlist.';
501					} else {
502						alertMessage += ' An item has been added to your Wishlist.';
503					}
504				}
505			}
506
507			/**** Selected store updates ****/
508			var storeFromProfile = data.storeFromProfile ? data.storeFromProfile : null;
509			var addToCartOptionsStoreId = $('.bopis-add-to-cart-options-store-selector .selected-store').length > 0 ? $('.bopis-add-to-cart-options-store-selector .selected-store').attr('store-id') : null;
510			var navStoresDropdownStoreId = $('.stores-dropdown .current-store .map.store').length > 0 ? $('.stores-dropdown .current-store .map.store').attr('data-id') : null;
511			var storeID;
512
513			var isProductPage = $('.pt_product-details').length;
514			var isCategory = $('.pt_product-search-result').length;
515			if (isProductPage) {
516				storeID = addToCartOptionsStoreId ? addToCartOptionsStoreId : storeFromProfile ? storeFromProfile : null;
517				if (storeID) {
518					// Redraw the "My Store" piece on the PDP
519					$(document).trigger('Bopis.StoreSearch.Select', storeID);
520				}
521			}
522			if (isCategory) {
523				storeID = navStoresDropdownStoreId ? navStoresDropdownStoreId : storeFromProfile ? storeFromProfile : null;
524			}
525			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
526				if (isCategory) {
527					if (storeID) {
528						// Make sure to update the store number in customer profile when on the PLP
529						$(document).trigger('Bopis.AssignStoreToProfile', storeID);
530					} 
531				}
532				if (storeID) {
533					// Redraw the selected store pieces in nav, Shop Your Store on PLP, My Account store drop-down, and store search modal
534					$(document).trigger('Bopis.SelectedStore.Refresh', storeID);
535				}
536			}
537
538			/**** Minicart update ****/
539			if (data.cartQuantity) {
540				var $miniCartElm = $('.mini-cart .minicart-quantity').length > 0 ? $('.mini-cart .minicart-quantity') : $('.mini-cart .btn-minicart .btn-minicart-qty');
541				$miniCartElm.html(data.cartQuantity);
542			}
543
544			/**** My Account navigation update ****/
545			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
546				$('.login-dropdown .user-panel .form-login').addClass('d-none');
547				$('.login-dropdown .login-dropdown-customer-menu').removeClass('d-none');
548			} else {
549				var profileName = data.customerFirstName ? ' ' + data.customerFirstName : '';
550				$('.mobile-account-main.first-name a').first().find('span').html('Hello' + profileName);
551			}
552
553			/**** Client Window > Customer object update ****/
554			Customer.authenticated = true;
555			Customer.registered = true;
556			
557			/**** Close the account dialog ****/
558			dialog.close();
559
560			/**** Show the alert ****/
561			alert.init({
562				type: 'alert-primary',
563				timer: 3000,
564				content: alertMessage
565			});
566		} else {
567			window.location.href = data.registerDialogContinueUrl ? data.registerDialogContinueUrl : data.loginDialogContinueUrl ? data.loginDialogContinueUrl : location.reload();
568		}
569	});
570
571	// Dom-Ready Sliders
572	sliders.init('.barn-slider', {dots: false, breakpoints: {0: {slidesToShow: 1}, 1024: {slidesToShow: 2}}});
573	sliders.init('.content-tiles-slider.has-slider .row', {spaceBetween: 2, loop: false, dots: false, breakpoints: {0: {slidesToShow: 1.5}, 768: {slidesToShow: 3}}});
574	sliders.init('.brands-slider.has-slider.slider-mobile-overflow .row', {spaceBetween: 40, loop: false, dots: false, breakpoints: {0: {slidesToShow: 2.5}, 768: {slidesToShow: 6}}});
575	sliders.init('.store-offerings-slider.has-slider .row', {spaceBetween: 20, loop: false, dots: false, breakpoints: {0: {slidesToShow: 2}, 768: {slidesToShow: 4}}});
576	sliders.init('.store-ads-slider.has-slider .row', {spaceBetween: 10, loop: false, dots: false, breakpoints: {0: {slidesToShow: 1.5}, 768: {slidesToShow: 3}}});
577	sliders.init('.einstein-product-recos-slider.has-slider .row', {spaceBetween: 20, loop: false, dots: false, breakpoints: {0: {slidesToShow: 2}, 768: {slidesToShow: 4}}});
578	sliders.init('.single-hero-slider.has-slider .row', {spaceBetween: 0, loop: false, dots: false, breakpoints: {0: {slidesToShow: 1}, 768: {slidesToShow: 1}}});
579	sliders.init('.content-tiles-m2-slider.has-slider .row', {spaceBetween: 2, loop: false, dots: false, breakpoints: {0: {slidesToShow: 2}, 768: {slidesToShow: 3}}});
580	sliders.init('.collections-content.has-slider .curated', {loop: false, dots: false, breakpoints: {0: {slidesToShow: 1}, 768: {slidesToShow: 3}}});
581	sliders.init('.section-offers.tiles .section-body', {dots: false, breakpoints: {0: {slidesToShow: 1}, 480: {slidesToShow: 3}, 768: {slidesToShow: 4}}});
582	sliders.init('.section-offers.section-brands .brand-list', {dots: false, breakpoints: {0: {slidesToShow: 4}, 480: {slidesToShow: 3}, 768: {slidesToShow: 6}}});
583	sliders.init('.pt_storefront .product-recommendations-sfcc .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {dots: false, breakpoints: {0: {slidesToShow: 1}, 480: {slidesToShow: 3}, 768: {slidesToShow: 4}}});
584	sliders.init('.product-banner-slots-carousel.swiper .swiper-wrapper', {dots: false, loop: false, breakpoints: {0: {slidesToShow: 1}, 768: {slidesToShow: 1}}});
585
586	// Tabbed Sliders
587	if ($('.tabbed-slider').length > 0) {
588		var $tabbedSlidersParent = $('<div class="tabbed-sliders-container"></div>');
589		var $tabbedButtons = $('<div class="slider-panel-buttons"></div>');
590
591		$('.tabbed-slider').each(function(i, slider) {
592			var $tabbedSlider = $(slider);
593			var $header = $tabbedSlider.find('.section-head');
594			var $button = $tabbedSlider.find('.slider-panel-btn');
595			var $panel = $tabbedSlider.find('.section-body');
596			var isFeatured = $tabbedSlider.attr('data-tab-featured');
597
598			if (isFeatured == 'true') { // Slot attribute that allows Site Managers to select a default tab to show on render
599				$button.addClass('slider-panel-btn-active');
600				$panel.addClass('slider-panel-active');
601			}
602			if (i == 0) {
603				$tabbedSlidersParent.insertBefore($tabbedSlider);
604				$tabbedSlidersParent.append($header).append($tabbedButtons);
605			}
606			$tabbedButtons.append($button);
607			$tabbedSlidersParent.append($tabbedSlider);
608		});
609		if ($('.slider-panel-active').length <= 0) { // If Site Managers didn't select a default tab to show, go ahead and activate the first tab
610			var $activateTab = $('.tabbed-slider').first();
611			$activateTab.find('.slider-panel-btn').addClass('slider-panel-btn-active');
612			$activateTab.find('.section-body').addClass('slider-panel-active');
613		}
614		initTabPanels();
615	}
616
617	$('.pt_storefront .product-recommendations-sfcc .accordion-head').addClass('active');
618	$('.pt_storefront .product-recommendations-sfcc .accordion-body').show();
619
620	if ($(document).find('.jumbotron-mobile').length > 0 && window.innerWidth <= 767) {
621		var jumbotronExpandedCookie = getCookie('jumbotronExpandedCookie');
622		if (jumbotronExpandedCookie == 'true' && $('#jumbotron-current-offers-icon').hasClass('fa-angle-down')) {
623			$('.jumbotron-mobile').toggleClass('active');
624			$('#jumbotron-current-offers-icon').removeClass('fa-angle-down');
625			$('#jumbotron-current-offers-icon').addClass('fa-angle-up');
626		} else if(jumbotronExpandedCookie == 'false' && $('#jumbotron-current-offers-icon').hasClass('fa-angle-up')) {
627			$('.jumbotron-mobile').toggleClass('active');
628			$('#jumbotron-current-offers-icon').removeClass('fa-angle-up');
629			$('#jumbotron-current-offers-icon').addClass('fa-angle-down');
630		}
631	}
632		
633	if(window.innerWidth > 767 ) {
634		$('.mobile-only .guest-checkout-form').remove();
635		$('.mobile-only .auto-checkout-form').remove();
636	} else {
637		$('.mobile-hidden .guest-checkout-form').remove();
638		$('.mobile-hidden .auto-checkout-form').remove();
639	}
640	
641	$('#guest_checkout_optin:checkbox').change(function() {
642		if ($('#guest_checkout_optin').prop("checked")) {
643			$('.optin-input').addClass('checked');
644		} else {
645			$('.optin-input').removeClass('checked');
646		}
647	});
648	
649	$('#auto_checkout_optin:checkbox').change(function() {
650		if ($('#auto_checkout_optin').prop("checked")) {
651			$('.optin-input').addClass('checked');
652		} else {
653			$('.optin-input').removeClass('checked');
654		}
655	});
656	
657	$('#checkout_email_optin:checkbox').change(function() {
658		if ($('#checkout_email_optin').prop("checked")) {
659			$('.optin-input').addClass('checked');
660		} else {
661			$('.optin-input').removeClass('checked');
662		}
663	});
664	
665	//rewards auto-enrollment points earned message
666	if ($(document).find('.pt_order-confirmation').length > 0) {
667		var orderTotal = $('.order-total').text().replace('$','');
668		var earnedPoints = Math.round(orderTotal);
669		$('span.rewardsEarnedPoints').text(earnedPoints);
670	}
671
672	// selected states for account navigation
673	if ($(document).find('.pt_account, .pt_order, .pt_customer-service, .pt_wish-list').length > 0) {
674		var currentPage = window.location.origin + window.location.pathname;
675		var isOnTopLevelPage = false;
676		$('#secondary nav a')
677			.removeClass('is-active')
678			.each(function(index, value) {
679				if (value.href === currentPage) {
680					$(this).addClass('is-active');
681					isOnTopLevelPage = true;
682				}
683		});
684		if (!isOnTopLevelPage) {
685			$('#secondary nav a').each(function(index, value) {
686				if (value.href === document.referrer) {
687					$(this).addClass('is-active');
688				}
689			});
690		}
691	}
692
693	// ---------------- ADA Fixes -------------------
694	// Slick slider fixes
695	$(document).trigger('SlickSliderAccessibilityMods');
696	// Vendor fixes
697	var adaTagIntervalCounter = 0;
698	var adaTagInterval = setInterval(function(){ 
699		if (!$('img[src*="arttrk.com"]').is('[alt]')) {
700			$('img[src*="arttrk.com"]').attr('alt','Tracking pixel');
701		}
702		if (!$('#fileUploadForm input#fileSelector').is('[aria-label]')) {
703			$('#fileUploadForm input#fileSelector').attr('aria-label', 'Select file to upload');
704		}	
705		if (adaTagIntervalCounter > 9 || ($('img[src*="arttrk.com"]').is('[alt]') && $('#fileUploadForm input#fileSelector').is('[aria-label]'))) {
706			clearInterval(adaTagInterval);
707		}
708		adaTagIntervalCounter++;
709	}, 1000); // 1000 ms = start after 1sec 
710	
711	$(document).ready(function() {
712		$('#g-recaptcha-response').attr('tabindex','-1');
713	});
714
715	$(document).off('click.ShippingWarnings').on('click.ShippingWarnings','.shipping-warnings .shipping-warning-button', function(e) {
716		var $button = $(this);
717		if ($button.hasClass('shipping-warning-button-closed')) {
718			$button.removeClass('shipping-warning-button-closed').addClass('shipping-warning-button-open').attr('aria-expanded','true');
719			$button.siblings('ul.shipping-warning-product-items').show();
720		} else if ($button.hasClass('shipping-warning-button-open')) {
721			$button.removeClass('shipping-warning-button-open').addClass('shipping-warning-button-closed').attr('aria-expanded','false');
722			$button.siblings('ul.shipping-warning-product-items').hide();
723		}
724	});
725
726	// Skip link for product recommendations container
727	$(document).off('click.Skip.Recommendations').on('click.Skip.Recommendations','.skip-product-recommendations-container', function(e) {
728		$(this).siblings('.product-recommendations-container-dive').focus();
729	});
730
731
732	$(document).off('click.Zoom.Skip.Section').on('click.Zoom.Skip.Section','.product-image-container-mobile .skip-to-next', function(e) {
733		e.preventDefault();
734		var isSkipToWishlistButton = $(this).attr('href').indexOf('pdp-wishlist-button-container') > -1;
735		if (isSkipToWishlistButton) {
736			$('#pdp-wishlist-button-container .wishlist-item-status-button').addClass('focus-visible-indicator').focus();
737		} else {
738			window.location = $(this).attr('href');
739		}
740	});
741
742	// --- Initialize Events: ADA Compliance Keyboard Tab Visual Indicator ---
743	$(document).on('keyup', function (event) {
744		if (event.which === SiteConstants.KeyCodes.Tab) {
745			$(':focus').addClass('focus-visible-indicator');
746			if ($(':focus').hasClass('visually-hidden')) {
747				// Get the innards of the current focus element and copy them into the hidden-element-memo that is then prepended to the <body>
748				var htmlText = $(':focus').html();
749				var $hiddenElementMemo = $('<div/>', {"class": "hidden-element-memo"}).html(htmlText);
750				$('body').prepend($hiddenElementMemo);
751
752				// Clear memo after 3 seconds if it has not already been cleared
753				setTimeout(function() { 
754					$('.hidden-element-memo').remove();
755				}, 3000); // 3000 ms = start after 1sec 
756			}
757		}
758	}).on('keydown keypress mousemove', function (event) {
759		$('.focus-visible-indicator').removeClass('focus-visible-indicator');
760		$('.hidden-element-memo').remove();
761	});
762
763	$(document).on('init.Slider.Aria', '.slider', function(event, slick) {
764		$(this).find('.slick-list').removeAttr('aria-live');
765	}).on('click.Slider.Aria keyup.Slider.Aria', function(event) {
766		if (event.target.matches('.slider, .slider *')) {
767			if (!$(event.target).closest('.slider').find('.slick-list').attr('aria-live')) {
768				$(event.target).closest('.slider').find('.slick-list').attr('aria-live', 'polite');
769			}
770		} else {
771			$('.slider').find('.slick-list').removeAttr('aria-live');
772		}
773	});
774
775    $.fn.hasAttr = function(name) {
776		return this.attr(name) !== undefined;
777	};
778	
779	function CheckloginCreate() {
780		var loginCreateCount = $(".login-create-account").length;
781		if(loginCreateCount == 0) {
782		$(".order-confirmation-details").addClass("fullWidth");
783		}else {
784			$(".order-confirmation-details").removeClass("fullWidth");
785		}
786	}
787	
788	// birthday month has to be selected first
789	// which then adjusts the day options
790	// BB registration form and my account page
791	$(document).ready(function() {
792		var $birthdayForm = $('#dwfrm_rewardsregistration');
793		if ($('#dwfrm_birthday').length >= 1 ){
794			var $birthdayForm = $('#dwfrm_birthday');
795		}
796		
797		$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').prop('disabled', true);
798		var longMonths = ['1','3','5','7','8','10','12'];
799		var febMonth = '2';
800		$birthdayForm.on('change', 'select#dwfrm_rewardsregistration_rewardscustomer_birthday_month', function(e) {
801			$('.birthday.day').removeClass('disabled');
802			$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').find('option:selected').prop('selected', false);
803			$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option').prop('disabled', false);
804			var monthVal = $('#dwfrm_rewardsregistration_rewardscustomer_birthday_month').val();
805			if ($.inArray(monthVal, longMonths) === -1) {
806				$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option[value="31"]').prop('disabled', true);
807				if (monthVal == febMonth){
808					$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option[value="30"]').prop('disabled', true);
809				}
810			}
811			$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').prop('disabled', false);
812		});
813	});
814	
815	// set customer birthday
816	$(document).on('click','#dwfrm_birthday button.confirm', function(event){
817		event.preventDefault();
818		var email = $('.credential-list--email').text().trim();
819		var birthdayField = $('#dwfrm_birthday');
820		var birthmonth = birthdayField.find('.birthday.month select');
821		var birthday = birthdayField.find('.birthday.day select');
822		var year = '1976';
823		var month, day, birthdate;
824		
825		if (!birthmonth.val()) {
826			birthmonth.addClass('error-red');
827		} else {
828			birthmonth.removeClass('error-red');
829			month = birthmonth.find("option:selected").text();
830		}
831		
832		if (!birthday.val()) {
833			birthday.addClass('error-red');
834		} else {
835			birthday.removeClass('error-red');
836			day = birthday.find("option:selected").text();
837		}
838		
839		if (birthdayField.find('.error-red').length > 0){
840			return;
841		}
842		
843		birthdate = month + '/' + day;
844		
845		$.ajax({
846			type: 'post',
847			url: Urls.setBirthday,
848			contentType: 'application/json',
849			data: JSON.stringify({
850				email: email,
851				month: month,
852				day: day,
853				year: year
854			}),
855			success: function (data) {
856				$('#dwfrm_birthday').remove();
857				$('.credential-list--birthdate').text(birthdate);
858				
859				if(SitePreferences.CORDIAL_ENABLED){				
860					var auth_data = {
861						email: email
862					}
863					var contact_data = {
864					    'channels': {
865					        'email': {
866					            'address': email
867					        }
868					    },
869					    'birthday': birthdate + '/' + year
870					};
871	
872					if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
873						crdl('contact', auth_data, contact_data);
874					}
875				}
876			}
877		});
878	});
879	
880	if(siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.PINTEREST_ENABLED){	
881		if ($(document).find('.pt_product-details').length > 0) {
882			setTimeout(function(){   	
883				var thisItem = JSON.parse($('.productCardMetadata.pdp-browse').attr('data-product-gtm-json'));
884				var productID = thisItem["id"];
885				var dateTime = Math.floor(Date.now() / 1000);
886				var eventID = 'pdp-' + productID + '-' + dateTime;
887
888			  	pintrk('track', 'pagevisit', {
889			  		event_id: eventID,
890			  		line_items: [
891			  			{
892					  		product_name: thisItem["name"].replace(/\\/g, ""),
893					  		product_id: productID,
894					  		product_category: thisItem["category"].replace(/\\/g, ""),
895					  		product_price: thisItem["price"],
896					  		product_brand: thisItem["brand"]
897			  			}
898			  		]
899			  	});
900			}, 3000);					
901		}
902		
903		// checkout event created on confirmation	
904		if ($(document).find('.order-confirmation-details').length > 0) {
905			var orderID = $('.orderdetails .sectionheader h3').text().replace('Order Number:','').trim();
906			var dateTime = Math.floor(Date.now() / 1000);
907			var eventID = 'checkout-' + orderID + '-' + dateTime;
908			var currency = 'USD';
909			
910			var items = [];
911			$('.order-item').each(function(index, el) {
912				var _this = $(this);
913				items[index] = {};
914				_this.find('.productCardMetadata').each(function(key, v) {
915					var thisItem = JSON.parse($(this).attr('data-product-gtm-json'));
916					var currency = thisItem["currency"];
917					var product = {};	
918					product.product_name = thisItem["id"];
919					product.product_variant_id = thisItem["skuid"];
920					product.product_name = thisItem["name"].replace(/\\/g, "");
921					product.product_brand = thisItem["brand"];
922					product.product_quantity = thisItem["quantity"];
923					product.product_price = thisItem["price"];
924					items[index]= product;
925				});
926			});
927
928			pintrk('track', 'checkout', {
929				event_id: eventID,
930				value: parseFloat($('.orderdetails .total .order-total').text().replace('$','')).toFixed(2),
931				order_quantity: Math.round($('.orderdetails span.item-count').data('count')),
932				currency: currency,
933				order_id: orderID,
934				line_items: items
935			});
936		}
937	}
938	
939	// CORDIAL ACCOUNT SIGN-UP, BROWSE, CART, ORDER
940	if(SitePreferences.CORDIAL_ENABLED){
941		// homepage browse
942		if ($(document).find('[content-slot-id="homepage-01"]').length > 0) {		
943			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
944				crdl('event', 'homepage_browse');
945			}		
946		}
947		
948		// plp browse
949		if ($(document).find('.pt_product-search-result').length > 0) {
950			var maxTiles = 10;
951			var products = [];
952			$('.product-tile').each(function(i) {
953				if (i < maxTiles) {
954					var product = $(this).attr('data-itemid');
955					products.push(product);
956				}
957			});
958			
959			var properties = {
960			    "category": $('#categoryTitle').text().replace(/\\/g, "").trim(),
961			    "url": $(location).attr("href").split("?utm")[0].split("#utm")[0].split("&utm")[0],
962			    "products": products
963			};
964			
965			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
966				crdl('event', 'category_browse', properties);
967			}		
968		}
969		
970		// pdp browse
971		if ($(document).find('.pt_product-details').length > 0) {
972			setTimeout(function(){   	
973				var thisItem = JSON.parse($('.productCardMetadata.pdp-browse').attr('data-product-gtm-json'));
974			  	var images = [];
975				$('.product-primary-image [class*="-slide"]').each(function () {
976					var image = $(this).find('a').attr('href');
977					if (!image || image == undefined || image == null) {
978						image = $(this).find('img').attr('src');
979					}
980					if (images.indexOf(image) === -1) {
981						images.push(image);
982					}
983				});
984				
985			  	var properties = {
986					"productID": thisItem["id"],
987					"productName": thisItem["name"].replace(/\\/g, ""),
988					"category": thisItem["category"].replace(/\\/g, ""),
989					"price": thisItem["price"],
990					"images": images,
991			  		"url": $(location).attr("href").split("?utm")[0].split("#utm")[0].split("&utm")[0],
992					"attr": {
993			            "manufacturer": thisItem["brand"]
994			        },
995				};
996				
997				if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
998			  		crdl('event', 'product_browse', properties); 
999				}
1000			}, 3000);					
1001		}
1002		
1003		// add Cordial contact from checkout
1004		$('#submitBilling').on('click', function(e){
1005	    	e.preventDefault();
1006	    	if ($('.bill-address-fields .field.error').length == 0) {
1007				var email = $('#dwfrm_billingaddress_email').val().trim();
1008				var firstName = $('#dwfrm_billingaddress_firstName').val();
1009				var lastName = $('#dwfrm_billingaddress_lastName').val();
1010				var phone = $('#dwfrm_billingaddress_phone').val();
1011				var zip = $('#dwfrm_billingaddress_postal').val();
1012				var country = $('#dwfrm_billingaddress_country').val();
1013				var state = $('#dwfrm_billingaddress_states_state').val();
1014				var city = $('#dwfrm_billingaddress_city').val();
1015						
1016				if ($('input#newslettersignup').is(':checked')){
1017					var auth_data = {
1018						email: email
1019					}
1020					var contact_data = {
1021					    'channels': {
1022					        'email': {
1023					            'address': email,
1024					            'subscribeStatus': 'subscribed'
1025					        }
1026					    },
1027						'first_name': firstName,
1028						'last_name': lastName,
1029						'phone': phone,
1030						'postal_code': zip,
1031						'signup_source': 'checkout',
1032						'welcome_signup': true,
1033						'geo_location': {
1034						    'postal_code': zip,
1035						    'country': country,
1036						    'state': state,
1037						    'city': city
1038						}
1039					};
1040					
1041					var properties = {
1042					    "signup_source": 'checkout',
1043					    "welcome_signup": true
1044					};
1045		
1046					if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1047						crdl([
1048							['contact', auth_data, contact_data],
1049							['event', 'email_signup', properties]
1050						]);
1051					}
1052				}
1053	    	}
1054		});
1055		
1056		// update Cart when qty change or removal in cart
1057		function cordialUpdateCart() {
1058			setTimeout(function(){
1059				var items = [];
1060				$('.order-item').each(function(index, el) {
1061					var _this = $(this);
1062					items[index] = {};
1063					_this.find('.productCardMetadata').each(function(key, v) {
1064						var thisItem = JSON.parse($(this).attr('data-product-gtm-json'));
1065						var product = {};	
1066						product.productID = thisItem["id"];
1067						product.sku = thisItem["skuid"];
1068						product.category = thisItem["category"].replace(/\\/g, "");
1069						product.name = thisItem["name"].replace(/\\/g, "");
1070						product.qty = thisItem["quantity"];
1071						product.itemPrice = thisItem["price"];
1072						product.images = [_this.find('img').attr('src')];
1073						product.url = window.location.origin + _this.find('.name').find('a').attr('href').split("?utm")[0].split("#utm")[0].split("&utm")[0];
1074						product.attr = {'manufacturer': thisItem["brand"], 'color': thisItem["color"], 'size': thisItem["size"] };
1075						items[index]= product;
1076					});
1077				});
1078				
1079				var cart_data = items;
1080		
1081				if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1082					crdl([
1083						['cart', 'clear'],
1084						['cartitem', 'add' , cart_data], 
1085						['event', 'cart']
1086					]); 
1087				}
1088			}, 3000);
1089		}
1090		
1091		function cordialClearCart() {
1092			setTimeout(function(){
1093				var properties = {
1094				    'status': 'empty'
1095				};
1096				
1097				if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1098					crdl([
1099						['cart', 'clear'],
1100						['event', 'cart', properties]
1101					]); 
1102				}
1103			}, 3000);
1104		}
1105		
1106		if ($(document).find('.pt_cart').length > 0 || $(document).find('.pt_onepcheckout').length > 0) {
1107			if($('.order-item').length > 0){
1108				cordialUpdateCart();
1109		    } else {		    	
1110		    	cordialClearCart();
1111		    }
1112		}
1113		
1114		$('body').on('change', 'input.opc-qty', function(event) {
1115			cordialUpdateCart();
1116		});
1117		
1118		$('body').on('click', '.cart-change', function(event) {
1119			setTimeout(function(){
1120				if($('.order-item').length == 0){
1121					cordialClearCart();
1122			    } else {
1123			    	cordialUpdateCart();
1124			    }	
1125	    	}, 2000);
1126		});
1127		
1128		// order event created on confirmation; myaccount signup	
1129		if ($(document).find('.order-confirmation-details').length > 0) {
1130			var orderID = $('.order-history-info span.value').first().text().trim();
1131			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
1132				var orderID = $('.orderdetails .sectionheader h3').text().replace('Order Number:','').trim();
1133			}
1134			var email = $('.order-billing-email').text().trim();
1135			var shippingName = $('.order-shipping-name').text().trim();
1136			var shippingAddress2 = $('.order-shipping-address2').text().trim();
1137			var shippingAddress3 = $('.order-shipping-address3').text().trim();
1138			var shippingAddress = $('.order-shipping-address1').text().trim();
1139			if(shippingAddress2) { 
1140				shippingAddress += ", " + shippingAddress2;
1141			}
1142			if(shippingAddress3) { 
1143				shippingAddress += ", " + shippingAddress3;
1144			}
1145			var shippingCity = $('.order-shipping-geo .city').text().trim().replace(',','');
1146			var shippingState = $('.order-shipping-geo .state').text().trim();
1147			var shippingZip = $('.order-shipping-geo .zip').text().trim();
1148			var shippingCountry = $('.order-shipping-country').text().trim();
1149			var billingName = $('.order-billing-name').text().trim();
1150			var billingAddress2 = $('.order-billing-address2').text().trim();
1151			var billingAddress3 = $('.order-billing-address3').text().trim();
1152			var billingAddress = $('.order-billing-address1').text().trim();
1153			if(billingAddress2) { 
1154				billingAddress += ", " + billingAddress2;
1155			}
1156			if(billingAddress3) { 
1157				billingAddress += ", " + billingAddress3;
1158			}
1159			var billingCity = $('.order-billing-geo .city').text().trim().replace(',','');
1160			var billingState = $('.order-billing-geo .state').text().trim();
1161			var billingZip = $('.order-billing-geo .zip').text().trim();
1162			var billingCountry = $('.order-billing-country').text().trim();
1163			var billingPhone = $('.order-billing-phone').text().replace('Phone:','').trim();
1164			
1165			var name = billingName.split(' ');
1166			var firstName = name[0];
1167			var lastName = name[name.length-1];
1168			
1169			var items = [];
1170			$('.order-item').each(function(index, el) {
1171				var _this = $(this);
1172				items[index] = {};
1173				_this.find('.productCardMetadata').each(function(key, v) {
1174					var thisItem = JSON.parse($(this).attr('data-product-gtm-json'));
1175					var product = {};	
1176					product.productID = thisItem["id"];
1177					product.sku = thisItem["skuid"];
1178					product.category = thisItem["category"].replace(/\\/g, "");
1179					product.name = thisItem["name"].replace(/\\/g, "");
1180					product.qty = thisItem["quantity"];
1181					product.itemPrice = thisItem["price"];
1182					product.images = [_this.find('img').attr('src')];
1183					product.url = window.location.origin + _this.find('.name').find('a').attr('href');
1184					product.attr = {'manufacturer': thisItem["brand"], 'color': thisItem["color"], 'size': thisItem["size"] };
1185					items[index]= product;
1186				});
1187			});
1188			
1189			var order_data = {
1190			  "orderID": orderID,
1191			  "shippingAddress": {
1192			    "name": shippingName,
1193			    "address": shippingAddress,
1194			    "city": shippingCity,
1195			    "state": shippingState,
1196			    "postalCode": shippingZip,
1197			    "country": shippingCountry
1198			  },
1199			  "billingAddress": {
1200				"name": billingName,
1201				"address": billingAddress,
1202				"city": billingCity,
1203				"state": billingState,
1204				"postalCode": billingZip,
1205				"country": billingCountry
1206			  },
1207			  "items": items
1208			};
1209			
1210			var properties = {
1211			    "orderID": orderID
1212			};
1213						
1214			var auth_data = {
1215				email: email
1216			}
1217			var contact_data = {
1218			    'channels': {
1219			        'email': {
1220			            'address': email
1221			        }
1222			    },
1223			    'first_name': firstName,
1224				'last_name': lastName,
1225				'phone': billingPhone,
1226				'postal_code': billingZip,
1227				'source_create': 'order',
1228				'geo_location': {
1229				    'postal_code': billingZip,
1230				    'country': billingCountry,
1231				    'state': billingState,
1232				    'city': billingCity
1233				}
1234			};				
1235			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {	
1236				crdl([
1237					['contact', auth_data, contact_data],
1238					['cart', 'clear'],
1239					['order', 'add', order_data],
1240					['event', 'order', properties]
1241				]);
1242			}
1243			
1244			// create Cordial contact on confirmation page
1245			$(document).on('click','.largebutton[name="dwfrm_profile_confirm"]', function(){
1246				var password = $('.password-holder .password').val();
1247				var passwordConfirm = $('.password-holder .password-confirm').val();
1248				var formValidation = true;
1249				var signupEvent = "";
1250
1251				var regexPassword = /^(?=.*?[A-z])(?=.*?[0-9])(?=.*?[#?!\=\/\,\.\~\`\(\)\{\}\[\]\\@_$%^&*-]).{6,15}$/g;
1252				if ( String(password).match(regexPassword) == null ) {
1253					formValidation = false;
1254				}
1255				
1256				if (password !== passwordConfirm) {
1257					formValidation = false;
1258				}
1259				
1260				if (!password || !passwordConfirm) {
1261					formValidation = false;
1262				}
1263				
1264				if (formValidation && $('#RegistrationForm .field.error').length == 0){					
1265					var auth_data = {
1266						email: email
1267					}
1268					
1269					var contact_data = {
1270					    'channels': {
1271					        'email': {
1272					            'address': email
1273					        }
1274					    },
1275						'first_name': firstName,
1276						'last_name': lastName,
1277						'phone': billingPhone,
1278						'postal_code': billingZip,
1279						'source_create': 'myaccount',
1280						'geo_location': {
1281						    'postal_code': billingZip,
1282						    'country': billingCountry,
1283						    'state': billingState,
1284						    'city': billingCity
1285						}
1286					};
1287					
1288					var loginProperties = {
1289					    "email": email
1290					};
1291					
1292					if ($('input#newslettersignup').is(':checked')){					
1293						var contact_data = {
1294						    'channels': {
1295						        'email': {
1296						            'address': email,
1297						            'subscribeStatus': 'subscribed'
1298						        }
1299						    },
1300							'first_name': firstName,
1301							'last_name': lastName,
1302							'phone': billingPhone,
1303							'postal_code': billingZip,
1304							'signup_source': 'myaccount',
1305							'source_create': 'myaccount',
1306							'welcome_signup': true,
1307							'geo_location': {
1308							    'postal_code': billingZip,
1309							    'country': billingCountry,
1310							    'state': billingState,
1311							    'city': billingCity
1312							}
1313						};
1314						
1315						var properties = {
1316						    "signup_source": 'myaccount',
1317						    "page": 'confirmation',
1318						    "welcome_signup": true
1319						};
1320						
1321						signupEvent = ['event', 'email_signup', properties];
1322					}
1323							
1324				if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1325						crdl([				
1326							['contact', auth_data, contact_data],
1327							['event', 'account_create'],
1328							['event', 'login', loginProperties],
1329							signupEvent
1330						]);
1331					}
1332				}
1333			});
1334		}
1335		
1336		// Password Reset - No Account
1337		if ($(document).find('.reset-password .no-account').length > 0) {
1338			var email = $('.no-account').text();
1339	
1340			var auth_data = {
1341				email: email
1342			}
1343			var contact_data = {
1344			    'channels': {
1345			        'email': {
1346			            'address': email
1347			        }
1348			    }
1349			};
1350			
1351			var properties = {
1352			    "email": email
1353			};
1354
1355			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1356				crdl([
1357					['contact', auth_data, contact_data],
1358					['event', 'reset-password-no-account', properties]
1359				]);
1360			}
1361		}
1362		
1363		// Password Reset Confirmation
1364		if ($(document).find('#password-reset-confirm').length > 0) {
1365			var email = $('#password-reset-confirm').attr('data-email');
1366			var passwordEvent = "reset-password";
1367			
1368			if ($(document).find('.noAccount').length > 0) {
1369				var passwordEvent = "reset-password-no-account";
1370			}
1371			
1372			var auth_data = {
1373				email: email
1374			}
1375			var contact_data = {
1376			    'channels': {
1377			        'email': {
1378			            'address': email
1379			        }
1380			    }
1381			};
1382			
1383			var properties = {
1384			    "email": email
1385			};
1386
1387			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
1388				crdl([
1389					['contact', auth_data, contact_data],
1390					['event', passwordEvent, properties]
1391				]);
1392			}
1393		}
1394	}
1395	//END CORDIAL
1396
1397	
1398	// Reloading login page after unsuccessful attempt w/ error messaging,
1399	// and retaining username if 'Remember me' was checked
1400	if (window.location.href.indexOf('retry') >= 0) {
1401        $('.login-dialog-error .invalid-login').removeClass('d-none');
1402		if (window.location.href.indexOf('=') >= 0) {
1403			var username = window.location.href.split('=')[1];
1404			$('input[name*="dwfrm_login_username"]').val(username);
1405			$('input[name*="dwfrm_login_rememberme"]').prop('checked', true);
1406		}
1407	}
1408	
1409	// Setting marketing email optin default to checked on Account Edit page
1410	$(document).ready(function() {
1411		$('.update-account #dwfrm_profile_customer_addtoemaillist').prop('checked', true);
1412	});
1413	
1414	// Make this my store click delegation
1415	$(document).on('click','.assign-store-container .assign-store', function(e){
1416		e.preventDefault();
1417		var storeID = $(this).parents('.store').data('id');
1418		$.ajax({
1419			url : Urls.assignStoreId,
1420			data : { store_id : storeID },
1421			method : "POST",
1422			dataType : "JSON",
1423			success : function(data) {
1424				if ( 'success' in data && data['success'] == true ) {
1425					location.reload();
1426				}
1427			}
1428		});
1429	});
1430
1431	// Bold day of the week on store page 
1432	$(document).ready(function() {
1433		if ($('#wrapper').hasClass('pt_store-locator')) {
1434			var daysOfWeek = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
1435			var currentDay = new Date().getDay(); //0=Sun, 1=Mon, ..., 6=Sat
1436			var matchingStoreHours = $('.store-detail-info .store-hours-days .stores-day').filter(function() { 
1437				return $(this).html().indexOf(daysOfWeek[currentDay]) >= 0; 
1438			});
1439			$(matchingStoreHours).css('font-weight','bold').next().css('font-weight','bold');
1440		}
1441	});
1442	
1443	//EMPLIFI REVIEWS
1444	if(SitePreferences.TURNTO_ENABLED){
1445		if (pageContext && 'type' in pageContext && pageContext.type == 'product') {
1446			//show/hide teaser div for BB desktop/mobile
1447			if (window.innerWidth <= 767) {
1448				$('.mobile-hidden .turntoTeaser').remove();
1449			} else {
1450				$('.mobile-only .turntoTeaser').remove();
1451			}
1452			
1453			var iterations = 0;
1454			var maxIterations = 100;
1455			var intervalId = setInterval(function() {
1456				iterations++;
1457				//hide empty inline ratings and reviews
1458				if($('.tt-c-teaser__avg-rating').length > 0){
1459					$('.product-review').removeClass('hide-reviews');
1460					$('.product-reviews-container').removeClass('hide-reviews');
1461				}
1462				//autoclick the mobile "Show details" summary button
1463				if (window.innerWidth <= 767) {
1464					$('.tt-o-expander__button').trigger('click');
1465				}
1466				if (iterations >= maxIterations) {
1467					clearInterval(intervalId);
1468				}
1469			}, 100);
1470		}
1471	}
1472	
1473	//PIXLEE ADD-TO-CART TRACKING
1474	if(SitePreferences.PIXLEE_ENABLED){
1475		function pixleeAddToCart(id) {
1476			setTimeout(function(){
1477				var thisItem = $('.productCardMetadata[data-product-id="'+id+'"]');
1478				var itemMeta = JSON.parse(thisItem.attr('data-product-ga4-json'));
1479				var quantity = $('.quantity-stepper[data-product-id="'+id+'"] input.opc-qty').val();
1480				if (typeof pixlee_analytics !== 'undefined') {
1481				    pixlee_analytics.events.trigger('add:to:cart', {
1482				        'product_sku': itemMeta["item_id"],
1483				        'price': itemMeta["price"],
1484				        'quantity': quantity,
1485				        'currency': itemMeta["currency"]
1486				    });
1487				 }
1488			}, 3000);
1489		}
1490		
1491		$('body').on('change', 'input.opc-qty', function(e) {
1492			var thisItemID = $(this).parent('.opc-qty-container').parent('.quantity-stepper').attr('data-product-id');
1493			pixleeAddToCart(thisItemID);
1494		});
1495		
1496		$('body').on('click', '.increase.cart-change', function(e) {
1497			var thisItemID = $(this).parent('.quantity-stepper').attr('data-product-id');
1498			pixleeAddToCart(thisItemID);
1499		});
1500	}
1501	
1502	$('.section-group .section-body:last').append($('.section-reviews'));
1503
1504	$(".pt_customer-service #secondary .nav-title").click(function() {
1505		$(this).toggleClass('active');
1506		$(".pt_customer-service #secondary #customer-service-nav").slideToggle();
1507	});	
1508
1509	searchbar.init();
1510		
1511	if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
1512		var btn = $('#back-to-top'),
1513		scroll_position = 2100;
1514		
1515		if (window.innerWidth <= 767) {
1516			scroll_position = 2771;
1517		}
1518		
1519		if (window.innerWidth > 767) {
1520    		$('#main').prepend('<div class="menu-hover-overlay" style="display:none;"></div>');
1521	    	
1522	    	$('.has-dropdown.nav-menu').hover(
1523				function() {
1524					$('#wrapper').addClass('menu-active');
1525				},
1526				function() {
1527					$('#wrapper').removeClass('menu-active');
1528				}
1529    	    );
1530    	}
1531		
1532		$(window).scroll(function() {
1533		  if ($(window).scrollTop() > scroll_position) {
1534		    btn.addClass('show');
1535		  } else {
1536		    btn.removeClass('show');
1537		  }
1538		});
1539
1540		btn.on('click', function(e) {
1541		  e.preventDefault();
1542		  $('html, body').animate({scrollTop:0}, scroll_position);
1543		});
1544
1545		if($('.homepage-03 .content-asset-body').length >= 1) {
1546			var slot_position = $('.homepage-03 .content-asset-body').attr('slot-position').trim();
1547			if(slot_position != '3'){
1548				var promo_slot = $('.shell.homepage-03').html(),
1549				before_this_slot = '.homepage-0' + parseInt(slot_position, 10);
1550				$('.shell.homepage-03').remove();
1551				
1552				if(slot_position > 3 && slot_position < 8){
1553					before_this_slot = '.homepage-0' + (parseInt(slot_position, 10) + 1);									
1554				}
1555				if(slot_position > 7){
1556					$('.shell' + before_this_slot).after('<div class="shell homepage-03">' + promo_slot + '</div>');
1557				} else {
1558					$('.shell' + before_this_slot).before('<div class="shell homepage-03">' + promo_slot + '</div>');
1559				}
1560			}
1561		}
1562	}
1563
1564	var brands_count = $('.brand').length;
1565	var styles_count = $('.style').length;
1566	var toestyle_count = $('.toe-style').length;
1567
1568	if(brands_count < 16) {
1569		$('.pt_content .viewallbrands').hide();
1570	}
1571
1572	if(styles_count < 8) {
1573		$('.pt_content .viewallstyles').hide();
1574	}
1575
1576	if(toestyle_count < 8) {
1577		$('.pt_content .viewalltoes').hide();
1578	}
1579
1580	$(document).on('click','.ca-header-contact-info',function(event) {
1581		if(window.innerWidth > 767) {
1582			event.preventDefault();
1583		}
1584	});
1585	
1586	/*if(window.innerWidth < 767 ) {
1587		if(siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
1588			$('.product-listing-1x4 .grid-tile').each(function(){
1589				$(this).find('.product-content').before('<div class="savings-bar"></div>');
1590			    if($(this).find('.product-sales-price').length !== 0){
1591			    	var $promoMsg = $(this).find('.product-sales-price').text().trim().replace('You Save:', 'Save');
1592			    	$(this).find('.savings-bar').css('background-color','#E3ECEE').text($promoMsg).show();
1593			    }	
1594			});
1595		}
1596	}*/
1597
1598	if(window.innerWidth < 767 ) {
1599		if(brands_count > 8) {
1600			$('.pt_content .viewallbrands').show();
1601		}
1602
1603		$('.pt_content .style:nth-child(n+9)').css({'display':'none'});
1604		$('.pt_content .toe-style:nth-child(n+9)').css({'display':'none'});
1605
1606		if ($('.top-banner-bar').attr('data-header-for') != 'bootbarn_us') {
1607			$('.pt_storefront .list-review-products li .offer-list').slideUp();
1608			$('.pt_storefront .offer-head h2').addClass('close');
1609		}
1610	} else {
1611		$('.pt_content .style:nth-child(n+9)').css({'display':'block'});
1612		$('.pt_content .toe-style:nth-child(n+9)').css({'display':'block'});
1613	}
1614
1615	$(document).on('click','.pt_content .viewallbrands',function(event) {
1616		event.preventDefault();
1617		if($('.pt_content .viewallbrands').hasClass("expanded")) {
1618			$('.pt_content .viewallbrands').removeClass("expanded");
1619			$('.pt_content .brand:nth-child(n+9)').css({'display':'none'});
1620			$('.pt_content .viewallbrands').html(Resources.SHOWMOREBRANDS);
1621		} else {
1622			$('.pt_content .viewallbrands').addClass("expanded");
1623			$('.pt_content .brand:nth-child(n+9)').css({'display':'block'});
1624			$('.pt_content .viewallbrands').html(Resources.CLOSE);
1625		}
1626	});
1627
1628	$(document).on('click','.pt_content .viewallstyles',function(event) {
1629		event.preventDefault();
1630		if($('.pt_content .viewallstyles').hasClass("expanded")) {
1631			$('.pt_content .viewallstyles').removeClass("expanded");
1632			$('.pt_content .style:nth-child(n+9)').css({'display':'none'});
1633			$('.pt_content .viewallstyles').html(Resources.SHOWMORESTYLE);
1634		} else {
1635			$('.pt_content .viewallstyles').addClass("expanded");
1636			$('.pt_content .style:nth-child(n+9)').css({'display':'block'});
1637			$('.pt_content .viewallstyles').html(Resources.CLOSE);
1638		}
1639	});
1640
1641	$(document).on('click','.pt_content .viewalltoes',function(event) {
1642		event.preventDefault();
1643		if($('.pt_content .viewalltoes').hasClass("expanded")) {
1644			$('.pt_content .viewalltoes').removeClass("expanded");
1645			$('.pt_content .toe-style:nth-child(n+9)').css({'display':'none'});
1646			$('.pt_content .viewalltoes').html(Resources.SHOWMORETOE);
1647		} else {
1648			$('.pt_content .viewalltoes').addClass("expanded");
1649			$('.pt_content .toe-style:nth-child(n+9)').css({'display':'block'});
1650			$('.pt_content .viewalltoes').html(Resources.CLOSE);
1651		}
1652	});
1653
1654	$(document).on('click', '.swatch-list .swatch:not(.selected) [class*="swatch-im"]', function() {
1655		var $parentBlock = $(this).closest('.slide, .swatch-list');
1656		var $mainImg = $parentBlock.find('.product-image img:first');
1657		var swatchData = $(this).data('thumb');
1658		
1659		if (swatchData) {
1660			$mainImg.attr('alt', swatchData.alt);
1661			$mainImg.attr('title', swatchData.title);
1662			$mainImg.attr('src', swatchData.src);
1663			
1664			$parentBlock.find('.swatch').removeClass('selected');
1665			$(this).closest('.swatch').addClass('selected');
1666		} else {
1667			window.location.href = $(this).closest('.swatch').attr('href');
1668		}
1669	});
1670	
1671	$(document).on('click','.yes-alts button.altButton', function(e){
1672		e.preventDefault();
1673		var altHiddenCount = $(this).closest('.yes-alts ul.swatch-list').find('.hidden-content').length;
1674		var altHiddenToggle = '+ ' + altHiddenCount;
1675		$(this).text( $(this).text().indexOf('+') >= 0 ? "- Less" : altHiddenToggle);
1676		$(this).closest('.yes-alts ul.swatch-list').find('.hidden-content').toggle();
1677	});
1678
1679	//Terms and Conditions Popup on Checkout
1680	$(document).off('click', '[dialog-content]').on('click', '[dialog-content]', function(event) {
1681        event.preventDefault();
1682        
1683        var $this = $(event.currentTarget);
1684        var $contentContainer = $($this.attr('dialog-content'));
1685        
1686        dialog.open({
1687            html: $contentContainer.html(),
1688            options: {
1689                title: $contentContainer.attr('title'),
1690                dialogClass: 'fixed checkout-terms' + ($contentContainer.attr('dialog-class') ? ' ' + $contentContainer.attr('dialog-class') : ''),
1691                draggable: false,
1692                open: function(event, ui) {
1693                	var $this = $(this);
1694                	
1695	            	$('body').addClass('dialog-open');
1696	            	$this.scrollTop(0);
1697	            	emailForm.initialize($this);
1698	            },
1699	            close: function(event, ui) {
1700	            	$('body').removeClass('dialog-open');
1701	            }
1702            }
1703        });
1704	});
1705
1706	//Page Top functionality for popup content in terms and conditions
1707	$(document).off('click', '.ui-dialog [href="#pgTop"]').on('click', '.ui-dialog [href="#pgTop"]', function(event) {
1708    	event.preventDefault();
1709    	
1710    	var $this = $(event.currentTarget);
1711    	$this.closest('.ui-dialog-content').scrollTop(0);
1712    });
1713
1714	$('.video-play-btn')
1715	.on('click', function(e) {
1716		var $this = $(this);
1717		var $video = $this.closest('.video');
1718		var $iframe = $video.find('iframe');
1719		var originalSRC = $video.find('iframe').attr('src').replace('?autoplay=1','');
1720		var videoHtml = $(this).next('iframe').prop('outerHTML');
1721		videoHtml = videoHtml.replace(/src=".+?"/, 'src="' + originalSRC + '?autoplay=1"');
1722		dialog.open({
1723            html:(videoHtml),
1724            options: {
1725            	width: 560,
1726                dialogClass: 'videopopup',
1727                title: $video.find('.video-caption').text(),
1728                close: function() {
1729                	$('.ui-dialog').find('iframe').attr('src', '');
1730                }
1731            },
1732		});
1733
1734		e.preventDefault();
1735	})
1736
1737	
1738	//Prepare for touch/menu
1739	if (util.isTouchDevice()) {
1740		$('body').addClass('touchDevice');
1741	}
1742	if(window.navigator.platform.match("i(Win)")){
1743		$('body').addClass('isWin')
1744	}
1745	
1746	if (siteName != SiteConstants.SiteIds.BootBarnUS && siteName != SiteConstants.SiteIds.SheplersUS) {
1747		if(window.innerWidth < 767) {
1748			if ($('.header-banner').children().length == 0) {
1749			$('.top-banner').addClass('no-top');
1750			}
1751		}
1752	}
1753	
1754    $('body')
1755        .on('keydown', 'textarea[data-character-limit]', function (e) {
1756            var text = $.trim($(this).val()),
1757                charsLimit = $(this).data('character-limit'),
1758                charsUsed = text.length;
1759
1760            if ((charsUsed >= charsLimit) && (controlKeys.indexOf(e.which.toString()) < 0)) {
1761                e.preventDefault();
1762            }
1763        })
1764        .on('change keyup mouseup', 'textarea[data-character-limit]', function () {
1765            var text = $.trim($(this).val()),
1766                charsLimit = $(this).data('character-limit'),
1767                charsUsed = text.length,
1768                charsRemain = charsLimit - charsUsed;
1769
1770            if (charsRemain < 0) {
1771                $(this).val(text.slice(0, charsRemain));
1772                charsRemain = 0;
1773            }
1774
1775            $(this).next('div.char-count').find('.char-remain-count').html(charsRemain);
1776        })
1777        .on('click','.pt_storefront .offer-head h2',function(event) {
1778        	event.preventDefault();
1779        	if(window.innerWidth < 767 && $('.top-banner-bar').attr('data-header-for') != 'bootbarn_us') {
1780	        	$(this)
1781	        		.toggleClass('close')
1782	        		.closest('li')
1783	        		.find('.offer-list')
1784	        		.slideToggle(200);
1785			} else if ($('.top-banner-bar').attr('data-header-for') == 'bootbarn_us') {
1786				$(this)
1787					.toggleClass('active')
1788					.closest('li')
1789					.toggleClass('active')
1790					.find('.offer-list')
1791					.toggleClass('active')
1792					.slideToggle(200);
1793			}
1794		})
1795		.on('click', '.footer-item .content-asset h3', function(event) {
1796			event.preventDefault();
1797			
1798			if(window.innerWidth < 1025) {
1799				$(this)
1800					.toggleClass('active')
1801					.closest('.footer-item')
1802					.siblings()
1803					.find('.active')
1804					.removeClass('active');
1805
1806				$(this)
1807					.next()
1808					.stop()
1809					.slideToggle()
1810					.closest('.footer-item')
1811					.siblings()
1812					.find('.menu-footer')
1813					.slideUp();
1814			}
1815		})
1816		.on('click', '.btn-expand', function (event) {
1817			event.preventDefault();
1818
1819			var element = $(this).attr('href');
1820			
1821			if($('.sticky-menu').length > 0) {
1822				var element = element  +  ".search-mobile";
1823			}
1824			
1825			$(element).toggleClass('show');
1826			
1827            if($('.sticky-menu').length > 0) { 
1828                $('#main').toggleClass('search-open');
1829            }
1830
1831			if($('.menu-toggle').hasClass('active')) {
1832				navigation.CloseNavigation();
1833			}
1834
1835			if($('#mini-cart').hasClass('active')) {
1836				$('#mini-cart .mini-cart-link').removeClass('active');
1837				$('#mini-cart').removeClass('active');
1838			}
1839			
1840			if($('.second-mobnav').hasClass('active')) {				
1841				$('.second-mobnav').removeClass('active')
1842				$('#second-mobnav').slideUp();
1843			}
1844			
1845			if (element == '#search' || element == '#search.search-mobile') {
1846				$(document).trigger('SearchBar.Expand');
1847            }
1848		})
1849		.on('click', '.btn-location-redirect', function(event){
1850			event.preventDefault();
1851			var linkRedirectStore = $(this).find('span').attr('loc-link');
1852			if (linkRedirectStore != null) {
1853				location.href = linkRedirectStore;
1854			}
1855		})
1856		.on('click', '.btn-location', function(event) {
1857			event.preventDefault();
1858
1859			if($('#mini-cart').hasClass('active')) {
1860				$('#mini-cart .mini-cart-link').removeClass('active');
1861				$('#mini-cart').removeClass('active');
1862			}
1863
1864			$('#navigation')
1865				.stop()
1866				.slideToggle();
1867
1868			navigation.CloseNavigation();
1869			
1870			var element = $(this).attr('href');
1871
1872			$(element)
1873				.find('.nav-secondary-dropdown')
1874				.stop()
1875				.slideToggle()
1876				.parent()
1877				.siblings()
1878				.find('.nav-secondary-dropdown')
1879				.slideUp();
1880		})
1881		.on('click', '.jumbotron-current-offers', function (event) {
1882			$('.jumbotron-mobile').toggleClass('active');
1883			if($('#jumbotron-current-offers-icon').hasClass('fa-angle-up')) {
1884				$('#jumbotron-current-offers-icon').removeClass('fa-angle-up');
1885				$('#jumbotron-current-offers-icon').addClass('fa-angle-down');
1886				setCookie('jumbotronExpandedCookie', false, 2);
1887			} else {
1888				$('#jumbotron-current-offers-icon').removeClass('fa-angle-down');
1889				$('#jumbotron-current-offers-icon').addClass('fa-angle-up');
1890				setCookie('jumbotronExpandedCookie', true, 2);
1891			}
1892		})
1893		.on('click', '.second-mobnav', function (event) {
1894			event.preventDefault();
1895			
1896			$(this).toggleClass('active');
1897			
1898			$('#second-mobnav')
1899			.stop()
1900			.slideToggle(150);
1901			
1902			if($('.menu-toggle').hasClass('active')) {
1903				$('.menu-toggle').removeClass('active');
1904				$('html').removeClass('menu-active');
1905			}
1906			 
1907			if($('#mini-cart').hasClass('active')) {
1908				$('#mini-cart .mini-cart-link').removeClass('active');
1909				$('#mini-cart').removeClass('active');
1910			}
1911			$(document).trigger('SearchBar.Hide');
1912		})
1913		
1914		.on('click', '.accordion > a', function (event) {
1915			var $container = $(this).closest('.accordion');
1916			$container.find('ul').slideToggle('fast');
1917		})
1918/*
1919		.on('touchstart', function(event)  {
1920    		if (!$(event.target).closest('#navigation').length && !$(event.target).is('#navigation') && $win.width() > 767 )  {
1921    		    $('.level-2').stop().slideUp(400);
1922    		}  
1923    	})
1924*/
1925		
1926		.on('mouseover', '#mini-cart', function () {
1927			if(window.innerWidth > 1024) {
1928				$(this)
1929					.find('.mini-cart-link')
1930					.addClass('active');
1931
1932				$(this)
1933					.addClass('active');
1934			}
1935
1936			if($('#navigation').hasAttr('style')) {
1937				$('.menu-toggle').removeClass('active');
1938			}
1939			$(document).trigger('SearchBar.Hide');
1940		})
1941		.on('mouseleave', '#mini-cart', function () {
1942			var $this = $(this);
1943
1944			if(window.innerWidth > 1024) {
1945				setTimeout(function () {
1946					$this
1947						.find('.mini-cart-link')
1948						.removeClass('active');
1949
1950					$this
1951						.removeClass('active');
1952				}, 1000);
1953			}
1954		})
1955		.on('click', '.edit-search-query', function () {
1956			$('.category-popup-list-stores').dialog('close');
1957			$('.grid-page-storeselector-popup').dialog('open');
1958		})
1959		.on('click', '.popup-list-stores input[name="apply"]', function () {
1960			if (!$('input[name="store"]:checked').val()) {
1961				alert('Select one of stores');
1962
1963				return;
1964			}
1965
1966			$.ajax({
1967				type: 'get',
1968				url: Urls.setDefaultStore,
1969				data: {
1970					storeId: $('input[name="store"]:checked').val()
1971				},
1972
1973				success: function (data) {
1974					$('.category-popup-list-stores').dialog('close');
1975					document.location.reload();
1976				}
1977			});
1978		})
1979		
1980		.on('click', '.pt_product-search-result .category-slot a', function(event) {
1981			var refinementValue = $(event.target).closest('[data-link-attribute-value]').data('link-attribute-value');
1982			var isRefinementLink = refinementValue != undefined;
1983
1984			if (isRefinementLink) {	
1985				var $sidebarRefinement = $('#refinements-inner').find('#field-' + refinementValue);
1986				var urlRefinementData = getUrlRefinementData();
1987
1988				if ($sidebarRefinement.length > 0) {
1989					event.preventDefault();
1990					$sidebarRefinement.trigger('click');
1991				} else if (urlRefinementData.hasRefinements) {
1992					event.preventDefault();
1993					var refinementId = $(event.target).closest('[data-link-attribute-id]').data('link-attribute-id');
1994					var refinementIsAlreadySelected = false;
1995					var redirectUrl;
1996
1997					for (var refinement in urlRefinementData.parameters) {
1998						if (urlRefinementData.parameters[refinement] === refinementValue) {
1999							refinementIsAlreadySelected = true;
2000						}
2001					}
2002
2003					if (!refinementIsAlreadySelected) {
2004						redirectUrl = window.location.href + '&prefn' + urlRefinementData.newRefinementStartNumber + '=' + refinementId + '&prefv' + urlRefinementData.newRefinementStartNumber + '=' + refinementValue;
2005						window.location.href = redirectUrl;
2006					}
2007				}
2008			}
2009		});
2010
2011		$(document).on('click', '.thumb-link, .name-link', function(event) {
2012			var $this = $(this);
2013			var $container = $this.closest('.accordion, .section-offers, .tiles, .cat-header-content.cat-header-slider').first();
2014			if ($container.length > 0) {
2015				event.preventDefault();
2016
2017				if ($container.find('.product-tile').length > 0) {
2018					var containerId = $container.attr('content-slot-id') || $container.attr('id');
2019	
2020					var dataObject = {
2021						promotion_name: containerId
2022					}
2023
2024					var pageType;
2025					var $wrapper = $this.closest('.add-to-cart-modal');
2026					if ($wrapper.length > 0) {
2027						pageType = 'addtocartmodal';
2028					}
2029
2030					if (!pageType) {
2031						$wrapper = $('#wrapper');
2032						if ($wrapper.length > 0) {
2033							if (pageContext && pageContext.type) {
2034								switch (pageContext.type) {
2035									case 'storefront':
2036									pageType = 'Homepage';
2037									break;
2038	
2039									case 'search':
2040									pageType = 'PLP';
2041									break;
2042	
2043									case 'product':
2044									pageType = 'PDP';
2045									break;
2046	
2047									case 'cart':
2048									pageType = 'Cart';
2049									break;
2050	
2051									case 'onepcheckout_groups':
2052									pageType = 'Checkout';
2053									break;
2054	
2055									case 'orderconfirmation':
2056									pageType = 'Order Confirmation';
2057									break;
2058	
2059									case 'customerservice':
2060									pageType = 'Customer Service';
2061									break;
2062	
2063									case 'account':
2064									pageType = 'Account';
2065									break;
2066	
2067									case 'content':
2068									pageType = 'Content Page';
2069									break;
2070	
2071									case 'error':
2072									pageType = 'Error Page';
2073									break;									
2074								}
2075							}
2076						}
2077					}
2078
2079					if (pageType) {
2080						dataObject.promotion_id = pageType;
2081					}
2082
2083					var redirectHandler = function() {
2084						location.href = $this.attr('href');
2085					};
2086	
2087					GoogleTagManagerClient.PushDataLayerForDataLayerType('SelectPromotion', dataObject, { Container: $this.closest('.product-tile') }).then(redirectHandler).catch(redirectHandler);
2088				}
2089			}
2090		});
2091
2092		$(document).on('click', '[content-asset-id] a, [content-asset-id] button', function(event) {
2093			var $this = $(this);
2094			var isLink = $this.attr('href') && $this.attr('href').length > 0 && $this.attr('href').indexOf('//') >= 0;
2095			var isClose = $this.attr('class') && $this.attr('class').length > 0 && $this.attr('class').indexOf('close') >= 0;
2096
2097			if ($this.closest('.product-tile').length == 0 && !isClose) {
2098				var $container = $this.closest('[content-asset-id]').first();
2099				if ($container.length > 0) {
2100					if (isLink) {
2101						event.preventDefault();
2102					}
2103
2104					var dataObject = {
2105						content_type: 'content',
2106						item_id: $container.attr('content-asset-id')
2107					}
2108
2109					var redirectHandler = function() {
2110						if (isLink) {
2111							if ($this.attr('target')) {
2112								window.open($this.attr('href'), $this.attr('target'));
2113							} else {
2114								location.href = $this.attr('href');
2115							}
2116						}
2117					};
2118		
2119					GoogleTagManagerClient.PushDataLayerForDataLayerType('SelectContent', dataObject).then(redirectHandler).catch(redirectHandler);
2120				}
2121			}
2122		});
2123
2124		$(document).on('click.CassidyClick', '.btn-cassidy', function(event) {
2125			GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', {
2126				'action_name': 'cassidyclick',
2127				'action_page_type': 'pdp',
2128				'action_value': window.location.href.split('?')[0], 
2129				'action_items': []
2130			});
2131		});
2132
2133		$(document).on('click.CassidySuggestionButton', '.cim-widget-modal-container .cim-widget-suggestion-button', function(event) {
2134			var $this = $(this);
2135			GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', {
2136				'action_name': 'suggestionbutton_cassidy',
2137				'action_page_type': 'cassidy',
2138				'action_value': window.location.href.split('?')[0], 
2139				'action_items': [$this.text().trim()]
2140			});
2141		});
2142
2143		$(document).on('click.CassidyProductRecommendation', '.cim-widget-modal-container a.cim-widget-product-tile-pdp', function(event) {
2144			var $this = $(this);
2145			var hrefArray = $this.attr('href').split('.html')[0].split('/');
2146			GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', {
2147				'action_name': 'productrec_cassidy',
2148				'action_page_type': 'cassidy',
2149				'action_value': window.location.href.split('?')[0], 
2150				'action_items': [hrefArray[hrefArray.length - 1]]
2151			});
2152		});
2153
2154		addPromotionDataLayers();
2155		$(document).off('GoogleTagManager.AddPromotionDataLayers').on('GoogleTagManager.AddPromotionDataLayers', function(event) {
2156			addPromotionDataLayers();
2157		});
2158		
2159		/**
2160		 * Add class to content slot refinements if they're currently active
2161		 * and remove any static refinements if they're not found in the sidebar
2162		 * to prevent empty search results.
2163		 */
2164		if ($('body').find('.pt_product-search-result .category-slot a').length > 0) {
2165			var $slotLink = $('body').find('.pt_product-search-result .category-slot a');
2166			var urlRefinementData = getUrlRefinementData();
2167			var sidebarRefinements = getSidebarRefinements();
2168			$slotLink.each(function(index, value) {
2169				var linkRefinementType = $(value).data('link-attribute-value');
2170				for (var refinement in urlRefinementData.parameters) {
2171					if (refinement.match(/^\prefv/)) {
2172						var refinementValues = urlRefinementData.parameters[refinement].split('%7C');
2173						for (var index = 0; index < refinementValues.length; index++) {
2174							if (refinementValues[index] === linkRefinementType) {
2175								$(value).addClass('refinement-active');
2176							}
2177						}
2178					}
2179				}
2180				if (linkRefinementType != undefined) {
2181					var foundInSidebar = false;
2182					for (var index = 0; index < sidebarRefinements.length; index++) {
2183						if (sidebarRefinements[index] === linkRefinementType) {
2184							var foundInSidebar = true;
2185						}
2186					}
2187					if (!foundInSidebar) {
2188						$(value).closest('li').remove();
2189					}
2190				}
2191			});
2192			$slotLink.closest('.section-block').removeClass('hidden');
2193		}
2194
2195		/**
2196		 * helper function to get a list of existing refinements from the URL
2197		 */
2198		function getUrlRefinementData() {
2199			var refinements = location.search.substr(1).split('&');
2200			var urlRefinementData = {
2201				newRefinementStartNumber: 1,
2202				parameters: {},
2203				hasRefinements: false
2204			};
2205			if (refinements.length > 0) {
2206				for (var index = 0; index < refinements.length; index++) {
2207					var parameter = refinements[index].split('=');
2208					if (parameter[0].match(/^\prefn/)) {
2209						urlRefinementData.newRefinementStartNumber++;
2210						urlRefinementData.hasRefinements = true;
2211						urlRefinementData.parameters[parameter[0]] = parameter[1];
2212					}
2213					if (parameter[0].match(/^\prefv/)) {
2214						urlRefinementData.parameters[parameter[0]] = parameter[1];
2215					}
2216				}
2217			}
2218			return urlRefinementData;
2219		}
2220
2221		/**
2222		 * helper function to get a list of refinement names present in refinement sidebar
2223		 */
2224		function getSidebarRefinements() {
2225			var sidebarRefinementList = [];
2226			$('#secondary.refinements .checkbox input').each(function(index, value) {
2227				if ($(value).attr('id').match(/^field/)) {
2228					var refinementName = $(value).attr('id').split('-')[1];
2229					sidebarRefinementList.push(refinementName);
2230				}
2231			});
2232			return sidebarRefinementList;
2233		}
2234 
2235    	//ready #navigation for interaction (arrests odd initial interaction on touch devices)
2236    	$("#navigation").addClass("hoverReady");
2237
2238		// Switch show text
2239		$('.switch input').change(function () {
2240			if($(this).prop('checked')) {
2241				$(this)
2242					.closest('.result-options')
2243					.addClass('show-info');
2244			} else {
2245				$(this)
2246					.closest('.result-options')
2247					.removeClass('show-info');
2248			}
2249		});
2250
2251		if($('body').find('.result-options .switch input[name="field-true"]').prop('checked')) {
2252			var toggleBtn = $('body').find('.result-options .switch input[name="field-switch"]');
2253
2254			if(!toggleBtn.prop('checked')) {
2255				toggleBtn.closest('.result-options').addClass('show-info');
2256				toggleBtn.closest('.result-options').find('input[name="field-switch"]').attr('checked', 'checked');
2257			} else {
2258				toggleBtn.closest('.result-options').removeClass('show-info');
2259				toggleBtn.closest('.result-options').find('input[name="field-switch"]').removeAttr('checked');
2260			}
2261		}
2262
2263		$('.swatches > li > ul').on('click', 'a', function(event) {
2264			event.preventDefault();
2265
2266			$(this)
2267				.toggleClass('current')
2268				.parent()
2269				.siblings()
2270				.find('.current')
2271				.removeClass('current');
2272		});
2273
2274		// Generic slider init
2275		sliders.init('.slider .slides');
2276		
2277		$('.service-toggle-item').on('click', function (event) {
2278			event.preventDefault();
2279
2280			$(this).toggleClass('active');
2281
2282			$(this).next('.customer-service-inner').toggleClass('open');
2283			$(this).next('.customer-service-inner').slideToggle();
2284		});
2285
2286		$('.top-brands .category-head').on('click', function (event) {
2287			event.preventDefault();
2288
2289			$(this).toggleClass('active');
2290
2291			$(this).next('.category-body').toggleClass('open');
2292			$(this).next('.category-body').slideToggle();
2293		});
2294
2295		$(document).on('update', '.slider-products-primary', function() {
2296    		sliders.init('.slider-products-primary .slides', {breakpoints: {0: {slidesToShow: 1, dots: true}, 768: {slidesToShow: 3}, 1024: {slidesToShow: 4}}});
2297		});
2298
2299		// make sure slider gets initialized for product recommendations when dynamically added
2300		if ($('.pt_product-details').length > 0 || $('.no-hits-banner').length > 0) {
2301			sliders.initializeRecommendationSlider();
2302		}
2303
2304		$(document).off('EinsteinProductRecommendations.Initialize').on('EinsteinProductRecommendations.Initialize', function() {
2305			sliders.initializeRecommendationSlider();
2306		});
2307
2308		$(document).on('init-slider', '.slider-products-primary', function() {
2309			if (pageContext.site !== SiteConstants.SiteIds.BootBarnUS) {
2310				var showMobileDots = siteName == SiteConstants.SiteIds.CountryOutfittersUS ? false : true;
2311				sliders.init('.product-recommendations .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: showMobileDots, arrows: true}, 768: {slidesToShow: 3, dots: false, arrows: true}, 1024: {slidesToShow: 4, dots: false, arrows: true}}}, true);
2312				sliders.init('.product-recommendations-sfcc .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: showMobileDots, arrows: true}, 768: {slidesToShow: 3, dots: false, arrows: true},  1024: {slidesToShow: 4, dots: false, arrows: true}}});
2313				sliders.init('.last-visited .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: showMobileDots, arrows: true}, 768: {slidesToShow: 3, dots: false, arrows: true},  1024: {slidesToShow: 4, dots: false, arrows: true}}});
2314			} else {
2315				sliders.init('.product-recommendations .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: true, arrows: true}, 768: {slidesToShow: 3, dots: false, arrows: true}}}, true);
2316				sliders.init('.product-recommendations-sfcc .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: true, arrows: true}, 768: {slidesToShow: 4, dots: false, arrows: true}}});
2317				sliders.init('.last-visited .slider-products-primary .slides:not(.slick-slider):not(.swiper-full-wrapper):has(li)', {loop: false, breakpoints: {0: {slidesToShow: 1, dots: true, arrows: true}, 768: {slidesToShow: 4, dots: false, arrows: true}}});
2318			}
2319		});
2320
2321		sliders.init('.slider-filmstrip .slides', {loop: true});
2322
2323		sliders.initializeCategorySliders('.cat-header-content.cat-header-slider:not(.slick-initialized)');
2324
2325		// Waypoints
2326		$('.waypoint').on('click', function(event) {
2327			event.preventDefault();
2328
2329			var element = $(this).attr('href');
2330
2331			$('html, body').animate({
2332				scrollTop: $(element).offset().top
2333			}, 1000);
2334		});
2335
2336		// Btn Expand Alt
2337		$('.btn-expand-alt').on('click', function(event) {
2338			event.preventDefault();
2339			event.stopPropagation();
2340
2341			var element = $(this).attr('href');
2342
2343
2344			if ($(element).hasClass('grid-page-storeselector-popup')) {
2345				$(element).dialog('open');
2346
2347				return;
2348			}
2349
2350			$(element)
2351				.toggleClass('show');
2352		});
2353
2354		$('.grid-page-storeselector-popup').dialog({
2355			width: 450,
2356			autoOpen: false,
2357			modal: true,
2358			resizable: false
2359		});
2360
2361		// Sidebar Close
2362		$('#secondary .close, #sort-by .close, .popup-close').on('click', function (event) {
2363			event.preventDefault();
2364
2365			$(this)
2366				.closest('.show')
2367				.removeClass('show');
2368		});
2369
2370		$('.message .close').on('click', function (event) {
2371			event.preventDefault();
2372
2373			$(this)
2374				.parent()
2375				.slideUp();
2376		});
2377
2378		if ($('#GlobaleFreeShippingBannerContainer').length > 0) {
2379			var globaleFreeShippingBannerObserver = new MutationObserver(function(mutations, observer) {
2380				for (var mutationKey in mutations) {
2381					var mutation = mutations[mutationKey];
2382					if (mutation.type === 'childList') {
2383						resizeDeviceHelper();
2384					}
2385				}
2386			});
2387			globaleFreeShippingBannerObserver.observe(document.getElementById('GlobaleFreeShippingBannerContainer'), {attributes: false, childList: true, subtree: true });
2388		}
2389
2390		// Back to Top button
2391		function topButtonToggle() {
2392			var $scrollTop = $win.scrollTop();
2393			if ($scrollTop >  100 ) {
2394				$('body').addClass('btn-go-top-active');
2395				$( '.btn-go-top' ).addClass('active');
2396			} else {
2397				$('body').removeClass('btn-go-top-active');
2398				$( '.btn-go-top' ).removeClass('active');
2399			}
2400		}
2401		
2402		$win.on('scroll', function () {
2403			topButtonToggle();
2404			$(document).trigger('SearchBar.ScrollTop');
2405		});
2406
2407		$win.on('load', function() {
2408			accordionMobile();
2409			accordionMobileAlt();
2410
2411			// Disabled per BOOT-3517
2412			// $( '<span class="btn-go-top"><i class="fa fa-angle-up" aria-hidden="true"></i><span>Top</span></span>
2412' ).insertBefore($('.footer:not(.footer-mobile)'));
2413			// topButtonToggle();
2414		});
2415
2416		$doc.on('click', '.btn-go-top', function(e){
2417			$('html, body').animate({scrollTop: 0},800);
2418			e.preventDefault();
2419		})
2420
2421		// Read More / Less
2422		$doc.on('click', '.section-offers-text .read-more', function(e) {
2423			e.preventDefault();
2424			$(this).parents('.section-body').first().addClass('expand');
2425			return false;
2426		});
2427
2428		$doc.on('click', '.section-offers-text .read-less', function(e) {
2429			e.preventDefault();
2430			$(this).parents('.section-body').first().removeClass('expand');
2431			return false;
2432		});
2433		
2434		
2435		// Accordion
2436		function accordionMobile() {
2437			$('.offer .offer-head h6').on('click', function (e) {
2438				if(event.target.nodeName == "H6"){
2439					e.preventDefault();
2440					e.stopImmediatePropagation();
2441					$(this).find('.chevron-collapse').click();
2442				}
2443			});
2444			
2445			$('.offer .offer-head h6 .chevron-collapse').on('click', function (event) {
2446				if (window.innerWidth > 1024) {
2447					return;
2448				}
2449				event.stopImmediatePropagation();
2450				event.preventDefault();
2451				var $this = $(this)
2452				var $parent = $this.closest('.offer')
2453
2454				if ($parent.hasClass("active")) {
2455					$parent.removeClass('active').find('.offer-body').slideUp();
2456
2457				}
2458				else {
2459					$parent.addClass('active').find('.offer-body').slideDown();
2460				}
2461
2462			});
2463
2464			$win.resize(function() {
2465				if(window.innerWidth > 1024) {
2466					$('.offer .offer-body').show();
2467				} else {
2468					$('.offer .offer-body').hide()
2469				}
2470			});
2471		};
2472
2473		function accordionMobileAlt() {
2474			$('.accordion-mobile .accordion-mobile-head').on('click', function (event) {
2475				if (window.innerWidth > 1024) {
2476					return;
2477				}
2478
2479				event.preventDefault();
2480				var $this = $(this),
2481					$parent = $this.closest('.accordion-mobile');
2482
2483				$this
2484				.toggleClass('active')
2485				.next()
2486				.stop()
2487				.slideToggle();
2488			});
2489		};
2490
2491		// Accordion
2492		$(document).on('click', '.accordion-section .accordion-head', function(event) {
2493			event.preventDefault();
2494			var $this = $(this);
2495			var $container = $this.closest('.accordion');
2496			$this.toggleClass('active');
2497			$this.next('.accordion-body').stop().slideToggle(400);
2498
2499			if ($this.hasClass('active') && $container.find('.slides').length > 0 && $container.find('.slick-initialized').length > 0) {
2500				$container.find('.slides').slick('refresh');
2501			}
2502		});
2503
2504		$(document).on('click', '.section-offers.content-columns .section-column h6.column-title', function(event) {
2505			if (pageContext.site === SiteConstants.SiteIds.SheplersUS && window.innerWidth < 768) {
2506				event.preventDefault();
2507				$(this).toggleClass('active');
2508				$(this).next('.column-content').stop().slideToggle();
2509			}
2510		});
2511
2512		// Rotate Text
2513		function rotateText($element, duration) {
2514			var lengthSlider = $element.find('.rotate-element').length;
2515			var $rotateElement = $element.find('.rotate-element');
2516			
2517			if (lengthSlider > 0) {
2518				var interval = setInterval(function () {
2519					var $rotateElementActive 	= $element.find('.active');
2520					var $nextElement = $rotateElementActive.next();
2521	
2522					if($nextElement.index() === -1) {
2523						$nextElement = $element.find('.rotate-element:first-child');
2524	
2525						$nextElement
2526							.addClass('active')
2527							.siblings()
2528							.removeClass('active');
2529					} else {
2530						$nextElement
2531							.addClass('active')
2532							.siblings()
2533							.removeClass('active');
2534					}
2535				}, duration);
2536			}
2537		}
2538		rotateText($('.top-banner-message'), $('.top-banner-message').data('duration'));
2539
2540   	if (window.innerWidth <= 767) {
2541   		$('.refinement h3.toggle.expanded:not(.selected), .refinement .refinement-body.toggle-content.expanded:not(.selected)').removeClass('expanded');
2542   	}
2543   
2544    // add generic toggle functionality
2545    $('.toggle:not(.disabled)').not('.expanded').next('.toggle-content').hide();
2546    $('.toggle:not(.disabled)').click(function () {
2547		var $this = $(this);
2548		if ($this.siblings('.toggle-content').length > 0) {
2549        	$this.toggleClass('expanded').next('.toggle-content').toggle();
2550		}
2551    });
2552
2553    // subscribe email box
2554    var $subscribeEmail = $('.subscribe-email');
2555    if ($subscribeEmail.length > 0)    {
2556        $subscribeEmail.focus(function () {
2557            var val = $(this.val());
2558            if (val.length > 0 && val !== Resources.SUBSCRIBE_EMAIL_DEFAULT) {
2559                return; // do not animate when contains non-default value
2560            }
2561
2562            $(this).animate({color: '#999999'}, 500, 'linear', function () {
2563                $(this).val('').css('color', '#333333');
2564            });
2565        }).blur(function () {
2566            var val = $.trim($(this.val()));
2567            if (val.length > 0) {
2568                return; // do not animate when contains value
2569            }
2570            $(this).val(Resources.SUBSCRIBE_EMAIL_DEFAULT)
2571                .css('color', '#999999')
2572                .animate({color: '#333333'}, 500, 'linear');
2573        });
2574    }
2575
2576    $('.privacy-policy').on('click', function (e) {
2577        e.preventDefault();
2578        dialog.open({
2579            url: $(e.target).attr('href'),
2580            options: {
2581                height: 600
2582            }
2583        });
2584    });
2585
2586	$("#Mapmobile area").click(function () {
2587		$("#store-name").removeClass('storeheading');
2588		var value = $(this).attr("href");
2589		var valueName = value.replace('#','');
2590		$('html,body').animate({
2591			scrollTop: $("a[name="+ valueName +"]").offset().top - 200
2592		})
2593	});
2594    
2595    $( ".user-info" ).hover(
2596    	  function() {
2597    		 $(this).addClass('active');
2598    	  },
2599    	  function() {
2600    		 $(this).removeClass('active');
2601    	  }
2602
2603    );
2604
2605    var storesMediaQuery = matchMedia('(max-width: 1024px)');
2606	storesMediaQuery.addListener(mobileStores);
2607	$(mobileStores);
2608
2609    $(document).on('click', '.detail-toggle-item', function(e) {
2610        e.preventDefault();
2611        
2612        $(this).toggleClass('active');
2613        $('.discount-details-entry').toggleClass('open').animate({ 'height': 'toggle', 'opacity': 'toggle' }, 250, null, function() {
2614        	
2615        });
2616    });
2617
2618	// add show/hide password functionality
2619	$(document).on('click', '[data-action="toggle-password-visibility"]', function(e) {
2620		e.preventDefault();
2621		var $button = $(e.target);
2622		var $passwordInput = $button.closest('.password-holder').find('input');
2623		var passwordInputType = $passwordInput.attr('type');
2624		if (passwordInputType === 'password') {
2625			$button.addClass('password-visibility--visible');
2626			$button.removeClass('password-visibility--invisible');
2627			$passwordInput.attr('type', 'text').focus();
2628		} else {
2629			$button.addClass('password-visibility--invisible');
2630			$button.removeClass('password-visibility--visible');
2631			$passwordInput.attr('type', 'password').focus();
2632		}
2633	});
2634	
2635	if (siteName == SiteConstants.SiteIds.BootBarnUS) {
2636		$(document).off('submit.CouponForm').on('submit.CouponForm', 'form.coupon-form', function(event) {
2637			event.preventDefault();
2638			var $this = $(this);
2639			var $container = $this.closest('.enter-discount-code');
2640			$container.find('.error').remove();
2641
2642			$.ajax({
2643				type: 'GET',
2644				url: Urls.addCoupon,
2645				data: {
2646					couponCode: $this.find('input[type="text"]').val(),
2647					format: 'ajax'
2648				},
2649				success: function (data) {
2650					if (data && data.success) {
2651						window.location.href = $this.attr('action');
2652					} else {
2653						$container.append('<div class="error">There was a problem applying that code.</div>');
2654					}
2655				}
2656			});
2657		});
2658	}
2659
2660	// BOOT-5363: show/hide total savings message based on promotion callout messages
2661	$('.product-tile').each(function() {
2662		var $tile = $(this);
2663		if ($tile.find('.hide-total-savings').length > 0) {
2664			$tile.find('.product-total-savings').addClass('d-none');
2665		}
2666	});
2667	if($('.product-detail').find('.hide-total-savings').length > 0) {
2668		$('.product-total-savings').addClass('d-none');
2669	}
2670}
2671
2672/**
2673 * @private
2674 * @function
2675 * @description Adds tab functionality for slider panels.  When a tab is clicked, the corresponding panel will be displayed and all other panels will be hidden.
2676 */
2677function initTabPanels() {
2678	$(document).on('click', '.slider-panel-btn', function() {
2679		$('.slider-panel-btn').attr('aria-selected','false').removeClass('slider-panel-btn-active');
2680		$('.section-body').removeClass('slider-panel-active');
2681		var $button = $(this);
2682		var buttonID = $button.attr('id');
2683		var $panel = $('[aria-labelledby="' + buttonID + '"]');
2684		$button.attr('aria-selected','true').addClass('slider-panel-btn-active');
2685		$panel.addClass('slider-panel-active');
2686	});
2687}
2688
2689/**
2690 * @private
2691 * @function
2692 * @description Adds class ('js') to html for css targeting and loads js specific styles.
2693 */
2694function initializeDom() {
2695    // add class to html for css targeting
2696    $('html').addClass('js');
2697    // load js specific styles
2698    util.limitCharacters();
2699
2700    // DOM EVENT FOR HEADER STORE LOCATOR
2701	$('.stores-near-content .list-stores').delegate( 'a.make-this-mystore', 'click',function(e){
2702		e.preventDefault();
2703		var postCode = $(this).attr('data-postalcode');
2704		$('a.ico-crosshairs-use-my-location').data('postalcode',postCode);
2705		headerNavStoreSearch();
2706	});
2707
2708}
2709
2710// Resize Helper
2711function resizeDeviceHelper() {
2712	var countryOutfitters = 'country_outfitters_us';
2713	var bootBarn = 'bootbarn_us';
2714	var sliderMobileSwiper;
2715
2716	$('html').removeClass('size-mobile size-tablet size-desktop');
2717	if (window.innerWidth <= 767) {
2718		$('html').addClass('size-mobile');
2719	} else if (window.innerWidth <= 1024) {
2720		$('html').addClass('size-tablet');
2721	} else {
2722		$('html').addClass('size-desktop');
2723	}
2724	
2725	var $navigationContainer = $('#navigation');
2726	
2727	if (!$('.second-mobnav').hasClass('active')) {
2728		$('.nav-utilities').hide();
2729	}
2730	
2731	$('.nav-utilities, #navigation').removeClass('preload');
2732	$('#nav-utils').removeClass('loadhide');
2733	
2734	if ((siteName != bootBarn && window.innerWidth <= 1024) || window.innerWidth <= 767) {
2735		if (siteName != SiteConstants.SiteIds.BootBarnUS) {
2736			$navSecondary.insertAfter($navUl);
2737		}
2738
2739		$sectionErrorFigure.insertBefore($sectionErrorBox);
2740		$navUtilities.insertAfter($topBannerInnerLinks);
2741		if ($sliderMobile.length > 0) {
2742			sliderMobileSwiper = sliders.init('.slider-mobile', {dots: true, loop: true});
2743		}
2744
2745		var image = $(".slider-single-image img");
2746		$.removeData(image, 'elevateZoom');
2747		$('.zoomContainer').remove();
2748		
2749		if($('.section-offers.all-on-sale').length){
2750			setTimeout(function(){
2751				$('.section-offers.all-on-sale .chevron-collapse').each(function(){
2752					$(this).click();
2753				});
2754			}, 13);
2755			
2756		}
2757		$('.accordion-mobile').each(function () {
2758			var accMobile = $(this);
2759
2760			if (!accMobile.hasClass('active')) {
2761				accMobile.find('.accordion-mobile-body').hide();
2762			}
2763		});
2764	} else {
2765		$navUl.find('.has-dropdown.active').removeClass('active');
2766
2767		$navUtilities.insertBefore($topBannerBarInner);
2768		if (siteName != SiteConstants.SiteIds.BootBarnUS) {
2769			$navSecondary.prependTo($topBannerBar);
2770		}
2771
2772		$sectionErrorFigure.insertAfter($sectionErrorBox);
2773		$navigation.removeAttr('style');
2774
2775		if ($sliderMobile.length > 0) {
2776			if ($sliderMobile.find('.swiper-initialized').length > 0) {
2777				sliderMobileSwiper.destroy();
2778			}
2779			if ($sliderMobile.find('.slick-initialized').length > 0) {
2780				$sliderMobile.slick('unslick');
2781			}
2782		}
2783		$('.accordion-mobile-body').show();
2784		
2785		$('html').removeClass('menu-active');
2786		$('.menu-toggle').removeClass('active');
2787		$('#navigation .level-2').hide();
2788	}
2789	
2790	resizeLayoutHelper();
2791}
2792
2793function resizeLayoutHelper() {
2794	var newMarginTop = 0;
2795	var innerWidthThreshold;	
2796	if (siteName == SiteConstants.SiteIds.BootBarnUS) {
2797		innerWidthThreshold = 1024;
2798	} else {
2799		innerWidthThreshold = 766; /* Changing this to 766 since the mobile menu for CO and SHP isn't kicking in at 768 */
2800	}
2801	if (window.innerWidth <= innerWidthThreshold) {
2802		newMarginTop = parseFloat($('.header-wrapper').outerHeight());
2803	}
2804	if (siteName == SiteConstants.SiteIds.BootBarnUS) {
2805		if (!$('html').hasClass('menu-active')) {
2806			$('#main').css('margin-top', newMarginTop);
2807		}
2808	} else {
2809		if (!$('html').hasClass('menu-active')) {
2810			$('#wrapper').css('margin-top', newMarginTop);
2811		}
2812	}
2813	if (!$('html').hasClass('initialized')) {
2814		$('html').addClass('initialized');
2815	}
2816}
2817
2818function usemylocation(storeid) {
2819	var t_url = Urls.usecurrentlocation;
2820	if (storeid === undefined) {
2821		storeid = 'getall';
2822	}
2823
2824	$.ajax({
2825	   url: t_url,
2826	   type: 'POST',
2827	   data: { 'source' : 'ajax' },
2828	   dataType: 'json',
2829	   error: function() {
2830		   console.log('error');
2831	   },
2832	   success: function(dataJson) {
2833	      if( 'stores' in dataJson && dataJson['stores'].length > 0 ) {
2834	    	  	if(storeid == 'getall') {
2835	    	  		$('.stores-near-content .stores-near-body ul.list-stores').html('');
2836	    	  	}
2837				$('div.stores-near-actions a.go-to-findstore').html(Resources.FIND_MORE_STORES);
2838				if ( dataJson['stores'].length == 1 ) {
2839					$('.stores-near-content').slideUp();
2840				}
2841				dataJson['stores'].forEach(function(element, index){
2842					var store = element;
2843					var htmlDisplay = '';
2844					var storePhoneNumber = ('' + element['phone']).replace(/\D/g,'');
2845					var postalCodeVal = ( typeof element['postalCode'] != 'undefined' ? element['postalCode']  : '' );
2846
2847					if(storeid == 'getall') {
2848						if(index == 0) {
2849							htmlDisplay += '<span class="red store-name-value">'+element['name']+'</span>';
2850							htmlDisplay += element['address1'];
2851							htmlDisplay += '<br/>'+ ( typeof element['city'] != 'undefined' ? element['city'] +', ' : '' );
2852							htmlDisplay += ( typeof element['stateCode'] != 'undefined' ? element['stateCode'] +' ' : '' );
2853							htmlDisplay += postalCodeVal;
2854							htmlDisplay += '<br /><a href="tel:'+storePhoneNumber+'">'+store['phone']+'</a>';
2855							htmlDisplay += '<span>'+unescape(store['storeHours'])+'</span>';
2856							$('.stores-dropdown .current-store address').html(htmlDisplay);
2857							$('.stores-dropdown .current-store').slideDown();
2858						} else {
2859							htmlDisplay += '<li><h5><span>'+element['name']+'</span><a href="#" class="make-this-mystore" id="' + element['ID'] + '" data-postalcode="'+postalCodeVal+'">'+Resources.MAKE_THIS_MYSTORE+'</a></h5>';
2860							htmlDisplay += '<address>'+element['address1'];
2861							htmlDisplay += '<br/>'+ ( typeof element['city'] != 'undefined' ? element['city'] +', ' : '' );
2862							htmlDisplay += ( typeof element['stateCode'] != 'undefined' ? element['stateCode'] +' ' : '' );
2863							htmlDisplay += postalCodeVal;
2864							htmlDisplay += '<br /><a href="tel:'+storePhoneNumber+'">'+store['phone']+'</a>';
2865							htmlDisplay += '/<address>' + unescape(store['storeHours']);
2866							htmlDisplay += '</li>';
2867							$('.stores-near-content .stores-near-body ul.list-stores').append($(htmlDisplay));
2868							$('.stores-near-content').slideDown();
2869						}
2870					} else {
2871						if(storeid == element['ID']) {
2872							htmlDisplay += '<span class="red store-name-value">'+element['name']+'</span>';
2873							htmlDisplay += element['address1'];
2874							htmlDisplay += '<br/>'+ ( typeof element['city'] != 'undefined' ? element['city'] +', ' : '' );
2875							htmlDisplay += ( typeof element['stateCode'] != 'undefined' ? element['stateCode'] +' ' : '' );
2876							htmlDisplay += postalCodeVal;
2877							htmlDisplay += '<br /><a href="tel:'+storePhoneNumber+'">'+store['phone']+'</a>';
2878							htmlDisplay += '<span>'+unescape(store['storeHours'])+'</span>';
2879							$('.stores-dropdown .current-store address').html(htmlDisplay);
2880							$('.stores-dropdown .current-store').slideDown();
2881						}
2882					}
2883
2884				});
2885			} else {
2886				$('.stores-near-content').slideUp();
2887				$('div.stores-near-actions a.go-to-findstore').html(Resources.FIND_STORES);
2888				$('.stores-dropdown .current-store').slideUp();
2889				if ( $('.stores-dropdown h3.no_results').length == 0 ) {
2890					$('.stores-dropdown').prepend('<h3 class="no_results">'+Resources.NO_STORE_RESULTS+'</h3>');
2891				} else {
2892					$('.stores-dropdown h3.no_results').fadeIn();
2893				}
2894				$('.stores-dropdown h3.no_results').show().delay(2000).fadeOut();
2895		  }
2896	   }
2897	});
2898}
2899
2900
2901/** 
2902 *
2903 * Moves customer specific stores info in main header to mobile utility header.
2904 * This is needed because the store info is part of a remote include in another spot on desktop. 
2905 *
2906 */
2907function mobileStores() {	
2908	var $mobileStoreMetadata = $('#mobile-storeMetadata');
2909	var $mobileStoreBannerText = $('#mobile-banner-store-text');
2910	
2911	if ($mobileStoreMetadata !== undefined && $mobileStoreMetadata != null && $mobileStoreMetadata.length  > 0){
2912		if ($mobileStoreBannerText !== undefined && $mobileStoreBannerText != null && $mobileStoreBannerText.length  > 0){
2913			var isOpen = $mobileStoreMetadata.data('is-open');
2914			if (isOpen){
2915				var storeName = $mobileStoreMetadata.data('store-name');
2916				var openUntil =  $mobileStoreMetadata.data('open-until');
2917				var closingTime =  $mobileStoreMetadata.data('closing-time');
2918				var bannerText = storeName + ' &#8226; ' + openUntil + ' ' + closingTime;
2919				$mobileStoreBannerText.html(bannerText);	
2920			} else {
2921				var closedText =  $mobileStoreMetadata.data('closed-text');
2922				$mobileStoreBannerText.html(closedText);
2923			}
2924		}
2925	}
2926}
2927
2928function setCookie(cname, cvalue, exdays) {
2929	var d = new Date();
2930	d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
2931	var expires = "expires="+d.toUTCString();
2932	document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
2933  }
2934  
2935  function getCookie(cname) {
2936	var name = cname + "=";
2937	var ca = document.cookie.split(';');
2938	for(var i = 0; i < ca.length; i++) {
2939	  var c = ca[i];
2940	  while (c.charAt(0) == ' ') {
2941		c = c.substring(1);
2942	  }
2943	  if (c.indexOf(name) == 0) {
2944		return c.substring(name.length, c.length);
2945	  }
2946	}
2947	return "";
2948  }
2949
2950
2951
2952function addPromotionDataLayers() {
2953	var $containers = $('.accordion:visible, .section-offers:visible, .cat-header-content.cat-header-slider:visible');
2954	if ($containers.length > 0) {
2955		$containers.each(function() {
2956			var $container = $(this);
2957			if ($container.attr('data-layer-added') != 'true' && $container.find('.product-tile').length > 0) {
2958				var containerId = $container.attr('content-slot-id') || $container.attr('id');
2959
2960				var dataObject = {
2961					promotion_name: containerId
2962				}
2963
2964				var pageType;
2965				var $wrapper = $container.closest('.add-to-cart-modal');
2966				if ($wrapper.length > 0) {
2967					pageType = 'addtocartmodal';
2968				}
2969
2970				if (!pageType) {
2971					$wrapper = $('#wrapper');
2972					if ($wrapper.length > 0) {
2973						if (pageContext && pageContext.type) {
2974							switch (pageContext.type) {
2975								case 'storefront':
2976								pageType = 'Homepage';
2977								break;
2978
2979								case 'search':
2980								pageType = 'PLP';
2981								break;
2982
2983								case 'product':
2984								pageType = 'PDP';
2985								break;
2986
2987								case 'cart':
2988								pageType = 'Cart';
2989								break;
2990
2991								case 'onepcheckout_groups':
2992								pageType = 'Checkout';
2993								break;
2994
2995								case 'orderconfirmation':
2996								pageType = 'Order Confirmation';
2997								break;
2998
2999								case 'customerservice':
3000								pageType = 'Customer Service';
3001								break;
3002
3003								case 'account':
3004								pageType = 'Account';
3005								break;
3006
3007								case 'content':
3008								pageType = 'Content Page';
3009								break;
3010
3011								case 'error':
3012								pageType = 'Error Page';
3013								break;									
3014							}
3015						}
3016					}
3017				}
3018
3019				if (pageType) {
3020					dataObject.promotion_id = pageType;
3021				}
3022
3023				GoogleTagManagerClient.PushDataLayerForDataLayerType('ViewPromotion', dataObject, { Container: $container });
3024				$container.attr('data-layer-added', true);
3025			}
3026		});
3027	}
3028}
3029
3030var pages = {
3031	storefront: require('./pages/storefront'),
3032	account: require('./pages/account'),
3033	bootfinder: require('./pages/bootfinder'),
3034    cart: require('./pages/cart'),
3035	cart_echo: require('./pages/cart_echo'),
3036    checkout: require('./pages/checkout'),
3037	checkout_echo: require('../../../app_storefront_core/cartridge/js/pages/checkout_echo'),
3038    compare: require('./pages/compare'),
3039    product: require('./pages/product'),
3040    registry: require('./pages/registry'),
3041    search: require('./pages/search'),
3042    storelocator: require('./pages/storelocator'),
3043    onepcheckout_groups: require('./pages/onepcheckout_groups'),
3044	orderconfirmation: require('./pages/orderconfirmation'),
3045    categorylanding: require('./pages/categorylanding'),
3046    content: require('./pages/content'),
3047    authentication_EmailAddressCapture: require('./pages/Authentication/EmailAddressCapture') 
3048};
3049
3050var lastScrollTop = 0; 
3051var scrollDelta = 100;
3052var fixedHeaderLimit = 800;
3053var headerWrapper = $('.header-wrapper');
3054
3055var app = {
3056    init: function () {
3057		$.extend(page, window.pageContext);
3058
3059    	$sliderMobile = $('.slider-mobile');
3060		$navSecondary 			= $('.nav-secondary');
3061		$navUtilities 			= $('.nav-utilities');
3062		$navUl 					= $('#navigation > ul');
3063		$topBannerBar 			= $('.top-banner-bar');
3064		$topBannerBarInner		= $('.top-banner-bar-inner');
3065		$navigation 			= $('#navigation');
3066		$topBannerInnerLinks 	= $('.top-banner-inner-links');
3067		$selectSort 			= $('#sort-by');
3068		$secondaryHead 			= $('#secondary .refinements-head');
3069		$searchResultContainer 	= $('#search-result');
3070		$productContainerImage	= $('#pdpMain .product-image-container');
3071		$productDetail			= $('#pdpMain .product-detail');
3072		$sectionCheckout 		= $('.section-checkout');
3073		$mainInnerAlt			= $('.main-inner-alt');
3074		$sectionErrorFigure 	= $('.section-error figure');
3075		$sectionErrorBox 		= $('.section-error .box');
3076		$footerMenuItems		= $('.footer-item .content-asset');
3077		$promoMessages		= $productContainerImage.find('.promo-messages');
3078    	siteName = pageContext.site || '';
3079    	window.sgDialog = require('./dialog');
3080    	window.sgUtil = require('./util');
3081    	
3082        if (document.cookie.length === 0) {
3083            $('<div/>').addClass('browser-compatibility-alert cookies').append($('<p/>').addClass('browser-error').html(Resources.COOKIES_DISABLED)).appendTo('#browser-check');
3084            $('.browser-compatibility-alert.cookies').prepend('<i class="fa fa-exclamation-triangle fa-3x pull-left"></i>');
3085        }
3086        initializeDom();
3087        initializeEvents();
3088        
3089		login.init(page.options);
3090		register.init();
3091        navigation.Initialize();
3092        countries.init();
3093        //tooltip.init();
3094        minicart.init();
3095        validator.init();
3096        rating.init();
3097        searchplaceholder.init();
3098		rotate3d.init();
3099        count_down.init();
3100        lazyloadPageElements.init();
3101        giftCardBalanceInquiryHelper.initializeAllFoundComponents();
3102		slickHelpers.init();
3103		if (pageContext && 'type' in pageContext && pageContext.type != 'cart' && pageContext.type != 'onepcheckout_groups') {
3104            quantityStepper.init();
3105        }
3106		wishlist.init();
3107		Globale.initialize();
3108		cordialTagging.init();
3109		
3110        // execute page specific initializations
3111        
3112        var ns = page.ns;
3113        if (ns && pages[ns] && pages[ns].init) {
3114            pages[ns].init(page.options || {});
3115        }
3116
3117        GoogleTagManagerClient.PushDataLayerForPage(page);
3118        EmailSignup.Initialize(page);
3119        
3120        // Check TLS status if indicated by site preference
3121        if (SitePreferences.CHECK_TLS === true) {
3122            tls.getUserAgent();
3123		}
3124
3125        // Initialize Sticky header events
3126        $(window).scroll(function(event){
3127			if ((siteName == SiteConstants.SiteIds.BootBarnUS || siteName == SiteConstants.SiteIds.SheplersUS) && window.innerWidth <= 1023) {
3128				if ($(window).scrollTop() > fixedHeaderLimit) {
3129					headerWrapper.addClass('header-fixed');
3130				} else {
3131					headerWrapper.removeClass('header-fixed');
3132				}
3133
3134				var nowScrollTop = $(this).scrollTop();
3135				if(Math.abs(lastScrollTop - nowScrollTop) >= scrollDelta){
3136					if (nowScrollTop > lastScrollTop){
3137						if (headerWrapper.hasClass('header-fixed-active')) {
3138							headerWrapper.removeClass('header-fixed-active');
3139						}
3140					} else {
3141						if ($(window).scrollTop() > fixedHeaderLimit) {
3142							if (!headerWrapper.hasClass('header-fixed-active')) {
3143								headerWrapper.addClass('header-fixed-active');
3144							}
3145						} else {
3146							headerWrapper.removeClass('header-fixed-active');
3147						}
3148					}
3149					lastScrollTop = nowScrollTop;
3150		 		}
3151			}
3152
3153			if (siteName == SiteConstants.SiteIds.BootBarnUS && $('html').hasClass('size-tablet') && window.innerWidth == 1024) {
3154				var nowScrollTop = $(this).scrollTop();
3155				if(Math.abs(nowScrollTop) > 300) {
3156					if ($('#navigation .has-dropdown').hasClass('active')) {
3157						$('#navigation .has-dropdown').removeClass('active');
3158						$('html').removeClass('menu-active');
3159						$('.header-wrapper .top-banner').removeClass('nav-active');
3160						$('html').removeClass('desktop-nav-active');
3161					}
3162					
3163				}
3164			}
3165
3166			if ( $(".top-banner-inner .primary-logo a img.mobile-logo[src*=sheplers_us]").length ) {
3167	            if ( $(".second-mobnav").length > 0 && $(".second-mobnav").is(":visible") ) {
3168	                var st = $(this).scrollTop();
3169	                
3170	                if (st > 0) {
3171	                    $(".top-banner-bar .shell").hide();
3172	                    $(".top-banner div.top-banner-inner").css("margin-top","0px");
3173	                    $(".top-banner div.top-banner-bar .header-search").attr("style","top:55px !important");
3174	                } else {
3175	                    $(".top-banner-bar .shell").show();
3176	                    $(".top-banner div.top-banner-inner").css("margin-top","25px");
3177	                    $(".top-banner div.top-banner-bar .header-search").attr("style","top:80px !important");
3178	                }
3179	            } else {
3180	            		$(".top-banner-bar .shell").show();
3181	            		$(".top-banner div.top-banner-inner").css("margin-top","0px");
3182	            		$(".top-banner div.top-banner-bar .header-search").attr("style","top:80px !important");
3183	            		if ( $(".top-banner-bar-aside #second-mobnav").length ) {
3184	            			$(".top-banner-bar-aside #second-mobnav").show();
3185	            		}
3186	            }
3187			}
3188        });
3189
3190		resizeDeviceHelper();
3191    }
3192};
3193
3194// general extension functions
3195(function () {
3196    String.format = function () {
3197        var s = arguments[0];
3198        var i, len = arguments.length - 1;
3199        for (i = 0; i < len; i++) {
3200            var reg = new RegExp('\\{' + i + '\\}', 'gm');
3201            s = s.replace(reg, arguments[i + 1]);
3202        }
3203        return s;
3204    };
3205})();
3206
3207// initialize app
3208$(document).ready(function () {
3209    app.init();
3210});
3211
3212(function () {
3213 String.format = function () {
3214     var s = arguments[0];
3215     var i, len = arguments.length - 1;
3216     for (i = 0; i < len; i++) {
3217         var reg = new RegExp('\\{' + i + '\\}', 'gm');
3218         s = s.replace(reg, arguments[i + 1]);
3219     }
3220     return s;
3221 };
3222})();
3223
3224
3225},{"../../../app_storefront_core/cartridge/js/pages/checkout_echo":67,"../../../app_storefront_workforce/cartridge/js/login":109,"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"./ajax":3,"./alert":4,"./captcha":14,"./components/CordialTagging":17,"./components/EmailForm":18,"./components/EmailSignup":19,"./components/Globale":22,"./components/NavigationController":26,"./components/Notification":27,"./cookieprivacy":33,"./countdown":34,"./countries":35,"./dialog":36,"./helpers/GiftCardBalanceInquiryHelper":40,"./jquery-ext":41,"./lazyloadPageElements":42,"./login":43,"./loginDialog":44,"./minicart":45,"./page":46,"./pages/Authentication/EmailAddressCapture":47,"./pages/account":48,"./pages/bootfinder":49,"./pages/cart":50,"./pages/cart_echo":51,"./pages/categorylanding":52,"./pages/c
3225heckout":62,"./pages/compare":68,"./pages/content":69,"./pages/onepcheckout_groups":70,"./pages/orderconfirmation":71,"./pages/product":75,"./pages/registry":79,"./pages/search":80,"./pages/storefront":81,"./pages/storelocator":82,"./productRotate3d.js":85,"./quantityStepper":87,"./rating":89,"./register":90,"./searchbar":91,"./searchplaceholder":92,"./slickSliderHelpers":94,"./sliders":95,"./tls":100,"./util":102,"./validator":103,"./watermark":104,"./wishlist":105,"promise":134}],6:[function(require,module,exports){
3226'use strict';
3227
3228var dialog = require('./dialog'),
3229    page = require('./page'),
3230    util = require('./util');
3231
3232var selectedList = [];
3233var maxItems = 1;
3234var bliUUID = '';
3235
3236/**
3237 * @private
3238 * @function
3239 * description Gets a list of bonus products related to a promoted product
3240 */
3241function getBonusProducts() {
3242    var bonusproducts = [];
3243
3244    var i, len;
3245    for (i = 0, len = selectedList.length; i < len; i++) {
3246        var p = {
3247            pid: selectedList[i].pid,
3248            qty: selectedList[i].qty,
3249            options: {}
3250        };
3251        var a, alen, bp = selectedList[i];
3252        if (bp.options) {
3253            for (a = 0, alen = bp.options.length; a < alen; a++) {
3254                var opt = bp.options[a];
3255                p.options = {optionName:opt.name, optionValue:opt.value};
3256            }
3257        }
3258        bonusproducts.push({product:p});
3259    }
3260    return {bonusproducts: bonusproducts};
3261}
3262
3263var selectedItemTemplate = function (data) {
3264    var attributes = '';
3265    for (var attrID in data.attributes) {
3266        var attr = data.attributes[attrID];
3267        attributes += '<li data-attribute-id="' + attrID + '">\n';
3268        attributes += '<span class="display-name">' + attr.displayName + '</span>: ';
3269        attributes += '<span class="display-value">' + attr.displayValue + '</span>\n';
3270        attributes += '</li>';
3271    }
3272    attributes += '<li class="item-qty">\n';
3273    attributes += '<span class="display-name">Qty</span>: ';
3274    attributes += '<span class="display-value">' + data.qty + '</span>';
3275    return [
3276        '<li class="selected-bonus-item" data-uuid="' + data.uuid + '" data-pid="' + data.pid + '">',
3277        '<i class="remove-link fa fa-remove" title="Remove this product" href="#"></i>',
3278        '<div class="item-name">' + data.name + '</div>',
3279        '<ul class="item-attributes">',
3280        attributes,
3281        '<ul>',
3282        '<li>'
3283    ].join('\n');
3284};
3285
3286// hide swatches that are not selected or not part of a Product Variation Group
3287var hideSwatches = function () {
3288    $('.bonus-product-item:not([data-producttype="master"]) .swatches li').not('.selected').not('.variation-group-value').hide();
3289    // prevent unselecting the selected variant
3290    $('.bonus-product-item .swatches .selected').on('click', function () {
3291        return false;
3292    });
3293};
3294
3295/**
3296 * @private
3297 * @function
3298 * @description Updates the summary page with the selected bonus product
3299 */
3300function updateSummary() {
3301    var $bonusProductList = $('#bonus-product-list');
3302    if (!selectedList.length) {
3303        $bonusProductList.find('li.selected-bonus-item').remove();
3304    } else {
3305        var ulList = $bonusProductList.find('ul.selected-bonus-items').first();
3306        var i, len;
3307        for (i = 0, len = selectedList.length; i < len; i++) {
3308            var item = selectedList[i];
3309            var li = selectedItemTemplate(item);
3310            $(li).appendTo(ulList);
3311        }
3312    }
3313
3314    // get remaining item count
3315    var remain = maxItems - selectedList.length;
3316    $bonusProductList.find('.bonus-items-available').text(remain);
3317    if (remain <= 0) {
3318        $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');
3319    } else {
3320        $bonusProductList.find('.select-bonus-item').removeAttr('disabled');
3321    }
3322    $(document).trigger('update-summary');
3323}
3324
3325function initializeGrid () {
3326    var $bonusProduct = $('#bonus-product-dialog'),
3327        $bonusProductList = $('#bonus-product-list'),
3328        bliData = $bonusProductList.data('line-item-detail');
3329    maxItems = bliData.maxItems;
3330    bliUUID = bliData.uuid;
3331
3332    if (bliData.itemCount >= maxItems) {
3333        $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');
3334    }
3335
3336    var cartItems = $bonusProductList.find('.selected-bonus-item');
3337    cartItems.each(function () {
3338        var ci = $(this);
3339        var product = {
3340            uuid: ci.data('uuid'),
3341            pid: ci.data('pid'),
3342            qty: ci.find('.item-qty').text(),
3343            name: ci.find('.item-name').html(),
3344            attributes: {}
3345        };
3346        var attributes = ci.find('ul.item-attributes li');
3347        attributes.each(function () {
3348            var li = $(this);
3349            product.attributes[li.data('attributeId')] = {
3350                displayName:li.children('.display-name').html(),
3351                displayValue:li.children('.display-value').html()
3352            };
3353        });
3354        selectedList.push(product);
3355    });
3356
3357    $bonusProductList.on('click', '.bonus-product-item a[href].swatchanchor', function (e) {
3358        e.preventDefault();
3359        var url = this.href,
3360            $this = $(this);
3361        url = util.appendParamsToUrl(url, {
3362            'source': 'bonus',
3363            'format': 'ajax'
3364        });
3365        $.ajax({
3366            url: url,
3367            success: function (response) {
3368                $this.closest('.bonus-product-item').empty().html(response);
3369                hideSwatches();
3370            }
3371        });
3372    })
3373    .on('change', '.input-text', function () {
3374        $bonusProductList.find('.select-bonus-item').removeAttr('disabled');
3375        $(this).closest('.bonus-product-form').find('.quantity-error').text('');
3376    })
3377    .on('click', '.select-bonus-item', function (e) {
3378        e.preventDefault();
3379        if (selectedList.length >= maxItems) {
3380            $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');
3381            $bonusProductList.find('.bonus-items-available').text('0');
3382            return;
3383        }
3384
3385        var form = $(this).closest('.bonus-product-form'),
3386            detail = $(this).closest('.product-detail'),
3387            uuid = form.find('input[name="productUUID"]').val(),
3388            qtyVal = form.find('input[name="Quantity"]').val(),
3389            qty = (isNaN(qtyVal)) ? 1 : (+qtyVal);
3390
3391        if (qty > maxItems) {
3392            $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');
3393            form.find('.quantity-error').text(Resources.BONUS_PRODUCT_TOOMANY);
3394            return;
3395        }
3396
3397        var product = {
3398            uuid: uuid,
3399            pid: form.find('input[name="pid"]').val(),
3400            qty: qty,
3401            name: detail.find('.product-name').text(),
3402            attributes: detail.find('.product-variations').data('attributes'),
3403            options: []
3404        };
3405
3406        var optionSelects = form.find('.product-option');
3407
3408        optionSelects.each(function () {
3409            product.options.push({
3410                name: this.name,
3411                value: $(this).val(),
3412                display: $(this).children(':selected').first().html()
3413            });
3414        });
3415        selectedList.push(product);
3416        updateSummary();
3417    })
3418    .on('click', '.remove-link', function (e) {
3419        e.preventDefault();
3420        var container = $(this).closest('.selected-bonus-item');
3421        if (!container.data('uuid')) { return; }
3422
3423        var uuid = container.data('uuid');
3424        var i, len = selectedList.length;
3425        for (i = 0; i < len; i++) {
3426            if (selectedList[i].uuid === uuid) {
3427                selectedList.splice(i, 1);
3428                break;
3429            }
3430        }
3431        updateSummary();
3432    })
3433    .on('click', '.add-to-cart-bonus', function (e) {
3434        e.preventDefault();
3435        var url = util.appendParamsToUrl(Urls.addBonusProduct, {bonusDiscountLineItemUUID: bliUUID});
3436        var bonusProducts = getBonusProducts();
3437        if (bonusProducts.bonusproducts[0].product.qty > maxItems) {
3438            bonusProducts.bonusproducts[0].product.qty = maxItems;
3439        }
3440        // make the server call
3441        $.ajax({
3442            type: 'POST',
3443            dataType: 'json',
3444            cache: false,
3445            contentType: 'application/json',
3446            url: url,
3447            data: JSON.stringify(bonusProducts)
3448        })
3449        .done(function () {
3450            // success
3451            page.refresh();
3452        })
3453        .fail(function (xhr, textStatus) {
3454            // failed
3455            if (textStatus === 'parsererror') {
3456                window.alert(Resources.BAD_RESPONSE);
3457            } else {
3458                window.alert(Resources.SERVER_CONNECTION_ERROR);
3459            }
3460        })
3461        .always(function () {
3462            $bonusProduct.dialog('close');
3463        });
3464    })
3465    .on('click', '#more-bonus-products', function (e) {
3466        e.preventDefault();
3467        var uuid = $('#bonus-product-list').data().lineItemDetail.uuid;
3468
3469        //get the next page of choice of bonus products
3470        var lineItemDetail = JSON.parse($('#bonus-product-list').attr('data-line-item-detail'));
3471        lineItemDetail.pageStart = lineItemDetail.pageStart + lineItemDetail.pageSize;
3472        $('#bonus-product-list').attr('data-line-item-detail', JSON.stringify(lineItemDetail));
3473
3474        var url = util.appendParamsToUrl(Urls.getBonusProducts, {
3475            bonusDiscountLineItemUUID: uuid,
3476            format: 'ajax',
3477            lazyLoad: 'true',
3478            pageStart: lineItemDetail.pageStart,
3479            pageSize: $('#bonus-product-list').data().lineItemDetail.pageSize,
3480            bonusProductsTotal: $('#bonus-product-list').data().lineItemDetail.bpTotal
3481        });
3482
3483        $.ajax({
3484            type: 'GET',
3485            cache: false,
3486            contentType: 'application/json',
3487            url: url
3488        })
3489        .done(function (data) {
3490            //add the new page to DOM and remove 'More' link if it is the last page of results
3491            $('#more-bonus-products').before(data);
3492            if ((lineItemDetail.pageStart + lineItemDetail.pageSize) >= $('#bonus-product-list').data().lineItemDetail.bpTotal) {
3493                $('#more-bonus-products').remove();
3494            }
3495        })
3496        .fail(function (xhr, textStatus) {
3497            if (textStatus === 'parsererror') {
3498                window.alert(Resources.BAD_RESPONSE);
3499            } else {
3500                window.alert(Resources.SERVER_CONNECTION_ERROR);
3501            }
3502        });
3503    });
3504}
3505
3506var bonusProductsView = {
3507    /**
3508     * @function
3509     * @description Open the list of bonus products selection dialog
3510     */
3511    show: function (url) {
3512        var $bonusProduct = $('#bonus-product-dialog');
3513        // create the dialog
3514        dialog.open({
3515            target: $bonusProduct,
3516            url: url,
3517            options: {
3518                width: 795,
3519                title: Resources.BONUS_PRODUCTS
3520            },
3521            callback: function () {
3522                initializeGrid();
3523                hideSwatches();
3524            }
3525        });
3526    },
3527    /**
3528     * @function
3529     * @description Open bonus product promo prompt dialog
3530     */
3531    loadBonusOption: function () {
3532        var    self = this,
3533            bonusDiscountContainer = document.querySelector('.bonus-discount-container');
3534        if (!bonusDiscountContainer) { return; }
3535
3536        // get the html from minicart, then trash it
3537        var bonusDiscountContainerHtml = bonusDiscountContainer.outerHTML;
3538        bonusDiscountContainer.parentNode.removeChild(bonusDiscountContainer);
3539
3540        dialog.open({
3541            html: bonusDiscountContainerHtml,
3542            options: {
3543                width: 400,
3544                title: Resources.BONUS_PRODUCT,
3545                buttons: [{
3546                    text: Resources.SELECT_BONUS_PRODUCTS,
3547                    click: function () {
3548                        var uuid = $('.bonus-product-promo').data('lineitemid'),
3549                            url = util.appendParamsToUrl(Urls.getBonusProducts, {
3550                                bonusDiscountLineItemUUID: uuid,
3551                                source: 'bonus',
3552                                format: 'ajax',
3553                                lazyLoad: 'false',
3554                                pageStart: 0,
3555                                pageSize: 10,
3556                                bonusProductsTotal: -1
3557                            });
3558                        $(this).dialog('close');
3559                        self.show(url);
3560                    }
3561                }, {
3562                    text: Resources.NO_THANKS,
3563                    click: function () {
3564                        $(this).dialog('close');
3565                    }
3566                }]
3567            },
3568            callback: function () {
3569                // show hide promo details
3570                $('.show-promo-details').on('click', function () {
3571                    $('.promo-details').toggleClass('visible');
3572                });
3573            }
3574        });
3575    }
3576};
3577
3578module.exports = bonusProductsView;
3579
3580},{"./dialog":36,"./page":46,"./util":102}],7:[function(require,module,exports){
3581'use strict';
3582
3583var _ = require('lodash');
3584var addToCartOptionsStoreSearch = require('./AddToCartOptionsStoreSearch');
3585var dialog = require('../../dialog');
3586var loadingIndicator = require('../../components/LoadingIndicator');
3587var LocalDeliveryDialog = require('./LocalDeliveryDialog');
3588var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
3589var util = require('../../util');
3590var tooltip = require('../../tooltip');
3591
3592exports.initialize = initialize;
3593
3594var options;
3595var optionsStoreSearch;
3596var optionsLocalDelivery;
3597var dataBuffer;
3598var storeSearch;
3599var localDeliveryStoreSearch;
3600
3601var resultContainerTemplate;
3602var resultItemTemplate;
3603var localDeliveryDecorator;
3604
3605var $container;
3606var $storeSearchButton;
3607var $storeSearchButtonContainer;
3608var $addToCartOptions;
3609var $bopisButton;
3610
3611function initialize($containerParameter, optionsParameter) {
3612	dataBuffer = {};
3613	options = $.extend({
3614		successCallback: null,
3615		errorCallback: null,
3616		StorePickupOnly: $('#quantity_source_type').val() == 'BOP'
3617	}, optionsParameter);
3618
3619	var selectedSize;
3620	var $selectedSizeInDropdown = $('div.product-variations .size.size-options li.selected');
3621	var $selectedLength = $('div.product-variations .length.length-options li.selected');
3622	var $selectedWidth = $('div.product-variations .width.width-options li.selected');
3623	if ($selectedLength.length && $selectedWidth.length) {
3624		selectedSize = $selectedLength.find('[data-length-name]').attr('data-length-name') + ' ' + $selectedWidth.find('[data-width-name]').attr('data-width-name');
3625	} else if ($selectedSizeInDropdown.length > 0) {
3626		selectedSize = $selectedSizeInDropdown.find('[data-size-name]').attr('data-size-name');
3627	} else {
3628		selectedSize = util.getPreferenceValueFromWindowLocation('size');
3629	}
3630	storeSearch = new addToCartOptionsStoreSearch();
3631	localDeliveryStoreSearch = new addToCartOptionsStoreSearch();
3632	optionsStoreSearch = Object.assign(options);
3633	optionsLocalDelivery = Object.assign({
3634		LocalDeliveryOnly: true,
3635		Events: {
3636			SelectStore: 'Bopis.LocalDelivery.StoreSelect'
3637		},
3638		Functions: {
3639			Close: function() {
3640				$('.dialog-bopis-options').removeClass('hidden');
3641				$('.ui-widget-overlay').removeClass('hidden');
3642				$('.bopis-local-delivery-store-search-container').removeClass('show');
3643			}
3644		},
3645	}, options);
3646
3647	resultContainerTemplate = _.template($('#template-bopis-result-container').html(), TemplateConstants.Lodash.Settings);
3648	resultItemTemplate = _.template($('#template-bopis-result-item').html(), TemplateConstants.Lodash.Settings);
3649	localDeliveryDecorator = _.template($('#template-bopis-localdelivery-decorator').html(), TemplateConstants.Lodash.Settings);
3650
3651	$container = $containerParameter;
3652	$storeSearchButton = $container.find('.btn.store-search');
3653	$storeSearchButtonContainer = $container.find('.btn-store-search-container');
3654	$bopisButton = $container.find('.btn.bopis');
3655
3656	$addToCartOptions = $container.find('.bopis-add-to-cart-options');
3657	$addToCartOptions.attr('state', 'form');
3658	initializeTooltips(selectedSize);
3659
3660	$(document).off('click.SearchByStore.Display').on('click.SearchByStore.Display', '.bopis-add-to-cart-options-container .search-store:not(.disabled)', function(event) {
3661		event.preventDefault();
3662		event.stopImmediatePropagation();
3663
3664		var sizeSelected = $('div.product-variations .size.size-options li.selected').length;
3665		var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
3666		var widthSelected = $('div.product-variations .width.width-options li.selected').length;
3667		var colorSelected = $('div.product-variations .color.color-options li.selected').length;
3668		var isSizeSelected = (sizeSelected || (lengthSelected && widthSelected)) && colorSelected;
3669
3670		if (options.StoreUIVersion != '2024Redesign') {
3671			if (!isSizeSelected && !colorSelected) {
3672				dialog.open({
3673					html: 'Please select a size and color.',
3674					options: {
3675						dialogClass: 'dialog-alert',
3676						draggable: false
3677					}
3678				});
3679				return false;
3680			} else if (!isSizeSelected) {
3681				dialog.open({
3682					html: 'Please select a size',
3683					options: {
3684						dialogClass: 'dialog-alert',
3685						draggable: false
3686					}
3687				});
3688				return false;
3689			} else if (!colorSelected) {
3690				dialog.open({
3691					html: 'Please select a color',
3692					options: {
3693						dialogClass: 'dialog-alert',
3694						draggable: false
3695					}
3696				});
3697				return false;
3698			}
3699		}
3700
3701		if ($container.find('input[name="AddToCartOption"]:checked').val() == 'delivery') {
3702			localDeliveryStoreSearch.initialize($('.bopis-local-delivery-store-search-container'), optionsLocalDelivery);
3703		} else {
3704			storeSearch.initialize($('.bopis-add-to-cart-store-search-container'), optionsStoreSearch);
3705		}
3706    });
3707
3708	$(document).off('click.SearchByZipCode.Display').on('click.SearchByZipCode.Display', '.bopis-add-to-cart-options .search-zipcode:not(.disabled)', function (event) {
3709		event.preventDefault();
3710		event.stopImmediatePropagation();
3711
3712		localDeliveryStoreSearch.initialize($('.bopis-local-delivery-store-search-container'), optionsLocalDelivery);
3713    });
3714
3715	$(document).off('Bopis.LocalDelivery.StoreSelect').on('Bopis.LocalDelivery.StoreSelect', function (event, store) {
3716		$.ajax({
3717			method: 'GET',
3718			url: Urls.Bopis.GetLocalDeliveryFormData,
3719			dataType: 'json',
3720			success: function(localDeliveryFormData) {
3721				LocalDeliveryDialog.initialize(optionsLocalDelivery, store, localDeliveryFormData);
3722			},
3723			error: function() {
3724				LocalDeliveryDialog.initialize(optionsLocalDelivery, store);
3725			}
3726		});
3727	});
3728
3729	$(document).off('Bopis.LocalDelivery.Display').on('Bopis.LocalDelivery.Display', function (event, data) {
3730		var $storeData = $container.find('.selected-store[store-data]');
3731		if ($storeData.length > 0) {
3732			if (data) {
3733				optionsLocalDelivery = $.extend(optionsLocalDelivery, data);
3734			}
3735
3736			$.ajax({
3737				method: 'GET',
3738				url: Urls.Bopis.GetLocalDeliveryFormData,
3739				dataType: 'json',
3740				success: function(localDeliveryFormData) {
3741					LocalDeliveryDialog.initialize(optionsLocalDelivery, JSON.parse($storeData.attr('store-data')), localDeliveryFormData);
3742				},
3743				error: function() {
3744					LocalDeliveryDialog.initialize(optionsLocalDelivery, JSON.parse($storeData.attr('store-data')));
3745				}
3746			});
3747		}
3748	});
3749	
3750	$(document).off('Bopis.StoreSearch.Select').on('Bopis.StoreSearch.Select', function (event, storeId) {
3751		dataBuffer['SelectedLocation'] = storeId;
3752		redraw(true);
3753	});
3754
3755	$(document).off('click.Store.Select').on('click.Store.Select', '.bopis-add-to-cart-options-container .select-store', function(event) {
3756		dataBuffer['SelectedLocation'] = $(this).closest('[store-id]').attr('store-id');
3757		redraw(true);
3758	});
3759
3760	$(document).off('ProductDetailPage.Variant.AddToCartOptions').on('ProductDetailPage.Variant.AddToCartOptions', function() {
3761		redraw();
3762	});
3763
3764	$(document).off('click.AddToCartOptionsSelect').on('click.AddToCartOptionsSelect', '.bopis-add-to-cart-options-container li[option] input', function(event) {
3765		$(document).trigger('ProductDetailPage.AddToCartOptionsSelect', { ProductId: $('#pid').val(), StoreId: $('.bopis-add-to-cart-options-container .selected-store').attr('store-id') });
3766	});
3767
3768	dataBuffer['SelectedLocation'] = options.SelectedLocations;
3769}
3770
3771function redraw(saveSelectedLocation) {
3772	var selectedColor;
3773	var $selectedColorInSwatch = $('div.product-variations .color.color-options li.selected');;
3774	if ($selectedColorInSwatch.length > 0) {
3775		selectedColor = $selectedColorInSwatch.find('[data-color-name]').attr('data-color-id');
3776	} else {
3777		selectedColor = util.getPreferenceValueFromWindowLocation('color');
3778	}
3779
3780	var selectedSize;
3781	var $selectedSizeInDropdown = $('div.product-variations .size.size-options li.selected');
3782	var $selectedLength = $('div.product-variations .length.length-options li.selected');
3783	var $selectedWidth = $('div.product-variations .width.width-options li.selected');
3784	if ($selectedLength.length && $selectedWidth.length) {
3785		selectedSize = $selectedLength.find('[data-length-name]').attr('data-length-name') + ' ' + $selectedWidth.find('[data-width-name]').attr('data-width-name');
3786	} else if ($selectedSizeInDropdown.length > 0) {
3787		selectedSize = $selectedSizeInDropdown.find('[data-size-name]').attr('data-size-name');
3788	} else {
3789		selectedSize = util.getPreferenceValueFromWindowLocation('size');
3790	}
3791	
3792	var payload = {
3793		Channel: options.Channel,
3794		ProductId: options.ProductId,
3795		SelectedLocation: dataBuffer['SelectedLocation'],
3796		SaveSelectedLocation: !!saveSelectedLocation,
3797		SelectedOption: $container.find('input[name="AddToCartOption"]:checked').val()
3798	}
3799	payload['dwvar_' + options.ProductId + '_color'] = selectedColor;
3800	payload['dwvar_' + options.ProductId + '_size'] = selectedSize;
3801
3802	$.ajax({
3803		method: 'GET',
3804		url: Urls.Bopis.AddToCartOptions,
3805		contentType: 'application/json',
3806		data: payload,
3807		dataType: 'html',
3808		success: function(data) {
3809			$container.html($(data).children());
3810			$addToCartOptions = $container.find('.bopis-add-to-cart-options');
3811			initializeTooltips(selectedSize);
3812		},
3813		error: function() {
3814			
3815		}
3816	});
3817}
3818
3819function initializeTooltips(selectedSize) {
3820	if (!selectedSize) {
3821		$addToCartOptions.find('label.disabled > span').each(function(index, element) {
3822			var $this = $(this);
3823
3824			tooltip.init({
3825				items: $this,
3826				tooltipClass: 'tooltip-select-size',
3827				content: 'Select a size to<br />see availability'
3828			}, $this);
3829		});
3830	}
3831
3832	var localDeliveryRadius;
3833	var $storeData = $container.find('.selected-store[store-data]');
3834	if ($storeData && $storeData.length > 0) {
3835		var storeData = JSON.parse($storeData.first().attr('store-data'));
3836		if (storeData && storeData.location && storeData.location.local_delivery_vendor && storeData.location.local_delivery_radius) {
3837			localDeliveryRadius = storeData.location.local_delivery_radius;
3838		}
3839	}
3840
3841	$('.add-to-cart-options-tooltip').find('.delivery-radius').html(localDeliveryRadius);
3842	tooltip.init({
3843		items: '.add-to-cart-options-tooltip',
3844		tooltipClass: 'add-to-cart-options-tooltip'
3845	}, $('.add-to-cart-options-tooltip'));
3846}
3847},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../components/LoadingIndicator":25,"../../dialog":36,"../../tooltip":101,"../../util":102,"./AddToCartOptionsStoreSearch":8,"./LocalDeliveryDialog":9,"lodash":133}],8:[function(require,module,exports){
3848'use strict';
3849
3850var _ = require('lodash');
3851var loadingIndicator = require('../../components/LoadingIndicator');
3852var GeolocationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper');
3853var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
3854var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
3855var tooltip = require('../../tooltip');
3856var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
3857var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
3858var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
3859var ZipCodeToStateMap = require('../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json');
3860
3861module.exports = AddToCartOptionsStoreSearch;
3862
3863function AddToCartOptionsStoreSearch() {
3864	this.options;
3865	this.validator;
3866	this.loaded;
3867	this.dataBuffer;
3868
3869	this.resultContainerTemplate;
3870	this.resultItemTemplate;
3871	this.localDeliveryDecorator;
3872
3873	this.$container;
3874	this.$form;
3875	this.$result;
3876	this.$formInputPostalCode;
3877	this.$formInputRadius;
3878	this.$storeSearchButton;
3879	this.$storeSearchButtonContainer;
3880	this.$bopisButton;
3881	this.$errorMessage;
3882	this.$inputTooltip;
3883
3884	this.initialize = function($containerParameter, optionsParameter) {
3885		var self = this;
3886		if (!this.dataBuffer) {
3887			this.dataBuffer = {};
3888		}
3889		if (!this.loaded) {
3890			this.loaded = {};
3891		}
3892		this.options = $.extend({
3893			successCallback: null,
3894			errorCallback: null
3895		}, optionsParameter);
3896
3897		this.resultContainerTemplate = _.template($('#template-bopis-result-container-addtocartoptions').html(), TemplateConstants.Lodash.Settings);
3898		this.resultItemTemplate = _.template($('#template-bopis-result-item-addtocartoptions').html(), TemplateConstants.Lodash.Settings);
3899		this.localDeliveryDecorator = _.template($('#template-bopis-localdelivery-decorator').html(), TemplateConstants.Lodash.Settings);
3900
3901		this.$container = $containerParameter;
3902		this.$form = this.$container.find('form');
3903		this.$result = this.$container.find('.result');
3904		this.$formInputPostalCode = this.$form.find('input[name$="_postalCode"]');
3905		this.$formInputRadius = this.$form.find('[name$="_radius"]');
3906		this.$storeSearchButton = this.$container.find('.btn.store-search');
3907		this.$storeSearchButtonContainer = this.$container.find('.btn-store-search-container');
3908		this.$bopisButton = this.$container.find('.btn.bopis');
3909		this.$errorMessage = this.$form.find('span.error');
3910		this.$inputTooltip = this.$formInputPostalCode.parent();
3911		this.$container.find('.btn.store-select').addClass('disabled');
3912		$('html').addClass('scroll-lock');
3913
3914		if (this.options && this.options.LocalDelivery && this.options.LocalDelivery.Radius) {
3915			this.$container.find('.delivery-radius').html(this.options.LocalDelivery.Radius);
3916		}
3917		this.$container.attr('state', 'form');
3918		this.validator = this.$form.validate(ValidationSettings.Global);
3919		$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
3920		this.$formInputPostalCode.rules('add', ValidationRules.ZipCode);
3921
3922		this.$container.addClass('show');
3923		if (this.$container.attr('state') === 'form' && this.validator.checkForm() && !this.$storeSearchButton.hasClass('ready')) {
3924			this.$storeSearchButton.addClass('ready');
3925		}
3926
3927		this.$form.off('click.apply').on('click.apply', '.btn.store-search:not([loading])', function (event) {
3928			event.preventDefault();
3929			self.$storeSearchButtonContainer.loading();
3930			self.search();
3931		});
3932
3933		this.$container.off('click.apply').on('click.apply', '.btn.bopis:not([loading])', function (event) {
3934			event.preventDefault();
3935
3936			var selectedLocation = self.dataBuffer['StoresDisplayed'][$('input[name="Bopis_StoreSearch_StoreId"]:checked').val()];
3937			if (selectedLocation) {
3938				$(document).trigger('ProductDetailPage.Bopis.AddToCart', { bopis: true, shippingOption: 'pickup', channel: self.options.Channel });
3939			} else {
3940				$(document).trigger('ProductDetailPage.Bopis.AddToCart', { channel: self.options.Channel });
3941			}
3942		});
3943
3944		this.$formInputPostalCode.off('keydown.BopisStoreSearch.ValidationCheck').on('keydown.BopisStoreSearch.ValidationCheck', function (event) {
3945			if (self.$formInputPostalCode.val().length == 5 && parseInt(event.key, 10) == event.key && !window.getSelection().toString()) {
3946				event.preventDefault();
3947			}
3948		});
3949
3950		this.$formInputPostalCode.off('change.BopisStoreSearch.ValidationCheck').on('change.BopisStoreSearch.ValidationCheck', function (event) {
3951			if (self.$formInputPostalCode.val().length > 5) {
3952				self.$formInputPostalCode.val(self.$formInputPostalCode.val().substr(0, 5));
3953			}
3954		});
3955
3956		this.$formInputPostalCode.off('keyup.BopisStoreSearch.ValidationCheck, change.BopisStoreSearch.ValidationCheck').on('keyup.BopisStoreSearch.ValidationCheck, change.BopisStoreSearch.ValidationCheck', function (event) {
3957			event.stopImmediatePropagation();
3958
3959			if (self.$container.attr('state') === 'form') {
3960				if (self.validator.checkForm() && !self.$storeSearchButton.hasClass('ready')) {
3961					self.$storeSearchButton.addClass('ready');
3962				} else if (!self.validator.checkForm() && self.$storeSearchButton.hasClass('ready')) {
3963					self.$storeSearchButton.removeClass('ready');
3964				}
3965			}
3966
3967			if (self.$container.attr('state') === 'result') {
3968				self.$container.attr('state', 'form');
3969				self.$formInputPostalCode.val('');
3970			}
3971		});
3972
3973		$(document).off('mousedown.BopisStoreSearch.ValidationClear').on('mousedown.BopisStoreSearch.ValidationClear', function (event) {
3974			if (self.validator && $(event.target).closest(self.$container).length == 0) {
3975				self.validator.resetForm();
3976				self.$errorMessage.html('').hide();
3977			}
3978		});
3979
3980		$(document).off('Bopis.StoreSearch.Reset').on('Bopis.StoreSearch.Reset', function (event) {
3981			self.reset();
3982		});
3983
3984		this.$container.off('click.BopisStoreSearch.Selection').on('click.BopisStoreSearch.Selection', 'input[name="Bopis_StoreSearch_StoreId"]', function (event) {
3985			var bopisStore = $('input[name="Bopis_StoreSearch_StoreId"]:checked').val();
3986
3987			if (!self.$bopisButton.hasClass('ready')) {
3988				self.$bopisButton.addClass('ready');
3989			}
3990
3991			$('.product-add-to-cart button').attr('disabled', 'disabled').addClass('hidden');
3992
3993			if (bopisStore) {
3994				self.$bopisButton.attr('state', 'pickup');
3995			} else {
3996				self.$bopisButton.attr('state', 'ship');
3997			}
3998		});
3999
4000		this.$container.off('click.BopisStoreSearch.NoResultsShip').on('click.BopisStoreSearch.NoResultsShip', '.btn.no-results-ship', function (event) {
4001			$('html').removeClass('scroll-lock');
4002			self.close();
4003			$(document).trigger('ProductDetailPage.Bopis.AddToCart', { channel: self.options.Channel });
4004		});
4005
4006		this.$container.off('click.BopisAddToCartOptionsStoreSearch.Select').on('click.BopisAddToCartOptionsStoreSearch.Select', 'li[store]', function (event) {
4007			event.preventDefault();
4008			
4009			var $this = $(this);
4010			$this.find('input').prop('checked', true);
4011			self.$container.find('.btn.store-select').removeClass('disabled');
4012		});
4013
4014		this.$container.off('click.BopisAddToCartOptionsStoreSearch.Confirm').on('click.BopisAddToCartOptionsStoreSearch.Confirm', '.btn.store-select:not(.disabled)', function (event) {
4015			var store = $(this).closest('input[store]').attr('store');
4016			var store = self.$container.find('input[name="bopis-store"]:checked').val();
4017			var eventName = 'Bopis.StoreSearch.Select';
4018
4019			if (self.options.Events && self.options.Events.SelectStore) {
4020				eventName = self.options.Events.SelectStore;
4021				store = self.dataBuffer['StoresDisplayed'][store];
4022			}
4023			$('html').removeClass('scroll-lock');
4024			$(document).trigger(eventName, store);
4025			self.close();
4026		});
4027
4028		this.$container.off('click.BopisAddToCartOptionsStoreSearch.Close').on('click.BopisAddToCartOptionsStoreSearch.Close', '.close', function (event) {
4029			$('html').removeClass('scroll-lock');
4030			self.close();
4031		});
4032
4033		this.$container.off('click.UseMyLocation').on('click.UseMyLocation', '.use-my-location', function (event) {
4034			event.preventDefault();
4035			
4036			GeolocationHelper.GetPosition().then(function(position) {
4037				self.search(true, position);
4038			}).catch();
4039		});
4040
4041		this.reset();
4042		if (this.options.StoreUIVersion == '2024Redesign' && this.$formInputPostalCode.val()) {
4043			this.search();
4044		}
4045	}
4046
4047	this.reset = function() {
4048		this.$container.attr('result-count', '0');
4049		this.$result.html('');
4050	}
4051
4052	this.close = function() {
4053		$('html').removeClass('scroll-lock');
4054		if (this.options.Functions && this.options.Functions.Close) {
4055			this.options.Functions.Close();
4056		} else {
4057			$('.bopis-add-to-cart-store-search-container').removeClass('show');
4058		}
4059	}
4060
4061	this.search = function(isByGeolocation, position) {
4062		var self = this;
4063		var colorSelected = $('div.product-variations .color.color-options li.selected').length;
4064		var sizeSelected = $('div.product-variations .size.size-options li.selected');
4065		var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
4066		var widthSelected = $('div.product-variations .width.width-options li.selected').length;
4067		var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
4068
4069		if (this.options.StoreUIVersion != '2024Redesign') {
4070			if (!isSizeSelected && !colorSelected) {
4071				this.$errorMessage.html('Please select a size and color.').show();
4072				return false;
4073			} else if (!isSizeSelected) {
4074				this.$errorMessage.html('Please select a size.').show();
4075				return false;
4076			} else if (!colorSelected) {
4077				this.$errorMessage.html('Please select a color.').show();
4078				return false;
4079			}
4080		}
4081		this.$errorMessage.html('').hide();
4082		this.$storeSearchButton.removeAttr('disabled');
4083
4084		var geolocationPosition;
4085		if (isByGeolocation && position && position.coords) {
4086			geolocationPosition = {
4087				coords: {
4088					accuracy: position.coords.accuracy,
4089					latitude: position.coords.latitude,
4090					longitude: position.coords.longitude
4091				},
4092				timestamp: position.timestamp
4093			}
4094		}
4095
4096		var isValid = isByGeolocation || this.validator.valid();
4097		if (Urls && Urls.Bopis.GetStoreInventoryForProductForPostalCode && isValid) {
4098			this.$result.html('');
4099			this.$result.loading();
4100
4101			this.loaded['LocationData'] = false;
4102			this.loaded['InventoryData'] = false;
4103			this.dataBuffer['LocationData'] = null;
4104			this.dataBuffer['InventoryData'] = null;
4105
4106			var getLocationUrl;
4107			var getLocationData;
4108			if (isByGeolocation) {
4109				getLocationUrl = Urls.Bopis.GetLocationsForGeolocationPosition;
4110				getLocationData = {
4111					Channel: this.options.Channel,
4112					Source: this.options.Source,
4113					GeolocationPosition: geolocationPosition,
4114					Radius: parseFloat(this.$formInputRadius.val()) || 3000
4115				}
4116			} else {
4117				getLocationUrl = Urls.Bopis.GetLocationsForPostalCode;
4118				getLocationData = {
4119					Channel: this.options.Channel,
4120					Source: this.options.Source,
4121					PostalCode: this.$formInputPostalCode.val(),
4122					Radius: parseFloat(this.$formInputRadius.val()) || 3000
4123				};
4124			}
4125
4126			$.ajax({
4127				method: 'POST',
4128				url: getLocationUrl,
4129				contentType: 'application/json',
4130				data: JSON.stringify(getLocationData),
4131				dataType: 'json',
4132				success: function(data) {
4133					var locationData;
4134					if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data && data.InquiryResult.data.length > 0) {
4135						locationData = data.InquiryResult.data;
4136
4137						if (locationData && locationData.length > 0) {
4138							locationData = locationData.sort(function(left, right) {
4139								return left.distance - right.distance;
4140							});
4141
4142							self.dataBuffer['LocationData'] = locationData;
4143						}
4144					}
4145
4146					self.loaded['LocationData'] = true;
4147					self.render();
4148				},
4149				error: function() {
4150					if (self.options.errorCallback) {
4151						self.options.errorCallback();
4152					}
4153				}
4154			});
4155
4156			var productId = $('.product-add-to-cart input[name="pid"]').val();
4157			var getStoreInventoryUrl;
4158			var getStoreInventoryData;
4159			if (isByGeolocation) {
4160				getStoreInventoryUrl = Urls.Bopis.GetStoreInventoryForProductForGeolocationPosition;
4161				getStoreInventoryData = {
4162					Channel: this.options.Channel,
4163					Source: this.options.Source,
4164					ProductId: productId,
4165					GeolocationPosition: geolocationPosition,
4166					StoreSearchRadius: parseFloat(this.$formInputRadius.val()) || 3000,
4167				}
4168			} else {
4169				getStoreInventoryUrl = Urls.Bopis.GetStoreInventoryForProductForPostalCode;
4170				getStoreInventoryData = {
4171					Channel: this.options.Channel,
4172					Source: this.options.Source,
4173					ProductId: productId,
4174					PostalCode: this.$formInputPostalCode.val(),
4175					StoreSearchRadius: parseFloat(this.$formInputRadius.val()) || 3000,
4176					SavePostalCode: true
4177				};
4178			}
4179
4180			$.ajax({
4181				method: 'POST',
4182				url: getStoreInventoryUrl,
4183				contentType: 'application/json',
4184				data: JSON.stringify(getStoreInventoryData),
4185				dataType: 'json',
4186				success: function(data) {
4187					if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data && data.InquiryResult.data.length > 0 && data.InquiryResult.data[0].inventory_supplies) {
4188						var inquiryResult = data.InquiryResult.data[0].inventory_supplies;
4189						
4190						if (inquiryResult && inquiryResult.length > 0) {
4191							self.dataBuffer['InventoryData'] = [];
4192							
4193							for (var inventoryRecordElement in inquiryResult) {
4194								var inventoryRecord = inquiryResult[inventoryRecordElement];
4195								self.dataBuffer['InventoryData'][inventoryRecord.location.code] = inventoryRecord;
4196							}
4197						}
4198					}
4199
4200					self.loaded['InventoryData'] = true;
4201					self.render();
4202				},
4203				error: function() {
4204					if (self.$result.attr('loading')) {
4205						self.$result.loading('destroy');
4206					}
4207
4208					if (self.options.errorCallback) {
4209						self.options.errorCallback();
4210					}
4211				}
4212			});
4213		}
4214	}
4215
4216	this.render = function() {
4217		if (this.loaded['LocationData'] && this.loaded['InventoryData']) {
4218			if (this.$storeSearchButtonContainer.attr('loading')) {
4219				this.$storeSearchButtonContainer.loading('destroy');
4220			}
4221			if (this.$result.attr('loading')) {
4222				this.$result.loading('destroy');
4223			}
4224
4225			this.$container.attr('state', 'result');
4226			if (this.dataBuffer['LocationData'] && this.dataBuffer['LocationData'].length > 0) {
4227				this.dataBuffer['StoresDisplayed'] = [];
4228
4229				var $addToCartOptions = $('.bopis-add-to-cart-options');
4230				var quantitySourceType;
4231				var dropshipLeadDays;
4232				if ($addToCartOptions.length > 0) {
4233					quantitySourceType = $addToCartOptions.attr('quantity-source-type');
4234					dropshipLeadDays = parseInt($addToCartOptions.attr('dropship-lead-days'), 10);
4235				}
4236				var quantitySourceTypeOffset = 0;
4237				if (quantitySourceType == 'WHS') {
4238					quantitySourceTypeOffset = -1;
4239				}
4240
4241				var storeListHtml = '';
4242				var isStoreSelected = false;
4243				var status = '';
4244				var statusDisplay = '';
4245				var isLocalDeliverySupported = false;
4246				var localDeliveryHtml = '';
4247				var storesDisplayedCount = 0;
4248				var selectedStoreAttribute;
4249				var quantityAvailable = false;
4250				for (var locationKey in this.dataBuffer['LocationData']) {
4251					var location = this.dataBuffer['LocationData'][locationKey];
4252
4253					var inventoryRecord;
4254					if (this.dataBuffer['InventoryData'] && this.dataBuffer['InventoryData'][location.code]) {
4255						inventoryRecord = this.dataBuffer['InventoryData'][location.code];
4256					}
4257
4258					status = 'unavailable';
4259					statusDisplay = 'Unavailable';
4260					isLocalDeliverySupported = false;
4261					localDeliveryHtml = '';
4262					if (inventoryRecord && inventoryRecord.detail && inventoryRecord.detail.quantity > 0 && !this.dataBuffer['StoresDisplayed'][inventoryRecord.location.code] && (!this.options.LocalDeliveryOnly || (inventoryRecord.location.local_delivery_enabled && this.isLocationInLocalDeliveryRadius(inventoryRecord.location)))) {
4263						if (this.options && this.options.SelectedLocations == inventoryRecord.location.code) {
4264							isStoreSelected = true;
4265							selectedStoreAttribute ='checked';
4266						} else {
4267							selectedStoreAttribute = '';
4268						}
4269						
4270						if (this.options.LocalDelivery.Enabled && this.isLocationInLocalDeliveryRadius(inventoryRecord.location) && inventoryRecord.location.local_delivery_enabled) {
4271							localDeliveryHtml = this.localDeliveryDecorator();
4272							isLocalDeliverySupported = true;
4273						}
4274
4275						if (isLocalDeliverySupported && inventoryRecord.detail && inventoryRecord.detail.quantity > 0) {
4276							if (this.options.LocalDeliveryOnly) {
4277								statusDisplay = 'Same-Day Delivery<br/>Available';
4278								status = 'available';
4279							} else if (!this.options.LocalDeliveryOnly) {
4280								// statusDisplay = 'Pickup & Same-Day<br/>Delivery Available';
4281								statusDisplay = 'Available Today';
4282								status = 'available';
4283							}
4284						} else if (!this.options.LocalDeliveryOnly && inventoryRecord.detail && inventoryRecord.detail.quantity > 0) {
4285							statusDisplay = 'Available Today';
4286							status = 'available';
4287						}
4288
4289						quantityAvailable = inventoryRecord.detail.quantity;
4290					} else if (!this.options.LocalDeliveryOnly && !this.options.StorePickupOnly && pageContext.site != SiteConstants.SiteIds.BootBarnRspUS) {
4291						statusDisplay = 'Arrives in ' + (this.options.ShippingMethods.ShipToStore.DayRangeStart + dropshipLeadDays + quantitySourceTypeOffset) + '-' + (this.options.ShippingMethods.ShipToStore.DayRangeEnd + dropshipLeadDays + quantitySourceTypeOffset) + '<br/>Business Days';
4292						status = 'available';
4293					} else {
4294						quantityAvailable = false
4295					}
4296
4297					var locationName;
4298					if (this.options && this.options.StoreUIVersion == '2024Redesign') {
4299						locationName = '<strong>' + location.StoreInfo.Name + ' - ' + location.StoreInfo.StoreId + '</strong>';
4300					} else {
4301						locationName = '<strong>' + location.StoreInfo.Name + '</strong>';
4302					}
4303					if (this.options && this.options.AgentStoreID == location.code) {
4304						locationName += '<br/>(Your Store)';
4305					}
4306
4307					storeListHtml += this.resultItemTemplate({
4308						StoreId: location.code,
4309						QuantityAvailable: quantityAvailable,
4310						Html: '<span class="store-name">' + locationName + '</span><br/>' + location.StoreInfo.Address1 + '<br/>' + location.StoreInfo.City + ', ' + location.StoreInfo.StateCodeParsed + ', ' + location.StoreInfo.PostalCode.substr(0, 5),
4311						Selected: selectedStoreAttribute,
4312						LocalDelivery: localDeliveryHtml,
4313						Status: status,
4314						CarriesFR: 'carriesFR' in location.StoreInfo.Custom && location.StoreInfo.Custom.carriesFR,
4315						StatusDisplay: statusDisplay
4316					});
4317
4318					storesDisplayedCount += 1;
4319					this.dataBuffer['StoresDisplayed'][location.code] = inventoryRecord ? inventoryRecord : { location: location };
4320				}
4321
4322				var resultHtml = this.resultContainerTemplate({
4323					Html: storeListHtml
4324				});
4325
4326				if (isStoreSelected) {
4327					this.$bopisButton.addClass('ready');
4328				}
4329
4330				if (storesDisplayedCount > 0) {
4331					this.$result.html(resultHtml);
4332					this.$container.attr('result-count', storesDisplayedCount);
4333					this.$container.find('.result-container').show();
4334					this.$container.find('.result-empty').hide();
4335				} else {
4336					this.$container.attr('result-count', 0);
4337					this.$container.find('.result-container').hide();
4338					this.$container.find('.result-empty').show();
4339				}
4340
4341				tooltip.init({
4342					items: '.tooltip'
4343				}, this.$result);
4344
4345				if (this.options.successCallback) {
4346					this.options.successCallback(inquiryResult);
4347				}
4348			} else {
4349				$('.product-add-to-cart button').removeAttr('disabled').removeClass('hidden');
4350				this.reset();						
4351				this.$result.closest('.result-container').hide();
4352				this.$container.find('.result-empty').show();
4353
4354				if (this.options.errorCallback) {
4355					this.options.errorCallback();
4356				}
4357			}
4358		}
4359	}
4360
4361	this.isLocationInLocalDeliveryRadius = function(location) {
4362		var localDeliveryRadius;
4363		if (this.options && this.options.LocalDelivery) {
4364			if (location.local_delivery_vendor && location.local_delivery_vendor == 'GoLocal') {
4365				localDeliveryRadius = this.options.LocalDelivery.RadiusForGoLocal;
4366			} else {
4367				localDeliveryRadius = this.options.LocalDelivery.RadiusForRoadie;
4368			}
4369		}	
4370
4371		if (localDeliveryRadius && location && location.distance && localDeliveryRadius >= location.distance) {
4372			return true;
4373		}
4374
4375		return false;
4376	}
4377}
4378},{"../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json":111,"../../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper":115,"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"../../tooltip":101,"lodash":133}],9:[function(require,module,exports){
4379'use strict';
4380
4381var _ = require('lodash');
4382var addToCartOptionsStoreSearch = require('./AddToCartOptionsStoreSearch');
4383var dialog = require('../../dialog');
4384var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
4385var loadingIndicator = require('../../components/LoadingIndicator');
4386var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
4387var tooltip = require('../../tooltip');
4388var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
4389var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
4390var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
4391var ZipCodeToStateMap = require('../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json');
4392
4393exports.initialize = initialize;
4394exports.remove = remove;
4395
4396var options;
4397var optionsLocalDelivery;
4398var validator;
4399var dataBuffer;
4400var localDeliveryStoreSearch;
4401var localDeliveryRadius;
4402
4403function initialize(optionsParameter, selectedLocation, localDeliveryFormData) {
4404	dataBuffer = {};
4405	options = $.extend({
4406		successCallback: null,
4407		errorCallback: null
4408	}, optionsParameter);
4409
4410	if (selectedLocation && selectedLocation.location && selectedLocation.location.local_delivery_vendor && selectedLocation.location.local_delivery_vendor == 'GoLocal') {
4411		localDeliveryRadius = options.LocalDelivery.RadiusForGoLocal;
4412	} else if (selectedLocation && selectedLocation.location && selectedLocation.location.local_delivery_vendor && selectedLocation.location.local_delivery_vendor == 'Roadie') {
4413		localDeliveryRadius = options.LocalDelivery.RadiusForRoadie;
4414	}
4415
4416	buildAddressHtmlForLocation(selectedLocation.location);
4417	$('#dialog-bopis-options').find('.delivery-radius').html(localDeliveryRadius);
4418	var $optionalMessage = $('#dialog-bopis-options header .optional-message');
4419	
4420	if (options.OptionalMessage) {
4421		if ($optionalMessage.length == 0) {
4422			$('#dialog-bopis-options header').append('<p class="optional-message">' + options.OptionalMessage + '</p>')
4423		}
4424	} else if ($optionalMessage.length > 0) {
4425		$optionalMessage.remove();
4426	}
4427	
4428	localDeliveryStoreSearch = new addToCartOptionsStoreSearch();
4429	optionsLocalDelivery = Object.assign(options, {
4430		LocalDeliveryOnly: true,
4431		Events: {
4432			SelectStore: 'Bopis.StoreSearchSelectLocalDelivery'
4433		},
4434		Functions: {
4435			Close: function() {
4436				$('.dialog-bopis-options').removeClass('hidden');
4437				$('.ui-widget-overlay').removeClass('hidden');
4438				$('.bopis-local-delivery-store-search-container').removeClass('show');
4439			}
4440		}
4441	});
4442
4443	GoogleTagManagerClient.PushDataLayerForEvent('roadieFormDisplay');
4444	dialog.open({
4445		html: $('#dialog-bopis-options').html(),
4446		options: {
4447			dialogClass: 'dialog-bopis-options',
4448			width: 320,
4449			draggable: false,
4450			resizable: false,
4451			open: function(event, ui) {
4452				var $this = $(this);
4453				var $addressForm = $this.find('form.bopis-delivery-address');
4454				appendCSRFToken($addressForm);
4455				
4456				if (localDeliveryFormData) {
4457					$addressForm.find('input[name="email"]').val(localDeliveryFormData.Email || '');
4458					$addressForm.find('input[name="phone"]').val(localDeliveryFormData.Phone || '');
4459					$addressForm.find('input[name="firstName"]').val(localDeliveryFormData.FirstName || '');
4460					$addressForm.find('input[name="lastName"]').val(localDeliveryFormData.LastName || '');
4461					$addressForm.find('input[name="address1"]').val(localDeliveryFormData.Address1 || '');
4462					$addressForm.find('input[name="address2"]').val(localDeliveryFormData.Address2 || '');
4463					$addressForm.find('input[name="address3"]').val(localDeliveryFormData.Address3 || '');
4464					$addressForm.find('input[name="city"]').val(localDeliveryFormData.City || '');
4465					$addressForm.find('select[name="stateCode"] option[value="' + localDeliveryFormData.State + '"]').prop('selected', true);
4466					$addressForm.find('input[name="postalCode"]').val(localDeliveryFormData.PostalCode || '');
4467					$addressForm.find('textarea[name="deliveryNote"]').val(localDeliveryFormData.DeliveryNote || '');
4468				}
4469
4470				var addressValidator = $addressForm.validate(ValidationSettings.Global);
4471				$.validator.addMethod('requiredExcludingPunctuation', ValidationHelper.ValidateRequiredExcludingPunctuation, Resources.INVALID_VALUE);
4472				$.validator.addMethod('postOfficeBox', ValidationHelper.ValidateAddressForPostOfficeBox, Resources.INVALID_ADDRESS_POBOX);
4473				$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
4474				$.validator.addMethod('stateCodeForCaliforniaStateLine', ValidationHelper.ValidateStateCodeForCaliforniaStateLine, Resources.INVALID_CALIFORNIA_STATE_LINE_LOCAL_DELIVERY);
4475				
4476				$addressForm.find('input[name="address1"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBox);
4477				$addressForm.find('input[name="address2"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBox);
4478				$addressForm.find('input[name="address3"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBox);
4479				$addressForm.find('select[name="stateCode"]').rules('add', ValidationRules.StateCodeForCaliforniaStateLine);
4480			    $addressForm.find('input[name="postalCode"]').rules('add', ValidationRules.ZipCode);
4481				$addressForm.find('input.required, select.required').rules('add', ValidationRules.RequiredExcludingPunctuation);
4482				$addressForm.find('input[name="email"]').rules('add', ValidationRules.Email);
4483				$this.attr('local-delivery-vendor', $('#dialog-bopis-options').attr('local-delivery-vendor'));
4484				$this.attr('local-delivery-radius', isLocalDeliveryValid(selectedLocation));
4485				$this.attr('store-state', selectedLocation.location.postal_address.province_or_state);
4486				
4487				$this.off('click.SelectDeliveryOption').on('click.SelectDeliveryOption', '.delivery-option label', function (event) {
4488					var deliveryOption = $(this).find('input').val();
4489
4490					if (deliveryOption == 'pickup') {
4491						$this.find('.address-form').hide();
4492					} else if (deliveryOption == 'delivery') {
4493						$this.find('.address-form').show();
4494					}
4495				});
4496
4497				$this.off('click.Apply').on('click.Apply', '.btn.bopis-option:not([loading])', function (event) {
4498					event.preventDefault();
4499
4500					$this.find('.ui-dialog-footer .error').remove();
4501
4502					var selectedBopisOption;
4503					if (options && options.Mode == 'AddToCartOptions') {
4504						selectedBopisOption = 'delivery';
4505					} else {
4506						selectedBopisOption = $this.find('input[name="bopis-option"]:checked').val();
4507					}
4508					var eventData = {bopis: true, bopisStore: selectedLocation.location.code, shippingOption: selectedBopisOption, channel: options.Channel, quantityAvailable: selectedLocation.detail.quantity};
4509					if (selectedBopisOption == 'delivery') {
4510						var deliveryAddressObject = {};
4511						var $deliveryAddressForm = $this.find('.bopis-delivery-address');
4512						var deliveryAddressArray = $deliveryAddressForm.serializeArray();
4513						$(deliveryAddressArray).each(function(index, element) {
4514							if (element.value != 'disabled' && $deliveryAddressForm.find('[name="' + element.name + '"]').attr('type') != 'hidden') {
4515								deliveryAddressObject[element.name] = element.value;
4516							}
4517						});
4518						eventData.deliveryAddress = JSON.stringify(deliveryAddressObject);
4519						$deliveryAddressForm.find('input[type="hidden"]').each(function () {
4520							var $this = $(this);
4521							eventData[$this.attr('name')] = $this.val();
4522						});
4523					}
4524
4525					if (selectedBopisOption != 'delivery' || $addressForm.valid()) {
4526						if (selectedBopisOption == 'delivery') {
4527							$.ajax({
4528					            method: 'POST',
4529					            url: Urls.Bopis.ValidatePostalCodeForLocations,
4530					            contentType: 'application/json',
4531					            data: JSON.stringify({
4532					                PostalCode: $addressForm.find('input[name="postalCode"]').val(),
4533					                Locations: [selectedLocation.location.code]
4534					            }),
4535					            dataType: 'json',
4536					            success: function(data) {
4537					            	if (data) {
4538										GoogleTagManagerClient.PushDataLayerForEvent('roadieFormSubmit');
4539					            		$(document).trigger('ProductDetailPage.Bopis.AddToCart', eventData);
4540					            	} else {
4541					            		var errorMessage = '<div class="error">The zip code you entered is different than the one you searched with, and the selected location is not in range. Please update your address and try again.</div>'
4542
4543					            		$this.find('.ui-dialog-footer').prepend(errorMessage);
4544					            	}
4545					            }
4546							});
4547						} else {
4548							$(document).trigger('ProductDetailPage.Bopis.AddToCart', eventData);
4549						}
4550					}
4551				});
4552
4553				$this.off('click.ChangeZip').on('click.ChangeZip', '.btn.change-zip', function (event) {
4554					$(document).trigger('Bopis.StoreSearch.Reset');
4555					dialog.close();
4556				});
4557
4558				$this.off('click.ChangeStore').on('click.ChangeStore', '.btn.change-store', function (event) {
4559					dialog.close();
4560				});
4561
4562				$this.off('click.SearchStore').on('click.SearchStore', '.search-store', function (event) {
4563					event.preventDefault();
4564					event.stopImmediatePropagation();
4565
4566					$('.dialog-bopis-options').addClass('hidden');
4567					$('.ui-widget-overlay').addClass('hidden');
4568					
4569					localDeliveryStoreSearch.initialize($('.bopis-local-delivery-store-search-container'), optionsLocalDelivery);
4570				});
4571            },
4572            close: function(event, ui) {
4573
4574            }
4575		}
4576	});
4577
4578	$(document).off('Bopis.StoreSearchSelectLocalDelivery').on('Bopis.StoreSearchSelectLocalDelivery', function (event, store) {
4579		buildAddressHtmlForLocation(store.location);
4580		$('.dialog-bopis-options').removeClass('hidden');
4581		$('.ui-widget-overlay').removeClass('hidden');
4582		$('.bopis-local-delivery-store-search-container').removeClass('show');
4583		$('.dialog-bopis-options').find('.dialog-content').attr('local-delivery-radius', isLocalDeliveryValid(store));
4584		$('.dialog-bopis-options').find('.dialog-content').attr('store-state', store.location.postal_address.province_or_state);
4585	});
4586}
4587
4588function remove() {
4589	validator.destroy();
4590	$('.product-add-to-cart button').removeAttr('disabled').removeClass('hidden');
4591}
4592
4593function buildAddressHtmlForLocation(location) {
4594	var locationHtml;
4595	if (options && options.Mode == 'AddToCartOptions') {
4596		locationHtml = '<p class="selected-location-name">' + location.postal_address.name + '</p>' + 
4597								'<p class="selected-location-address">' + location.postal_address.address_line1 + '<br/>' +
4598								location.postal_address.city + ', ' + location.postal_address.province_or_state + '</p>' +
4599								'<a class="search-store">Change Store</a>';
4600	} else {
4601		locationHtml = location.postal_address.address_line1 + '<br/>' +
4602		location.postal_address.city + ', ' + location.postal_address.province_or_state;
4603	}
4604
4605	$('.ui-dialog.dialog-bopis-options').find('.selected-location-detail
4605s').html(locationHtml);
4606	$('#dialog-bopis-options').find('.selected-location-details').html(locationHtml);
4607}
4608
4609function isLocalDeliveryValid(record) {
4610	if (options.LocalDelivery && options.LocalDelivery.Enabled && isLocationInLocalDeliveryRadius(record.location) && record.location.local_delivery_enabled && record.detail && record.detail.quantity > 0) {
4611		return true;
4612	}
4613
4614	return false;
4615}
4616
4617function isLocationInLocalDeliveryRadius(location) {
4618	if (localDeliveryRadius && location.distance && localDeliveryRadius >= location.distance) {
4619		return true;
4620	}
4621
4622	return false;
4623}
4624
4625function appendCSRFToken($form) {
4626	$.ajax({
4627		method: 'get',
4628		url: Urls.CSRFToken.Get,
4629		dataType: 'json',
4630		success: function(response) {
4631			$form.append('<input type="hidden" name="' + response.name + '" value="' + response.token + '"/>');
4632		}
4633	});
4634}
4635},{"../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json":111,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"../../dialog":36,"../../tooltip":101,"./AddToCartOptionsStoreSearch":8,"lodash":133}],10:[function(require,module,exports){
4636'use strict';
4637
4638var _ = require('lodash');
4639var loadingIndicator = require('../../components/LoadingIndicator');
4640var GeolocationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper');
4641var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
4642var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
4643var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
4644var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
4645var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
4646
4647var siteName;
4648
4649module.exports = LocationSearch;
4650
4651function LocationSearch() {
4652	this.$container;
4653	this.$form;
4654	this.dataBuffer;
4655	this.options;
4656	this.validator;
4657	
4658	this.resultContainerTemplate = _.template($('#template-bopis-result-container').html(), TemplateConstants.Lodash.Settings);
4659	this.resultItemTemplate = _.template($('#template-bopis-result-item').html(), TemplateConstants.Lodash.Settings);
4660
4661	this.$result;
4662	this.$formInputPostalCode;
4663	this.$formInputRadius;
4664	this.$storeSearchButton;
4665	this.$storeSelectButton;
4666	this.$bopisButton;
4667	this.$errorMessage;
4668	
4669	this.initialize = function ($container, options) {
4670		if (pageContext) {
4671			siteName = pageContext.siteId ? pageContext.siteId : pageContext.site ? pageContext.site : null;
4672		}
4673
4674		var self = this;
4675		this.$container = $container;
4676		this.$form = $container.find('form');
4677		this.options = $.extend({
4678			successCallback: null,
4679			errorCallback: null
4680		}, options);
4681		this.dataBuffer = {
4682			limit: this.options && this.options.Bopis && this.options.Bopis.StoreSearchLimit ? this.options.Bopis.StoreSearchLimit : 5
4683		};
4684		this.$result = this.$container.find('.result');
4685		this.$formInputPostalCode = this.$form.find('input[name$="_postalCode"]');
4686		this.$formInputRadius = this.$form.find('[name$="_radius"]');
4687		this.$storeSearchButton = this.$container.find('.btn.store-search');
4688		this.$storeSelectButton = this.$container.find('.btn.store-select');
4689		this.$bopisButton = this.$container.find('.btn.bopis');
4690	    this.$errorMessage = this.$form.find('span.error');
4691		$('html').addClass('scroll-lock');
4692
4693		if (this.options.DefaultZipCode) {
4694			this.$formInputPostalCode.val(this.options.DefaultZipCode);
4695		}
4696
4697	    this.$container.attr('state', 'form');
4698	    this.validator = this.$form.validate(ValidationSettings.Global);
4699		$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
4700	    this.$formInputPostalCode.rules('add', ValidationRules.ZipCode);
4701	
4702	    if (this.validator.checkForm() && this.$container.attr('state') === 'form' && !this.$storeSearchButton.hasClass('ready')) {
4703			this.$storeSearchButton.addClass('ready').removeAttr('disabled');
4704	    }
4705		this.$container.show();
4706	    
4707	    this.$form.off('click.Apply').on('click.Apply', '.btn.store-search:not([loading])', '.btn.store-search:not([loading])', function (event) {
4708	    	event.preventDefault();
4709	    	$('.bopis-store-search').attr('state', 'form');
4710	    	self.$container.trigger('LocationSearch.FormSubmit');
4711			if ($('.stores-near-location .location-identifier').length) {
4712				$('.stores-near-location .location-identifier').text(' ' + $('#Bopis_StoreSearch_postalCode_ls').val());
4713			}
4714	    });
4715	    
4716	    this.$container.off('click.DialogClose').on('click.DialogClose', '.close', function (event) {
4717	    	event.preventDefault();
4718			$('html').removeClass('scroll-lock');
4719	    	$('.bopis-store-search').attr('state', 'form');
4720			$('.location-search-dialog').hide();
4721	    });
4722	    
4723	    this.$container.off('LocationSearch.FormSubmit').on('LocationSearch.FormSubmit', function (event) {
4724			event.preventDefault();
4725
4726	        self.$errorMessage.html('').hide();
4727	        if (Urls && Urls.Bopis.GetLocationsForPostalCode && self.validator.valid() && self.$formInputPostalCode.val()) {
4728				self.dataBuffer.loadMore = 'postalCode';
4729				self.dataBuffer.offset = 0;
4730				self.$storeSearchButton.loading();
4731				self.$result.html('');
4732				self.$result.loading();
4733				self.getStoreListForPostalCode(self.$formInputPostalCode.val());
4734			}
4735		});
4736	    
4737	    this.$container.off('click.SelectStore').on('click.SelectStore', '.btn.select-store:not([loading])', function (event) {
4738			$('html').removeClass('scroll-lock');
4739			$(this).loading({
4740				IsLabelShown: false,
4741				CssClass: 'fullscreen',
4742				BackgroundColor: 'transparent'
4743			});
4744	    });
4745
4746		this.$container.off('click.SelectStore').on('click.SelectStore', '.btn.set-my-store:not([loading])', function (event) {
4747			var $this = $(this);
4748			var $storeInput = $this.closest('li').find('input');
4749			var storeId;
4750			$('html').removeClass('scroll-lock');
4751			if ($storeInput && $storeInput.length > 0) {
4752				storeId = $storeInput.val();
4753			}
4754			if (storeId) {
4755				self.updateStoreDetails(storeId);
4756			}
4757	    });
4758	    
4759	    this.$formInputPostalCode.off('keypress.LocationSearch.FormSubmit').on('keypress.LocationSearch.FormSubmit', function (event) {
4760	    	if (event.key == 'Enter') {
4761	    		self.$container.trigger('LocationSearch.FormSubmit');
4762	    		return false;
4763	    	}
4764	    });
4765	    
4766	    this.$formInputPostalCode.off('keydown.LocationSearch.ValidationCheck').on('keydown.LocationSearch.ValidationCheck', function (event) {
4767	    	if (self.$formInputPostalCode.val().length == 5 && parseInt(event.key, 10) == event.key && !window.getSelection().toString()) {
4768	    		event.preventDefault();
4769	    	}
4770	    	
4771	    	$(document).find('.bopis-store-search').attr('state', 'form');
4772	    });
4773	    
4774	    
4775	    this.$formInputPostalCode.off('change.LocationSearch.ValidationCheck').on('change.LocationSearch.ValidationCheck', function (event) {
4776	    	if (self.$formInputPostalCode.val().length > 5) {
4777	    		self.$formInputPostalCode.val(self.$formInputPostalCode.val().substr(0, 5));
4778	    	}
4779	    	
4780	    	$(document).find('.bopis-store-search').attr('state', 'form');
4781	    });
4782	    
4783	    this.$formInputPostalCode.off('keyup.LocationSearch.ValidationCheck, change.LocationSearch.ValidationCheck').on('keyup.LocationSearch.ValidationCheck, change.LocationSearch.ValidationCheck', function (event) {
4784	    	event.stopImmediatePropagation();
4785	    	
4786			if ($container.attr('state') === 'form') {
4787				if (self.validator.checkForm() && !self.$storeSearchButton.hasClass('ready')) {
4788					self.$storeSearchButton.addClass('ready').removeAttr('disabled');
4789				} else if (!self.validator.checkForm() && self.$storeSearchButton.hasClass('ready')) {
4790					self.$storeSearchButton.removeClass('ready').attr('disabled', 'disabled');
4791				}
4792			}
4793	    });
4794	    
4795	    $(document).off('mousedown.LocationSearch.ValidationClear').on('mousedown.LocationSearch.ValidationClear', function (event) {
4796	        if (self.validator && $(event.target).closest(self.$container).length == 0) {
4797	            self.$errorMessage.html('').hide();
4798	        }
4799	    });
4800	 
4801	    this.$container.off('click.LocationSearch.Reset').on('click.LocationSearch.Reset', '.reset', function (event) {
4802	    	self.$formInputPostalCode.val('');
4803	        self.$container.attr('state', 'form');
4804	        self.$storeSearchButton.removeClass('ready').attr('disabled', 'disabled');
4805	        $('.product-add-to-cart button').removeAttr('disabled');
4806	        self.$bopisButton.removeClass('ready');
4807	    });
4808   
4809	    this.$container.off('click.LocationSearch.Selection').on('click.LocationSearch.Selection', 'input[name="Bopis_StoreSearch_StoreId"]', function (event) {
4810	        var bopisStore = $('input[name="Bopis_StoreSearch_StoreId"]:checked').val();
4811	        
4812	        if (!self.$bopisButton.hasClass('ready')) {
4813	        	self.$bopisButton.addClass('ready');
4814	        }
4815
4816			if (self.$storeSelectButton.length > 0) {
4817				self.$storeSelectButton.attr('href', Urls.storeLandingPage + '?Store=' + bopisStore).addClass('ready');; 
4818			}
4819	        
4820	        $('.product-add-to-cart button').attr('disabled', 'disabled');
4821	        
4822	        if (bopisStore) {
4823	        	self.$bopisButton.attr('state', 'pickup');
4824	        } else {
4825	        	self.$bopisButton.attr('state', 'ship');
4826	        }
4827	    });
4828
4829		this.$container.off('click.UseMyLocation').on('click.UseMyLocation', '.use-my-location', function (event) {
4830			event.preventDefault();
4831			self.geolocationSearch();
4832		});
4833
4834		$(document).off('LocationSearch.GeolocationSearch').on('LocationSearch.GeolocationSearch', function (event) {
4835			self.geolocationSearch();
4836		});
4837
4838		this.$container.off('click.LoadMore').on('click.LoadMore', '.load-more', function (event) {
4839			event.preventDefault();
4840
4841			$(this).loading({ IsLabelShown: false });
4842
4843			if (siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.MOBILE_NAV_REDESIGN == 'redesign' && window.innerWidth <= 1024) {
4844				if (!self.dataBuffer.loadMore) {
4845					self.dataBuffer.loadMore = 'postalCode';
4846				}
4847				if (!self.dataBuffer.offset) {
4848					self.dataBuffer.offset = 0;
4849				}
4850			}
4851
4852			self.dataBuffer.offset += self.dataBuffer.limit;
4853			if (self.dataBuffer.loadMore == 'geolocationPosition') {
4854				self.getStoreListForGeolocationPosition(self.dataBuffer.geolocationPosition, true);
4855			} else if (self.dataBuffer.loadMore == 'postalCode') {
4856				self.getStoreListForPostalCode(self.$formInputPostalCode.val(), true);
4857			}
4858		});
4859	}
4860
4861	this.geolocationSearch = function() {
4862		var self = this;
4863		self.dataBuffer.loadMore = 'geolocationPosition';
4864		self.dataBuffer.offset = 0;
4865		this.$result.html('');
4866		this.$result.loading();
4867		GeolocationHelper.GetPosition().then(function(position) {
4868			self.dataBuffer.geolocationPosition = position;
4869			self.getStoreListForGeolocationPosition(self.dataBuffer.geolocationPosition);
4870		}).catch(function() {
4871			if (self.$result.attr('loading')) {
4872				self.$result.loading('destroy');
4873			}
4874		});
4875	}
4876
4877	this.remove = function() {
4878		this.validator.destroy();
4879		$('.product-add-to-cart button').removeAttr('disabled');
4880	}
4881	
4882	this.getStoreListForGeolocationPosition = function(position, isLoadMore) {
4883		var self = this;
4884		var geolocationPosition;
4885		if (position && position.coords) {
4886			geolocationPosition = {
4887				coords: {
4888					accuracy: position.coords.accuracy,
4889					latitude: position.coords.latitude,
4890					longitude: position.coords.longitude
4891				},
4892				timestamp: position.timestamp
4893			};
4894		}
4895		$.ajax({
4896			method: 'POST',
4897			url: Urls.Bopis.GetLocationsForGeolocationPosition,
4898			contentType: 'application/json',
4899			data: JSON.stringify({
4900				Channel: this.options.Channel,
4901				Source: this.options.Source,
4902				GeolocationPosition: geolocationPosition,
4903				Radius: parseFloat(this.$formInputRadius.val()) || 3000,
4904				Limit: this.dataBuffer.limit,
4905				Offset: this.dataBuffer.offset
4906			}),
4907			dataType: 'json',
4908			success: function(data) {
4909				self.renderStoreList(data, isLoadMore);
4910			},
4911			error: function() {
4912				
4913			}
4914		});		
4915	}
4916
4917	this.getStoreListForPostalCode = function(postalCode, isLoadMore) {
4918		var self = this;
4919		$.ajax({
4920			method: 'POST',
4921			url: Urls.Bopis.GetStoresByCityStatePostalSearchKeyAjax,
4922			contentType: 'application/json',
4923			data: JSON.stringify({
4924				Channel: self.options.Channel,
4925				Source: self.options.Source,
4926				SearchKey: postalCode,
4927				Radius: self.$formInputRadius.val() || 3000,
4928				Limit: self.dataBuffer.limit,
4929				Offset: self.dataBuffer.offset
4930			}),
4931			dataType: 'json',
4932			success: function(data) {
4933				self.renderStoreList(data, isLoadMore);
4934			},
4935			error: function() {
4936				if (self.options.errorCallback) {
4937					self.options.errorCallback();
4938				}
4939			}
4940		});
4941	}
4942
4943	this.renderStoreList = function(data, isLoadMore) {
4944		var locationData;
4945		if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data && data.InquiryResult.data.length > 0) {
4946			locationData = data.InquiryResult.data;
4947		}
4948
4949		this.$container.attr('state', 'result');
4950		if (locationData && locationData.length > 0) {
4951			var locationsDisplayed = 0;
4952			locationData = locationData.sort(function(left, right) {
4953				return left.distance - right.distance;
4954			});
4955			
4956			this.dataBuffer['StoresDisplayed'] = [];
4957			this.$container.attr('result-count', locationData.length);
4958			
4959			var storeListHtml = '';
4960			for (var locationElement in locationData) {
4961				var location = locationData[locationElement];
4962				
4963				if (location && !this.dataBuffer['StoresDisplayed'][location.code]) {
4964					var storePhone = '';
4965					var storeOpenUntil = '';
4966					if (location.StoreInfo) {
4967						if (location.StoreInfo.Phone) {
4968							storePhone = location.StoreInfo.Phone;
4969						}
4970						if (location.StoreInfo.OpenUntil) {
4971							storeOpenUntil = location.StoreInfo.OpenUntil;
4972						}
4973					}
4974
4975					var locationName;
4976					if (this.options && this.options.StoreUIVersion == '2024Redesign') {
4977						locationName = '<strong>' + location.StoreInfo.Name + ' - ' + location.StoreInfo.StoreId + '</strong>';
4978					} else {
4979						locationName = '<strong>' + location.StoreInfo.Name + '</strong>';
4980					}
4981					if (this.options && this.options.AgentStoreID == location.code) {
4982						locationName += '<br/>(Your Store)';
4983					}
4984					var html = locationName + '<br/>' +
4985								location.StoreInfo.Address1 + '<br />' + 
4986								location.StoreInfo.City + ', ' + location.StoreInfo.StateCodeParsed + ' ' + location.StoreInfo.PostalCode.substr(0, 5) + '<br />' +
4987								storePhone;
4988					var address = location.StoreInfo.Name + ' - ' + location.StoreInfo.Address1 + ' - ' + location.StoreInfo.City + ', ' + location.StoreInfo.StateCodeParsed + ' ' + location.StoreInfo.PostalCode.substr(0, 5);
4989					if (pageContext.siteId == SiteConstants.BootBarnRspUS && storeOpenUntil) {
4990						html += '<br />' + storeOpenUntil;
4991					}
4992
4993					storeListHtml += this.resultItemTemplate({
4994						StoreId: location.code,
4995						Distance: location.StoreInfo.Distance.toFixed(1) + ' mi',
4996						Selected: '',
4997						LandingCategory: this.options.LandingCategory,
4998						StatusDisplay: storeOpenUntil,
4999						Address: address,
5000						CarriesFR: 'carriesFR' in location.StoreInfo.Custom && location.StoreInfo.Custom.carriesFR,
5001						Html: html
5002					});
5003				}
5004				
5005				locationsDisplayed++;
5006				if (locationsDisplayed >= this.dataBuffer.limit) {
5007					break;
5008				}
5009				this.dataBuffer['StoresDisplayed'][location.code] = true;
5010			}
5011			
5012			var resultHtml = this.resultContainerTemplate({
5013				Html: storeListHtml
5014			});
5015			if (pageContext.siteId == SiteConstants.BootBarnRspUS && data.InquiryResult.meta.page < data.InquiryResult.meta.pages) {
5016				resultHtml += '<a href="#" class="load-more">Load More</a>';
5017			}
5018			if (this.$storeSearchButton.attr('loading')) {
5019				this.$storeSearchButton.loading('destroy');
5020			}
5021			if (this.$result.attr('loading')) {
5022				this.$result.loading('destroy');
5023			}
5024			if (isLoadMore) {
5025				this.$result.find('.load-more').remove();
5026				this.$result.append(resultHtml);
5027			} else {
5028				this.$result.html(resultHtml);
5029			}
5030		} else if (!isLoadMore) {
5031			this.$container.attr('result-count', '0');
5032			this.$result.html('');
5033		}
5034	}
5035
5036	this.updateStoreDetails = function(storeId) {
5037		if (storeId) {
5038			$.ajax({
5039				method: 'POST',
5040				url: Urls.Stores.SetMyStore,
5041				contentType: 'application/json',
5042				data: JSON.stringify({
5043					StoreId: storeId
5044				}),
5045				dataType: 'html',
5046				success: function(data) {
5047					$('.my-store-panel').replaceWith(data);
5048					$('.my-store-modal').hide();
5049					$('.location-search-dialog').hide();
5050
5051					$.ajax({
5052						method: 'GET',
5053						url: Urls.Stores.GetMyStorePanel,
5054						dataType: 'html',
5055						success: function(data) {
5056							$('.my-store-panel').replaceWith(data);
5057							$(document).trigger('Mobile.Redesign.ToggleEllipsis');
5058						}
5059					});
5060
5061					$.ajax({
5062						method: 'GET',
5063						url: Urls.Stores.GetMyStoreModal,
5064						dataType: 'html',
5065						success: function(data) {
5066							$('.my-store-modal').replaceWith(data);
5067							$(document).trigger('Mobile.Redesign.SetStore');
5068						}
5069					});
5070
5071					$.ajax({
5072						method: 'GET',
5073						url: Urls.getStoresPanel,
5074						dataType: 'html',
5075						success: function(data) {
5076							$('#location').html(data);
5077						}
5078					});
5079
5080					$(document).trigger('ShopMyStore.Update', storeId);
5081				},
5082				error: function() {
5083					$('.my-store-modal').hide();
5084					$('.location-search-dialog').hide();
5085				}
5086			});
5087		}
5088	}
5089
5090	this.assignStoreId = function(storeId) {
5091		if (storeId) {
5092			$.ajax({
5093				url : Urls.assignStoreId,
5094				data : { store_id : storeId },
5095				method : "POST",
5096				dataType : "JSON",
5097				success : function(data) {
5098					if ('success' in data && data['success'] == true) {
5099						// TODO: any follow-up needed client-side?
5100					}
5101				}
5102			});
5103		}
5104	}
5105	
5106	return this;
5107}
5108
5109},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper":115,"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"lodash":133}],11:[function(require,module,exports){
5110'use strict';
5111
5112exports.ValidateProductLineItems = validateProductLineItems;
5113
5114var options;
5115var buffer = {};
5116
5117function validateProductLineItems($bopisProductLineItems, optionsParameter) {
5118	if (optionsParameter) {
5119		options = optionsParameter;
5120	}
5121	
5122	buffer['TotalCount'] = $bopisProductLineItems.length;
5123	buffer['LoadedCount'] = 0;
5124	buffer['ProductLineItems'] = {};
5125	
5126	$bopisProductLineItems.each(function(index, element) {
5127		var $this = $(this);
5128		var uuid = $this.attr('uuid');
5129		var productId = $this.attr('sku');
5130		var storeId = $this.attr('store');
5131		var quantityRequested = $this.closest('.order-item').find('.qty-edit input[type="text"]').val();
5132		
5133		$.ajax({
5134            method: 'POST',
5135            url: Urls.Bopis.GetStoreInventoryForProductForStore,
5136            contentType: 'application/json',
5137            async: true,
5138            data: JSON.stringify({
5139                ProductId: productId,
5140                StoreId: storeId
5141            }),
5142            dataType: 'json',
5143            success: function(data) {
5144                var inquiryResult;
5145                if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data[0].inventory_supplies) {
5146                    inquiryResult = data.InquiryResult.data[0].inventory_supplies;
5147                }
5148                
5149                if (inquiryResult && inquiryResult && inquiryResult.length > 0) {
5150                	buffer['ProductLineItems'][uuid] = {
5151                		QuantityRequested: parseInt(quantityRequested, 10),
5152                		InquiryResult: inquiryResult
5153                	};
5154                	
5155                    buffer['LoadedCount'] += 1;
5156                    validateProductLineItemsSuccess();
5157                } else {
5158                	$result.html('');
5159                    
5160                    if (options && options.ErrorCallback) {
5161                    	options.errorCallback();
5162                    }
5163                }
5164            },
5165            error: function() {
5166                if (options && options.ErrorCallback) {
5167                	options.errorCallback();
5168                }
5169            }
5170        });
5171	});
5172	
5173	return false;
5174}
5175
5176function validateProductLineItemsSuccess() {
5177	if (buffer['LoadedCount'] == buffer['TotalCount']) {
5178		var isBopisInventoryValid = true;
5179		for (var productLineItemUUID in buffer['ProductLineItems']) {
5180			var productLineItem	= buffer['ProductLineItems'][productLineItemUUID];
5181			
5182			if (productLineItem.QuantityRequested > productLineItem.InquiryResult[0].detail.quantity) {
5183				isBopisInventoryValid = false;
5184			}
5185		}
5186		
5187		if (options && options.SuccessEvent) {
5188			$(document).trigger(options.SuccessEvent, {
5189				SubmitPaymentForBopis: true,
5190				IsBopisInventoryValid: isBopisInventoryValid
5191			});
5192		}
5193	}
5194}
5195},{}],12:[function(require,module,exports){
5196'use strict';
5197
5198var dialog = require('../../dialog');
5199var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
5200var loadingIndicator = require('../../components/LoadingIndicator');
5201
5202exports.initialize = initialize;
5203
5204var options;
5205var dataBuffer;
5206
5207function initialize(optionsParameter) {
5208	dataBuffer = {};
5209	options = $.extend({
5210		successCallback: null,
5211		errorCallback: null
5212	}, optionsParameter);
5213
5214	$('#shop-your-store-dialog').find('.store-name').text(options.StoreName);
5215	$('#shop-your-store-dialog').find('.category-links a').each(function(index, element) {
5216		var $this = $(this);
5217		var href = $this.attr('href');
5218		if (href.indexOf('?') > -1) {
5219			href = href.substr(0, href.indexOf('?'));
5220		}
5221		$this.attr('href', href + '?prefn1=availableStores&prefv1=' + options.StoreId + '&shopmystore=true');
5222	})
5223	
5224	// GoogleTagManagerClient.PushDataLayerForEvent('roadieFormDisplay');
5225	dialog.open({
5226		html: $('#shop-your-store-dialog').html(),
5227		options: {
5228			dialogClass: 'shop-your-store-dialog',
5229			width: 380,
5230			draggable: false,
5231			resizable: false,
5232			open: function(event, ui) {
5233				var $this = $(this);
5234				var $shopYourStoreToggle = $this.find('.shop-my-store-toggle .checkbox-toggle');
5235				$(this).closest('.ui-dialog').attr({
5236					id: "shop-your-store-dialog-container",
5237					"tabindex": "-1"
5238				}).focus();
5239				dataBuffer['toggleTimer'] = setInterval(function() {
5240					$shopYourStoreToggle.click();
5241				}, 2000);
5242            },
5243            close: function(event, ui) {
5244				clearInterval(dataBuffer['toggleTimer']);
5245				$(this).find('.shop-my-store-toggle .checkbox-toggle input').prop('checked', false);
5246            }
5247		}
5248	});
5249	
5250}
5251},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../components/LoadingIndicator":25,"../../dialog":36}],13:[function(require,module,exports){
5252'use strict';
5253
5254var _ = require('lodash');
5255var dialog = require('../../dialog');
5256var loadingIndicator = require('../../components/LoadingIndicator');
5257var LocalDeliveryDialog = require('./LocalDeliveryDialog');
5258var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
5259var tooltip = require('../../tooltip');
5260var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
5261var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
5262var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
5263var ZipCodeToStateMap = require('../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json');
5264
5265exports.initialize = initialize;
5266exports.search = search;
5267exports.remove = remove;
5268
5269var options;
5270var validator;
5271var dataBuffer;
5272
5273var resultContainerTemplate;
5274var resultItemTemplate;
5275var localDeliveryDecorator;
5276
5277var $container;
5278var $form;
5279var $result;
5280var $formInputPostalCode;
5281var $storeSearchButton;
5282var $storeSearchButtonContainer;
5283var $bopisButton;
5284var $errorMessage;
5285var $inputTooltip;
5286
5287function initialize($containerParameter, optionsParameter) {
5288	dataBuffer = {};
5289	options = $.extend({
5290		successCallback: null,
5291		errorCallback: null
5292	}, optionsParameter);
5293
5294	resultContainerTemplate = _.template($('#template-bopis-result-container').html(), TemplateConstants.Lodash.Settings);
5295	resultItemTemplate = _.template($('#template-bopis-result-item').html(), TemplateConstants.Lodash.Settings);
5296	localDeliveryDecorator = _.template($('#template-bopis-localdelivery-decorator').html(), TemplateConstants.Lodash.Settings);
5297
5298	$container = $containerParameter;
5299	$form = $container.find('form');
5300	$result = $container.find('.result');
5301	$formInputPostalCode = $form.find('input[name$="_postalCode"]');
5302	$storeSearchButton = $container.find('.btn.store-search');
5303	$storeSearchButtonContainer = $container.find('.btn-store-search-container');
5304	$bopisButton = $container.find('.btn.bopis');
5305    $errorMessage = $form.find('span.error');
5306	$inputTooltip = $formInputPostalCode.parent();
5307
5308    if (options && options.IsVariantSelected) {
5309		$('.btn.store-search').removeAttr('disabled');
5310		$inputTooltip.removeClass('tooltip');
5311    }
5312
5313	tooltip.init({
5314		items: '.store-search-tooltip',
5315		tooltipClass: 'store-search-tooltip'
5316	}, $('.store-search-tooltip'));
5317
5318    $container.attr('state', 'form');
5319    validator = $form.validate(ValidationSettings.Global);
5320	$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
5321    $formInputPostalCode.rules('add', ValidationRules.ZipCode);
5322
5323	if ($container.attr('state') === 'form' && validator.checkForm() && !$storeSearchButton.hasClass('ready')) {
5324		$storeSearchButton.addClass('ready');
5325    }
5326
5327    $form.off('click.apply').on('click.apply', '.btn.store-search:not([loading])', function (event) {
5328		event.preventDefault();
5329		search();
5330	});
5331
5332    $container.off('click.apply').on('click.apply', '.btn.bopis:not([loading])', function (event) {
5333		event.preventDefault();
5334
5335		var selectedLocation = dataBuffer['StoresDisplayed'][$('input[name="Bopis_StoreSearch_StoreId"]:checked').val()];
5336		if (selectedLocation) {
5337			if (options.LocalDelivery.Enabled) {
5338				LocalDeliveryDialog.initialize(options, selectedLocation);
5339			} else {
5340				$(document).trigger('ProductDetailPage.Bopis.AddToCart', { bopis: true, shippingOption: 'pickup', channel: options.Channel });
5341			}
5342		} else {
5343			$(document).trigger('ProductDetailPage.Bopis.AddToCart', { channel: options.Channel });
5344		}
5345	});
5346
5347    $formInputPostalCode.off('keydown.BopisStoreSearch.ValidationCheck').on('keydown.BopisStoreSearch.ValidationCheck', function (event) {
5348    	if ($formInputPostalCode.val().length == 5 && parseInt(event.key, 10) == event.key && !window.getSelection().toString()) {
5349    		event.preventDefault();
5350    	}
5351    });
5352
5353    $formInputPostalCode.off('change.BopisStoreSearch.ValidationCheck').on('change.BopisStoreSearch.ValidationCheck', function (event) {
5354    	if ($formInputPostalCode.val().length > 5) {
5355    		$formInputPostalCode.val($formInputPostalCode.val().substr(0, 5));
5356    	}
5357    });
5358
5359    $formInputPostalCode.off('keyup.BopisStoreSearch.ValidationCheck, change.BopisStoreSearch.ValidationCheck').on('keyup.BopisStoreSearch.ValidationCheck, change.BopisStoreSearch.ValidationCheck', function (event) {
5360    	event.stopImmediatePropagation();
5361
5362		if ($container.attr('state') === 'form') {
5363			if (validator.checkForm() && !$storeSearchButton.hasClass('ready')) {
5364				$storeSearchButton.addClass('ready');
5365			} else if (!validator.checkForm() && $storeSearchButton.hasClass('ready')) {
5366				$storeSearchButton.removeClass('ready');
5367			}
5368		}
5369
5370    	if ($container.attr('state') === 'result') {
5371			$container.attr('state', 'form');
5372        	$formInputPostalCode.val('');
5373        }
5374    });
5375
5376    $(document).off('mousedown.BopisStoreSearch.ValidationClear').on('mousedown.BopisStoreSearch.ValidationClear', function (event) {
5377        if (validator && $(event.target).closest($container).length == 0) {
5378            validator.resetForm();
5379            $errorMessage.html('').hide();
5380        }
5381    });
5382
5383    $(document).off('Bopis.StoreSearch.Reset').on('Bopis.StoreSearch.Reset', function (event) {
5384		reset();
5385    });
5386
5387    $container.off('click.BopisStoreSearch.Reset').on('click.BopisStoreSearch.Reset', '.reset', function (event) {
5388		reset();
5389    });
5390
5391    $(document).off('ProductDetailPage.Variant.StoreSearch').on('ProductDetailPage.Variant.StoreSearch', function (event) {
5392        if (options.SelectedLocations) {
5393			var sizeSelected = $('div.product-variations .size.size-options li.selected');
5394			var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
5395			var widthSelected = $('div.product-variations .width.width-options li.selected').length;
5396			var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
5397			var isColorSelected = $('div.product-variations .color.color-options li.selected').length;
5398
5399			if (isSizeSelected && isColorSelected) {
5400				search();
5401			}
5402		}
5403    });
5404
5405    $container.off('click.BopisStoreSearch.Selection').on('click.BopisStoreSearch.Selection', 'input[name="Bopis_StoreSearch_StoreId"]', function (event) {
5406        var bopisStore = $('input[name="Bopis_StoreSearch_StoreId"]:checked').val();
5407
5408        if (!$bopisButton.hasClass('ready')) {
5409        	$bopisButton.addClass('ready');
5410        }
5411
5412        $('.product-add-to-cart button').attr('disabled', 'disabled').addClass('hidden');
5413
5414        if (bopisStore) {
5415        	$bopisButton.attr('state', 'pickup');
5416        } else {
5417        	$bopisButton.attr('state', 'ship');
5418        }
5419    });
5420}
5421
5422function reset() {
5423	$formInputPostalCode.val('');
5424    $container.attr('state', 'form');
5425    $storeSearchButton.removeClass('ready');
5426    $('.product-add-to-cart button').removeAttr('disabled').removeClass('hidden');
5427    $bopisButton.removeClass('ready');
5428}
5429
5430function search() {
5431	var sizeSelected = $('div.product-variations .size.size-options li.selected');
5432	var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
5433	var widthSelected = $('div.product-variations .width.width-options li.selected').length;
5434	var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
5435	var isColorSelected = $('div.product-variations .color.color-options li.selected').length;
5436
5437    if (!isSizeSelected && !isColorSelected) {
5438    	$errorMessage.html('Please select a size and color first.').show();
5439    	return false;
5440    } else if (!isSizeSelected) {
5441    	$errorMessage.html('Please select a size first.').show();
5442    	return false;
5443    } else if (!isColorSelected) {
5444    	$errorMessage.html('Please select a color first.').show();
5445    	return false;
5446    } else {
5447    	$errorMessage.html('').hide();
5448    	$storeSearchButton.removeAttr('disabled');
5449    }
5450
5451    if (Urls && Urls.Bopis.GetStoreInventoryForProductForPostalCode && validator.valid()) {
5452        $storeSearchButtonContainer.loading({
5453            IsImageShown: false,
5454			Label: Resources.Loading.Loading,
5455			BackgroundColor: $storeSearchButton.css('background-color')
5456        });
5457
5458		$.ajax({
5459            method: 'POST',
5460            url: Urls.Bopis.GetStoreInventoryForProductForPostalCode,
5461            contentType: 'application/json',
5462            data: JSON.stringify({
5463				Channel: options.Channel,
5464				Source: options.Source,
5465                ProductId: $('.product-add-to-cart input[name="pid"]').val(),
5466                PostalCode: $formInputPostalCode.val()
5467            }),
5468            dataType: 'json',
5469            success: function(data) {
5470				if ($storeSearchButtonContainer.attr('loading')) {
5471                	$storeSearchButtonContainer.loading('destroy');
5472				}
5473                $('.product-add-to-cart button').attr('disabled', 'disabled').addClass('hidden');
5474
5475                var inquiryResult;
5476                if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data && data.InquiryResult.data.length > 0 && data.InquiryResult.data[0].inventory_supplies) {
5477                    inquiryResult = data.InquiryResult.data[0].inventory_supplies;
5478                }
5479
5480                $container.attr('state', 'result');
5481                if (inquiryResult && inquiryResult.length > 0) {
5482                	dataBuffer['StoresDisplayed'] = [];
5483                    $container.attr('result-count', inquiryResult.length);
5484
5485                    var storeListHtml = '';
5486                    var isStoreSelected = false;
5487                    var selectedStoreAttribute;
5488                    for (var inventoryRecordElement in inquiryResult) {
5489                    	var inventoryRecord = inquiryResult[inventoryRecordElement];
5490
5491                    	if (inventoryRecord.detail && inventoryRecord.detail.quantity > 0 && !dataBuffer['StoresDisplayed'][inventoryRecord.location.code]) {
5492                    		if (options && options.SelectedLocations == inventoryRecord.location.code) {
5493                    			isStoreSelected = true;
5494                    			selectedStoreAttribute ='checked';
5495                    		} else {
5496                    			selectedStoreAttribute = '';
5497                    		}
5498
5499                    		var localDeliveryHtml = '';
5500                    		if (options.LocalDelivery.Enabled && isLocationInLocalDeliveryRadius(inventoryRecord.location) && inventoryRecord.location.local_delivery_enabled) {
5501                    			localDeliveryHtml = localDeliveryDecorator();
5502                    		}
5503
5504							var labelPrefix;
5505							if (options && options.IsStoreHoliday) {
5506								labelPrefix = 'Available At ';
5507							} else {
5508								labelPrefix = 'Available TODAY At ';
5509							}
5510
5511							if (inventoryRecord.location.postal_address.namePrefix) {
5512								labelPrefix += '<span class="nowrap">' + inventoryRecord.location.postal_address.namePrefix + '</span> ';
5513							}
5514
5515                        	storeListHtml += resultItemTemplate({
5516                        		StoreId: inventoryRecord.location.code,
5517                        		QuantityAvailable: inventoryRecord.detail.quantity,
5518	                        	Html: labelPrefix + inventoryRecord.location.postal_address.name + ',<br/>' + inventoryRecord.location.postal_address.address_line1 + ', ' + inventoryRecord.location.postal_address.city + ', ' + inventoryRecord.location.postal_address.province_or_state,
5519	                        	Selected: selectedStoreAttribute,
5520	                        	LocalDelivery: localDeliveryHtml
5521	                        });
5522                    	}
5523
5524                    	dataBuffer['StoresDisplayed'][inventoryRecord.location.code] = inventoryRecord;
5525                    }
5526                    storeListHtml += resultItemTemplate({
5527                    	StoreId: '',
5528                    	QuantityAvailable: '',
5529                    	Html: 'Ship Instead',
5530                    	Selected: '',
5531                    	LocalDelivery: ''
5532                    });
5533
5534                    var resultHtml = resultContainerTemplate({
5535                    	Html: storeListHtml
5536                    });
5537
5538                    if (isStoreSelected) {
5539                    	$bopisButton.addClass('ready');
5540                    }
5541
5542                    $result.html(resultHtml);
5543
5544					tooltip.init({
5545			        	items: '.tooltip'
5546                	}, $result);
5547
5548                    if (options.successCallback) {
5549                    	options.successCallback(inquiryResult);
5550                    }
5551                } else {
5552					$('.product-add-to-cart button').removeAttr('disabled').removeClass('hidden');
5553                    $container.attr('result-count', '0');
5554                	$result.html('');
5555
5556                    if (options.errorCallback) {
5557                    	options.errorCallback();
5558                    }
5559                }
5560            },
5561            error: function() {
5562				if ($storeSearchButtonContainer.attr('loading')) {
5563            		$storeSearchButtonContainer.loading('destroy');
5564				}
5565
5566                if (options.errorCallback) {
5567                	options.errorCallback();
5568                }
5569            }
5570        });
5571	}
5572}
5573
5574function remove() {
5575	validator.destroy();
5576	$('.product-add-to-cart button').removeAttr('disabled').removeClass('hidden');
5577}
5578
5579function isLocationInLocalDeliveryRadius(location) {
5580	var localDeliveryRadius;
5581	if (location.local_delivery_vendor && location.local_delivery_vendor == 'GoLocal') {
5582		localDeliveryRadius = options.LocalDelivery.RadiusForGoLocal;
5583	} else {
5584		localDeliveryRadius = options.LocalDelivery.RadiusForRoadie;
5585	}	
5586
5587	if (localDeliveryRadius && location && location.distance && localDeliveryRadius >= location.distance) {
5588		return true;
5589	}
5590
5591	return false;
5592}
5593},{"../../../../../bc_sheplersbootbarn_core/cartridge/data/ZipCodeToStateMap.json":111,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"../../dialog":36,"../../tooltip":101,"./LocalDeliveryDialog":9,"lodash":133}],14:[function(require,module,exports){
5594'use strict';
5595
5596var dialog = require('./dialog');
5597var util = require('./util');
5598var SessionAttributes = window.SessionAttributes;
5599
5600/**
5601 * @function captcha    Used to display/control the scrim containing the simulated captcha code
5602 **/
5603module.exports = function () {
5604	/**
5605	 * if the session.privacy.ratelimited element is present then show the notification
5606	 * NOTE: You will probably want to replace this with a call to an actual CAPTCHA system to replace the simple one here
5607	 */
5608	if (SessionAttributes.SHOW_CAPTCHA) {
5609		dialog.open({
5610			html: '<p>' + Resources.PASSWORD_TIP_1 + '</p><p>' + Resources.PASSWORD_TIP_2 + '</p>',
5611			options: {
5612				title: Resources.ARE_YOU_HUMAN,
5613				closeOnEscape: false,
5614				dialogClass: 'no-close password-error',
5615				width: 400,
5616				buttons: [{
5617					text: Resources.OK,
5618					click: function () {
5619						var url = util.appendParamsToUrl(Urls.rateLimiterReset, {format: 'ajax'});
5620						$.ajax({
5621							url: url
5622						});
5623						$(this).dialog('close');
5624					}
5625				}]
5626			}
5627		});
5628	}
5629};
5630
5631},{"./dialog":36,"./util":102}],15:[function(require,module,exports){
5632'use strict';
5633
5634var page = require('./page'),
5635    util = require('./util'),
5636    TPromise = require('promise');
5637
5638var _currentCategory = '',
5639    MAX_ACTIVE = 6;
5640
5641/**
5642 * @private
5643 * @function
5644 * @description Verifies the number of elements in the compare container and updates it with sequential classes for ui targeting
5645 */
5646function refreshContainer() {
5647    var $compareContainer = $('.compare-items');
5648    var $compareItems = $compareContainer.find('.compare-item');
5649    var numActive = $compareItems.filter('.active').length;
5650
5651    if (numActive < 2) {
5652        $('#compare-items-button').attr('disabled', 'disabled');
5653    } else {
5654        $('#compare-items-button').removeAttr('disabled');
5655    }
5656
5657    $compareContainer.toggle(numActive > 0);
5658}
5659/**
5660 * @private
5661 * @function
5662 * @description Adds an item to the compare container and refreshes it
5663 */
5664function addToList(data) {
5665    // get the first compare-item not currently active
5666    var $item = $('.compare-items .compare-item').not('.active').first(),
5667        $productTile = $('#' + data.uuid);
5668
5669    if ($item.length === 0) {
5670        if ($productTile.length > 0) {
5671            $productTile.find('.compare-check')[0].checked = false;
5672        }
5673        window.alert(Resources.COMPARE_ADD_FAIL);
5674        return;
5675    }
5676
5677    // if already added somehow, return
5678    if ($('[data-uuid="' + data.uuid + '"]').length > 0) {
5679        return;
5680    }
5681    // set as active item
5682    $item.addClass('active')
5683        .attr('data-uuid', data.uuid)
5684        .attr('data-itemid', data.itemid)
5685        .data('uuid', data.uuid)
5686        .data('itemid', data.itemid)
5687        .append($(data.img).clone().addClass('compare-item-image'));
5688}
5689/**
5690 * @private
5691 * @function
5692 * description Removes an item from the compare container and refreshes it
5693 */
5694function removeFromList($item) {
5695    if ($item.length === 0) { return; }
5696    // remove class, data and id from item
5697    $item.removeClass('active')
5698        .removeAttr('data-uuid')
5699        .removeAttr('data-itemid')
5700        .data('uuid', '')
5701        .data('itemid', '')
5702        // remove the image
5703        .find('.compare-item-image').remove();
5704}
5705
5706function addProductAjax(args) {
5707    var promise = new TPromise(function (resolve, reject) {
5708        $.ajax({
5709            url: Urls.compareAdd,
5710            data: {
5711                pid: args.itemid,
5712                category: _currentCategory
5713            },
5714            dataType: 'json'
5715        }).done(function (response) {
5716            if (!response || !response.success) {
5717                reject(new Error(Resources.COMPARE_ADD_FAIL));
5718            } else {
5719                resolve(response);
5720            }
5721        }).fail(function (jqxhr, status, err) {
5722            reject(new Error(err));
5723        });
5724    });
5725    return promise;
5726}
5727
5728function removeProductAjax(args) {
5729    var promise = new TPromise(function (resolve, reject) {
5730        $.ajax({
5731            url: Urls.compareRemove,
5732            data: {
5733                pid: args.itemid,
5734                category: _currentCategory
5735            },
5736            dataType: 'json'
5737        }).done(function (response) {
5738            if (!response || !response.success) {
5739                reject(new Error(Resources.COMPARE_REMOVE_FAIL));
5740            } else {
5741                resolve(response);
5742            }
5743        }).fail(function (jqxhr, status, err) {
5744            reject(new Error(err));
5745        });
5746    });
5747    return promise;
5748}
5749
5750function shiftImages() {
5751    return new TPromise(function (resolve) {
5752        var $items = $('.compare-items .compare-item');
5753        $items.each(function (i, item) {
5754            var $item = $(item);
5755            // last item
5756            if (i === $items.length - 1) {
5757                return removeFromList($item);
5758            }
5759            var $next = $items.eq(i + 1);
5760            if ($next.hasClass('active')) {
5761                // remove its own image
5762                $next.find('.compare-item-image').detach().appendTo($item);
5763                $item.addClass('active')
5764                    .attr('data-uuid', $next.data('uuid'))
5765                    .attr('data-itemid', $next.data('itemid'))
5766                    .data('uuid', $next.data('uuid'))
5767                    .data('itemid', $next.data('itemid'));
5768            }
5769        });
5770        resolve();
5771    });
5772}
5773
5774/**
5775 * @function
5776 * @description Adds product to the compare table
5777 */
5778function addProduct(args) {
5779    var promise;
5780    var $items = $('.compare-items .compare-item');
5781    var $cb = $(args.cb);
5782    var numActive = $items.filter('.active').length;
5783    if (numActive === MAX_ACTIVE) {
5784        if (!window.confirm(Resources.COMPARE_CONFIRMATION)) {
5785            $cb[0].checked = false;
5786            return;
5787        }
5788
5789        // remove product using id
5790        var $firstItem = $items.first();
5791        promise = removeItem($firstItem).then(function () {
5792            return shiftImages();
5793        });
5794    } else {
5795        promise = TPromise.resolve(0);
5796    }
5797    return promise.then(function () {
5798        return addProductAjax(args).then(function () {
5799            addToList(args);
5800            if ($cb && $cb.length > 0) { $cb[0].checked = true; }
5801            refreshContainer();
5802        });
5803    }).then(null, function () {
5804        if ($cb && $cb.length > 0) { $cb[0].checked = false; }
5805    });
5806}
5807
5808/**
5809 * @function
5810 * @description Removes product from the compare table
5811 * @param {object} args - the arguments object should have the following properties: itemid, uuid and cb (checkbox)
5812 */
5813function removeProduct(args) {
5814    var $cb = args.cb ? $(args.cb) : null;
5815    return removeProductAjax(args).then(function () {
5816        var $item = $('[data-uuid="' + args.uuid + '"]');
5817        removeFromList($item);
5818        if ($cb && $cb.length > 0) { $cb[0].checked = false; }
5819        refreshContainer();
5820    }, function () {
5821        if ($cb && $cb.length > 0) { $cb[0].checked = true; }
5822    });
5823}
5824
5825function removeItem($item) {
5826    var uuid = $item.data('uuid'),
5827        $productTile = $('#' + uuid);
5828    return removeProduct({
5829        itemid: $item.data('itemid'),
5830        uuid: uuid,
5831        cb: ($productTile.length === 0) ? null : $productTile.find('.compare-check')
5832    });
5833}
5834
5835/**
5836 * @private
5837 * @function
5838 * @description Initializes the DOM-Object of the compare container
5839 */
5840function initializeDom() {
5841    var $compareContainer = $('.compare-items');
5842    _currentCategory = $compareContainer.data('category') || '';
5843    var $active = $compareContainer.find('.compare-item').filter('.active');
5844    $active.each(function () {
5845        var $productTile = $('#' +  $(this).data('uuid'));
5846        if ($productTile.length === 0) {return;}
5847        $productTile.find('.compare-check')[0].checked = true;
5848    });
5849    // set container state
5850    refreshContainer();
5851}
5852
5853/**
5854 * @private
5855 * @function
5856 * @description Initializes the events on the compare container
5857 */
5858function initializeEvents() {
5859    // add event to buttons to remove products
5860    $('.compare-item').on('click', '.compare-item-remove', function () {
5861        removeItem($(this).closest('.compare-item'));
5862    });
5863
5864    // Button to go to compare page
5865    $('#compare-items-button').on('click', function () {
5866        page.redirect(util.appendParamToURL(Urls.compareShow, 'category', _currentCategory));
5867    });
5868
5869    // Button to clear all compared items
5870    // rely on refreshContainer to take care of hiding the container
5871    $('#clear-compared-items').on('click', function () {
5872        $('.compare-items .active').each(function () {
5873            removeItem($(this));
5874        });
5875    });
5876}
5877
5878exports.init = function () {
5879    initializeDom();
5880    initializeEvents();
5881};
5882
5883exports.addProduct = addProduct;
5884exports.removeProduct = removeProduct;
5885
5886},{"./page":46,"./util":102,"promise":134}],16:[function(require,module,exports){
5887'use strict';
5888
5889var dialog = require('../../js/dialog');
5890
5891var openCarouselVideoModal = function(event) {
5892    var $videoElement = $('[data-carousel-video-element]').wrap('<div class="video-container"></div>').parent();
5893    
5894    dialog.open({
5895        html: $videoElement[0].outerHTML,
5896        options: {
5897            dialogClass: 'fixed carousel-video-modal',
5898            width: '100%',
5899            height: '100%',
5900			draggable: false,
5901			resizable: false,
5902            open: function(event, ui) {
5903                var $this = $(this);
5904                $this.find('.carousel-video-element')[0].play();
5905            },
5906            close: function(event, ui) {
5907                var $this = $(this);
5908                $this.find('.carousel-video-element')[0].pause();
5909            }
5910        }
5911    });
5912}
5913
5914function initialize() {
5915	$(document).off('click.CarouselVideo.Play').on('click.CarouselVideo.Play', '.product-primary-image [data-carousel-video]', openCarouselVideoModal);
5916
5917	$(document).off('click.CarouselVideo.Catch').on('click.CarouselVideo.Catch', '.product-primary-image .carousel-video-link', function(event) {
5918		event.preventDefault();
5919	});
5920}
5921
5922module.exports.init = initialize;
5923
5924},{"../../js/dialog":36}],17:[function(require,module,exports){
5925'use strict';
5926
5927var dialog = require('../dialog');
5928
5929exports.init = initialize;
5930
5931function initialize() {
5932    wishlistPostOrder();
5933}
5934
5935function wishlistPostOrder() {
5936    $(document).off('CordialTagging.WishlistPostOrder').on('CordialTagging.WishlistPostOrder', function (event, removedProducts, orderNumber) {
5937        if ($(document).find('.order-confirmation-details').length > 0 && removedProducts && orderNumber) {
5938            var auth_data = {
5939				email: $('.confirmation .billing-info .order-billing-email').text()
5940			};
5941            var contact_data = {
5942			    'channels': {
5943			        'email': {
5944			            'address': $('.confirmation .billing-info .order-billing-name').text()
5945			        }
5946			    },
5947			    'first_name': $('.confirmation .billing-info .order-billing-name').text().split(' ')[0],
5948				'last_name': $('.confirmation .billing-info .order-billing-name').text().split(' ')[1],
5949				'phone': $('.confirmation .billing-info .order-billing-phone').text(),
5950				'postal_code': $('.confirmation .billing-info .order-billing-geo .zip').text(),
5951				'source_create': 'order',
5952				'geo_location': {
5953				    'postal_code': $('.confirmation .billing-info .order-billing-geo .zip').text(),
5954				    'country': $('.confirmation .billing-info .order-billing-country').text(),
5955				    'state': $('.confirmation .billing-info .order-billing-geo .state').text(),
5956				    'city': $('.confirmation .billing-info .order-billing-geo .city').text()
5957				}
5958			};	
5959
5960			$('.order-item').each(function(index, el) {
5961				var _this = $(this);
5962				_this.find('.productCardMetadata').each(function(key, v) {
5963					var thisItem = JSON.parse($(this).attr('data-product-gtm-json'));
5964                    if (removedProducts.indexOf(thisItem["skuid"]) > -1) {
5965                        var product = {};	
5966                        product.productID = thisItem["skuid"];
5967                        product.productName = thisItem["name"].replace(/\\/g, "");
5968                        product.category = thisItem["category"].replace(/\\/g, "");
5969                        product.price = thisItem["price"];
5970                        product.images = [_this.find('img').attr('src')];
5971                        product.url = window.location.origin + _this.find('.name').find('a').attr('href');
5972                        product.attr = { 
5973                            'manufacturer': thisItem["brand"] 
5974                        };
5975
5976                        if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
5977                            crdl('event', 'wishlist_remove', product); 
5978                        }
5979                    }
5980				});
5981			});	
5982        }
5983    });
5984}
5985
5986},{"../dialog":36}],18:[function(require,module,exports){
5987'use strict';
5988
5989var dialog = require('../dialog');
5990
5991var ValidationSettings = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
5992var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
5993var ValidationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
5994
5995exports.initialize = initialize;
5996
5997function initialize($container) {
5998	$container.find('[email-form]').each(function (index, element) {
5999		var $form = $(element);
6000		var formType = $form.attr('email-form');
6001		appendCSRFToken($form);
6002
6003		if (formType && typeof handlers[formType] == 'function') {
6004			handlers[formType]($form);
6005		}
6006	});
6007}
6008
6009var handlers = {
6010	'cpra': function($form) {
6011		var $requiredFields = $form.find('input.required');
6012		var $formNameFirst = $form.find('input[name$="name_first"]');
6013		var $formNameLast = $form.find('input[name$="name_last"]');
6014		var $formEmail = $form.find('input[name$="email"]');
6015		var $formPhone = $form.find('input[name$="phone"]');
6016		var $formRequestType = $form.find('input[name$="requestType"]');
6017		
6018		var formValidator = $form.validate(ValidationSettings.Global);
6019		$.validator.addMethod('requiredExcludingPunctuation', ValidationHelper.ValidateRequiredExcludingPunctuation, Resources.INVALID_VALUE);
6020		$requiredFields.rules('add', ValidationRules.RequiredExcludingPunctuation);
6021		$formEmail.rules('add', ValidationRules.Email);
6022		$form.find('span.error').remove();
6023		
6024		$form.find('input[type="submit"]').on('click', function(event) {
6025			event.preventDefault();
6026
6027			if (formValidator.form()) {
6028				var data = {
6029					'form_type': $form.attr('email-form'),
6030					'name_first': $formNameFirst.val(),
6031					'name_last': $formNameLast.val(),
6032					'email': $formEmail.val(),
6033					'phone': $formPhone.val(),
6034//					'request_type': $formRequestType.filter(':checked').map(function() { return this.value; }).get().join(' and ')
6035					'request_type': $formRequestType.val()
6036				}
6037
6038				$form.find('input[type="hidden"]').each(function () {
6039					var $this = $(this);
6040					data[$this.attr('name')] = $this.val();
6041				});
6042
6043				$.ajax({
6044					url: Urls.EmailForm.Send,
6045					type: 'POST',
6046					data: data,
6047					success: function(data) {
6048						if (data && data.EmailSent) {
6049							var $successContent = $('.content.cpra-form-success');
6050							
6051							dialog.open({
6052					            html: $successContent.html(),
6053					            options: {
6054					                title: $successContent.attr('title'),
6055					                dialogClass: 'fixed checkout-terms' + ($successContent.attr('dialog-class') ? ' ' + $successContent.attr('dialog-class') : ''),
6056					                draggable: false,
6057					                open: function(event, ui) {
6058					                	var $this = $(this);
6059					        
6060						            	$('body').addClass('dialog-open');
6061						            	$this.scrollTop(0);
6062						            },
6063						            close: function(event, ui) {
6064						            	$('body').removeClass('dialog-open');
6065						            }
6066					            }
6067					        });
6068						} else {
6069							var $errorContent = $('.content.cpra-form-error');
6070							
6071							dialog.open({
6072					            html: $errorContent.html(),
6073					            options: {
6074					                title: $errorContent.attr('title'),
6075					                dialogClass: 'fixed checkout-terms' + ($errorContent.attr('dialog-class') ? ' ' + $errorContent.attr('dialog-class') : ''),
6076					                draggable: false,
6077					                open: function(event, ui) {
6078					                	var $this = $(this);
6079					        
6080						            	$('body').addClass('dialog-open');
6081						            	$this.scrollTop(0);
6082						            },
6083						            close: function(event, ui) {
6084						            	$('body').removeClass('dialog-open');
6085						            }
6086					            }
6087					        });
6088						}
6089					},
6090					error: function() {
6091						var $errorContent = $('.content.cpra-form-error');
6092						
6093						dialog.open({
6094				            html: $errorContent.html(),
6095				            options: {
6096				                title: $errorContent.attr('title'),
6097				                dialogClass: 'fixed checkout-terms' + ($errorContent.attr('dialog-class') ? ' ' + $errorContent.attr('dialog-class') : ''),
6098				                draggable: false,
6099				                open: function(event, ui) {
6100				                	var $this = $(this);
6101				        
6102					            	$('body').addClass('dialog-open');
6103					            	$this.scrollTop(0);
6104					            },
6105					            close: function(event, ui) {
6106					            	$('body').removeClass('dialog-open');
6107					            }
6108				            }
6109				        });
6110					}
6111				});
6112			}
6113		});
6114	}
6115}
6116
6117function appendCSRFToken($form) {
6118	$.ajax({
6119		method: 'get',
6120		url: Urls.CSRFToken.Get,
6121		dataType: 'json',
6122		success: function(response) {
6123			$form.append('<input type="hidden" name="' + response.name + '" value="' + response.token + '"/>');
6124		}
6125	});
6126}
6127},{"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../dialog":36}],19:[function(require,module,exports){
6128'use strict';
6129
6130var Recaptcha = require('../components/Recaptcha');
6131var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
6132var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
6133
6134exports.Initialize = initialize;
6135
6136var $footerEmailForm;
6137var validator;
6138var dataBuffer = {};
6139var options = {};
6140var loaded = {};
6141
6142
6143function initialize(page) {
6144	if (page && page.options) {
6145		options = page.options;
6146	}
6147
6148	var footerEmailFormSelector;
6149	if (page.site == SiteConstants.SiteIds.CountryOutfittersUS) {
6150		footerEmailFormSelector = 'form#email-alert-signup';
6151	} else if (page.site == SiteConstants.SiteIds.BootBarnUS) {
6152		footerEmailFormSelector = 'form#bootbarn-footer-email-alert-signup';
6153	} else {
6154		footerEmailFormSelector = 'form#footer-email-signup';
6155	}
6156	
6157	$footerEmailForm = $(footerEmailFormSelector);
6158	if ($footerEmailForm.length > 0) {		
6159		var $validatorErrorMessage = $footerEmailForm.find('span.error');
6160		
6161		$footerEmailForm.on('submit', function(event) {
6162			event.preventDefault();
6163
6164			if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForFooterEmailSignup) {
6165				submitEmailSignup();
6166			}			
6167		});
6168		
6169		$(document).off('mousedown.EmailSignUp.ValidationClear').on('mousedown.EmailSignUp.ValidationClear', function(event) {
6170			if (validator && $(event.target).closest(footerEmailFormSelector).parent().length == 0) {
6171				validator.resetForm();
6172				$validatorErrorMessage.removeClass('signup-success').html('').hide();
6173	        }
6174	    });
6175
6176		$(document).on('Global.EmailSignup', function (event, data) {
6177			if (options && options.Recaptcha && options.Recaptcha.EnabledForFooterEmailSignup) {
6178				submitEmailSignup();
6179			}
6180		});
6181
6182		if (options && options.Recaptcha && options.Recaptcha.EnabledForFooterEmailSignup && options.Recaptcha.GoogleSiteKey) {
6183			Recaptcha.initialize();
6184			renderRecaptchaForEmailSignup();
6185			$(document).on('Recaptcha.Loaded', function(event) {
6186				if (!loaded.Recaptcha) {
6187					loaded.Recaptcha = true;
6188					renderRecaptchaForEmailSignup();
6189				}
6190			});
6191		}
6192	}
6193}
6194
6195function submitEmailSignup() {
6196	var $emailAddress = $footerEmailForm.find('input#email-alert-address');		
6197	validator = $footerEmailForm.validate();
6198	$emailAddress.rules('add', ValidationRules.Email);
6199	validator.form();
6200
6201	if (validator.valid()) {
6202		if(SitePreferences.CORDIAL_ENABLED){
6203			var auth_data = {
6204				email: $emailAddress.val()
6205			}
6206			var contact_data = {
6207				'channels': {
6208					'email': {
6209						'address': $emailAddress.val(),
6210						'subscribeStatus': 'subscribed'				            
6211					}
6212				},				    
6213				'signup_source': 'footer',
6214				'welcome_signup': true
6215			};
6216			
6217			var properties = {
6218				"signup_source": 'footer',
6219				"welcome_signup": true
6220			};
6221
6222			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
6223				crdl([
6224					['contact', auth_data, contact_data],
6225					['event', 'email_signup', properties]
6226				]);
6227			}
6228		}
6229		if (SitePreferences.RETENTION_COM_ENABLED) {
6230			geq.suppress();
6231		}
6232
6233		if ($('#email-alert-address.valid').length > 0 && $('#email-alert-address').val() != '') {
6234			$('input#email-alert-address').val('');
6235			$('.signup-success').show();
6236		}
6237		setTimeout(function() {
6238			$('.signup-success').hide();
6239		}, 5000);
6240	}
6241}
6242
6243function renderRecaptchaForEmailSignup() {
6244    if (loaded.Recaptcha && $footerEmailForm.length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForFooterEmailSignup && options.Recaptcha.GoogleSiteKey) {
6245        if (dataBuffer.EmailSignupRecaptcha) {
6246            grecaptcha.reset(dataBuffer.EmailSignupRecaptcha);
6247        }
6248        dataBuffer.EmailSignupRecaptcha = grecaptcha.render('footer-email-alert-signup-submit', {
6249            'sitekey': options.Recaptcha.GoogleSiteKey,
6250            'callback': emailSignup,
6251            'badge': 'inline'
6252        });
6253    }
6254}
6255},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../components/Recaptcha":29}],20:[function(require,module,exports){
6256'use strict';
6257
6258var loadingIndicator = require('./LoadingIndicator');
6259var ReturnCodes = require('../../../../int_svs/cartridge/scripts/GiftCardConstants').ReturnCodes;
6260var ValidationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
6261var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
6262
6263exports.initialize = initialize;
6264exports.remove = remove;
6265
6266var giftCardBalanceInquiryValidator;
6267
6268function initialize($container, optionsParameter) {
6269	var options = $.extend({
6270		successCallback: null,
6271		errorCallback: null
6272	}, optionsParameter);
6273	
6274	var $giftCardBalanceInquiryComponent = $container.find('.gift-card-balance-inquiry-component');
6275	
6276	var $giftCardBalanceInquiryForm = $giftCardBalanceInquiryComponent.find('form');
6277	var $giftCardBalanceInquiryResult = $giftCardBalanceInquiryComponent.find('.balance-inquiry-result');
6278	var $giftCardBalanceInquiryResultAmount = $giftCardBalanceInquiryComponent.find('.amount');
6279	var $giftCardBalanceInquiryCardNumber = $giftCardBalanceInquiryForm.find('input[name$="giftCard_number"]');
6280    var $giftCardBalanceInquiryPinNumber = $giftCardBalanceInquiryForm.find('input[name$="giftCard_pin"]');
6281    var $giftCardBalanceInquiryErrorMessage = $giftCardBalanceInquiryForm.find('span.error');
6282	
6283    $container.attr('balance-inquiry-state', 'form');
6284	
6285    $giftCardBalanceInquiryComponent.off('click.apply').on('click.apply', '.btn.balance-inquiry:not([loading])', function (event) {
6286		event.preventDefault();
6287
6288		$giftCardBalanceInquiryErrorMessage.html('').hide();
6289        giftCardBalanceInquiryValidator = $giftCardBalanceInquiryForm.validate();
6290		$.validator.addMethod('giftCardNumberLength', ValidationHelper.ValidateGiftCardNumberLength, Resources.Validation.Messages.GiftCard.Number.Length);
6291		$.validator.addMethod('giftCardNumberValues', ValidationHelper.ValidateGiftCardNumberValues, Resources.Validation.Messages.GiftCard.Number.Values);
6292		$giftCardBalanceInquiryCardNumber.rules('add', ValidationRules.GiftCard.Number);
6293		$giftCardBalanceInquiryPinNumber.rules('add', ValidationRules.GiftCard.Pin);
6294        giftCardBalanceInquiryValidator.form();
6295
6296        if (Urls && Urls.GiftCard.BalanceInquiry && giftCardBalanceInquiryValidator.valid()) {
6297            var $this = $(this);
6298
6299            $this.loading({
6300                IsImageShown: false,
6301                Label: Resources.Loading.Loading
6302            });
6303
6304			var giftCardBalanceInquiryCardNumber = $giftCardBalanceInquiryCardNumber.val().match(/(\d)/g);
6305			if (giftCardBalanceInquiryCardNumber && giftCardBalanceInquiryCardNumber.length) {
6306				giftCardBalanceInquiryCardNumber = giftCardBalanceInquiryCardNumber.join('');
6307			}
6308
6309            $.ajax({
6310                method: 'POST',
6311                url: Urls.GiftCard.BalanceInquiry,
6312                contentType: 'application/json',
6313                data: JSON.stringify({
6314                    cardNumber: giftCardBalanceInquiryCardNumber,
6315                    pin: $giftCardBalanceInquiryPinNumber.val()
6316                }),
6317                dataType: 'json',
6318                success: function (data) {
6319                    $this.loading('destroy');
6320
6321                    var inquiryResult;
6322                    if (data && data.ServiceSuccessfullyCalled && data.InquiryResult) {
6323                        inquiryResult = data.InquiryResult;
6324                    }
6325
6326                    if (inquiryResult && inquiryResult.ReturnCode.ReturnCode == ReturnCodes.Approval) {
6327                        $container.attr('balance-inquiry-state', 'result');
6328                        
6329                        $giftCardBalanceInquiryResultAmount.text(inquiryResult.BalanceAmount.AmountFormatted);
6330                        
6331                        if (options.successCallback) {
6332                        	options.successCallback(inquiryResult);
6333                        }
6334                    } else {
6335                    	if (inquiryResult.ReturnCode.ReturnDescription == 'Invalid Message') {
6336                    		inquiryResult.ReturnCode.ReturnDescription = Resources.Error.Generic;	
6337                    	}
6338                    	
6339                        $giftCardBalanceInquiryErrorMessage.html(inquiryResult.ReturnCode.ReturnDescription || Resources.Error.Generic).show();
6340                        
6341                        if (options.errorCallback) {
6342                        	options.errorCallback();
6343                        }
6344                    }
6345                }
6346            });
6347		}
6348	});
6349    
6350    $giftCardBalanceInquiryCardNumber.off('keydown.GiftCardBalanceInquiry.ValidationClear, change.GiftCardBalanceInquiry.ValidationClear').on('keydown.GiftCardBalanceInquiry.ValidationClear, change.GiftCardBalanceInquiry.ValidationClear', function (event) {
6351		if ($container.attr('balance-inquiry-state') === 'result') {
6352			$container.attr('balance-inquiry-state', 'form');
6353        	$giftCardBalanceInquiryPinNumber.val('');
6354        	$giftCardBalanceInquiryResultAmount.text('');
6355        }
6356    });
6357    
6358    $(document).off('mousedown.GiftCardBalanceInquiry.ValidationClear').on('mousedown.GiftCardBalanceInquiry.ValidationClear', function (event) {
6359        if (giftCardBalanceInquiryValidator && $(event.target).closest('.gift-card-balance-inquiry-component').parent().length == 0) {
6360            giftCardBalanceInquiryValidator.resetForm();
6361            $giftCardBalanceInquiryErrorMessage.html('').hide();
6362        }
6363    });
6364}
6365
6366function remove() {
6367	giftCardBalanceInquiryValidator.destroy();
6368}
6369},{"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../../../int_svs/cartridge/scripts/GiftCardConstants":128,"./LoadingIndicator":25}],21:[function(require,module,exports){
6370'use strict';
6371
6372var dialog = require('../dialog');
6373
6374exports.initialize = initialize;
6375
6376function initialize() {
6377    dialog.open({
6378        html: $('#dialog-content-gift-card-balance-inquiry').html(),
6379        options: {
6380            title: 'Gift Card Balance Check',
6381            dialogClass: 'gift-card-balance-inquiry',
6382            resizable: false,
6383            draggable: false
6384        }
6385    });
6386}
6387
6388},{"../dialog":36}],22:[function(require,module,exports){
6389'use strict';
6390
6391var loadingIndicator = require('./LoadingIndicator');
6392
6393exports.initialize = initialize;
6394
6395var options;
6396var dataBuffer;
6397var loadingIndicatorSettings = {
6398    IsLabelShown: false,
6399    CssClass: 'fullscreen',
6400    BackgroundColor: 'transparent'
6401};
6402
6403function initialize(optionsParameter) {
6404	options = $.extend({
6405		successCallback: null,
6406		errorCallback: null
6407	}, optionsParameter);
6408	dataBuffer = {
6409		initialized: false
6410	};
6411
6412	$(document).off('click.GlobaleInitialize').on('click.GlobaleInitialize', '.country-selector', function(event) {
6413		if ($('div#globale-script-loader-data').length > 0 && !dataBuffer.initialized) {
6414			dataBuffer.initialized = true;
6415			event.preventDefault();
6416			$('body').loading(loadingIndicatorSettings);
6417			$('body').append('<script type="text/javascript" id="globale-script-loader" src="' + Urls.Globale.ScriptLoader + '"></script>');
6418		}
6419	});
6420
6421	$(document).off('Globale.Initialized').on('Globale.Initialized', function(event) {
6422		if (dataBuffer.initialized && $('.pt_checkout_globale').length == 0) {
6423			dataBuffer.showShippingSwitcherInterval = setInterval(showShippingSwitcher, 250);
6424		}
6425	});
6426}
6427
6428function showShippingSwitcher() {
6429	if (GlobalE && GlobalE.ShippingSwitcher && GlobalE.ShippingSwitcher.Show && typeof GlobalE.ShippingSwitcher.Show == 'function') {
6430		if ($('body').attr('loading')) {
6431			$('body').loading('destroy');
6432		}
6433		clearInterval(dataBuffer.showShippingSwitcherInterval);
6434		GlobalE.ShippingSwitcher.Show();
6435	}
6436}
6437},{"./LoadingIndicator":25}],23:[function(require,module,exports){
6438'use strict';
6439
6440exports.initialize = initialize;
6441
6442var ImageConstants;
6443var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
6444
6445var carouselVideo = require('./../CarouselVideo');
6446
6447var singleImageSlickOptions;
6448var productThumbnailSlickOptions;
6449
6450var html = [];
6451var data = [];
6452var loaded = [];
6453var options;
6454
6455var $singleImageSlides;
6456var $thumbnailImageContainer;
6457var $thumbnailImageSlides;
6458
6459function initialize(params) {
6460	if (params) {
6461    	options = params;
6462	}
6463    var $pdpMain = $('#pdpMain');
6464
6465	if (pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS) {
6466		ImageConstants = require('../../../../../app_storefront_countryoutfitters/cartridge/js/ImageConstants');
6467	} else if (pageContext.isstoresite) {
6468		ImageConstants = require('../../../../../bootbarn_rsp/cartridge/js/ImageConstants');
6469	} else { 
6470		ImageConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ImageConstants');
6471	}
6472	singleImageSlickOptions = ImageConstants.SlickSlider.Options.Single;
6473	productThumbnailSlickOptions = ImageConstants.SlickSlider.Options.Thumbnail;
6474
6475    $singleImageSlides = $('.slider-single-image .slides');
6476    $thumbnailImageContainer = $('.product-thumbnails-container');
6477	$thumbnailImageSlides = $thumbnailImageContainer.find('.slides');
6478
6479    $pdpMain.off('click.Variant.Leaflet.Zoom.In').on('click.Variant.Leaflet.Zoom.In', '.custom.leaflet-control-zoom-in', function (e) {
6480		var $slides = $('.slider-single-image .slides');
6481		var slick = $slides.slick('getSlick');
6482		$slides.find('[data-slick-index="' + slick.currentSlide + '"] img.high-resolution').imgViewer2('getMap').zoomIn();
6483    });
6484    
6485    $pdpMain.off('click.Variant.Leaflet.Zoom.Out').on('click.Variant.Leaflet.Zoom.Out', '.custom.leaflet-control-zoom-out', function (e) {
6486		var $slides = $('.slider-single-image .slides');
6487		var slick = $slides.slick('getSlick');
6488		$slides.find('[data-slick-index="' + slick.currentSlide + '"] img.high-resolution').imgViewer2('getMap').zoomOut();
6489    });
6490	
6491	if (pageContext.site === SiteConstants.SiteIds.SheplersUS || pageContext.site === SiteConstants.SiteIds.BootBarnUS || pageContext.options.StoreUIVersion == '2024Redesign') {
6492		$(window).off('resize.PDP.Slider.Primary').on('resize.PDP.Slider.Primary', function (event) {
6493			if (data['mapResizeTimer']) {
6494				clearTimeout(data['mapResizeTimer']);
6495			}
6496			data['mapResizeTimer'] = setTimeout(mapResizeTimerHandler, 250);
6497		});
6498	}
6499	
6500	$singleImageSlides.on('init', function(event, slick) {
6501		// if (pageContext.site === SiteConstants.SiteIds.SheplersUS || pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
6502			var $initialSlide = $singleImageSlides.find('.slick-slide.slick-active');
6503			data['singleImageSlidesInitialSlide'] = $initialSlide.attr('data-slick-index');
6504			data['singleImageSlidesCurrentSlide'] = data['singleImageSlidesInitialSlide'];
6505
6506			initializeImageViewerOnSlide($initialSlide);
6507			// setTimeout(function(){resizeSliderToMatchSlide($initialSlide);}, 250);
6508		// }
6509	});
6510	
6511	$singleImageSlides.on('beforeChange', function(event, slick, currentSlide, nextSlide) {
6512		if (nextSlide != data['singleImageSlidesCurrentSlide']) {
6513			$thumbnailImageSlides.find('.slick-current').removeClass('slick-current');
6514			$thumbnailImageSlides.find('.slide[data-slick-index="' + nextSlide + '"]').addClass('slick-current');
6515			setThumbnailPageBySlide(nextSlide + 1);
6516
6517			// var $currentSlide = $singleImageSlides.find('[data-slick-index="' + currentSlide + '"]');
6518			// removeImageViewerOnSlide($currentSlide);
6519
6520			var $nextSlide = $singleImageSlides.find('[data-slick-index="' + nextSlide + '"]');
6521			// turn off zoom on video slide
6522			if (!$nextSlide.hasClass('carousel-video-slide')) {
6523				initializeImageViewerOnSlide($nextSlide);
6524			}
6525			// initializeSlidesToSide($nextSlide);
6526
6527			data['singleImageSlidesCurrentSlide'] = nextSlide;
6528		}
6529	});
6530
6531    var singleImageSlickOptions = ImageConstants.SlickSlider.Options.Single;
6532    if (pageContext.site === SiteConstants.SiteIds.SheplersUS || pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
6533    	singleImageSlickOptions.dots = true;
6534		singleImageSlickOptions.responsive[0].settings.arrows = false;
6535	}
6536
6537	$singleImageSlides.not('.slick-initialized').slick(singleImageSlickOptions);
6538
6539    var productThumbnailSlickOptions = ImageConstants.SlickSlider.Options.Thumbnail;
6540    if (pageContext.site === SiteConstants.SiteIds.SheplersUS || pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
6541		productThumbnailSlickOptions.vertical = true;
6542	}
6543	$thumbnailImageSlides.not('.slick-initialized').slick(productThumbnailSlickOptions);
6544
6545	data['thumbnailsCurrentPage'] = 1;
6546	data['thumbnailsMaximumSlide'] = $thumbnailImageSlides.find('.slide').length;
6547	data['thumbnailsMaximumPage'] = Math.ceil(data['thumbnailsMaximumSlide'] / productThumbnailSlickOptions.slidesToShow);
6548
6549	if (data['thumbnailsMaximumPage'] <= 1) {
6550		$thumbnailImageContainer.find('.product-thumbnails-arrow.next').addClass('disable');
6551	} else {
6552		$thumbnailImageContainer.find('.product-thumbnails-arrow.next').removeClass('disable');
6553	}
6554
6555	$('.product-thumbnails-container .product-thumbnails-arrow').on('click', function(event) {
6556		event.preventDefault();
6557
6558		var $this = $(this);
6559		var pageDelta = 0;
6560		if ($this.hasClass('next')) {
6561			pageDelta = 1;
6562		} else if ($this.hasClass('previous')) {
6563			pageDelta = -1;
6564		}
6565
6566		setThumbnailPage(data['thumbnailsCurrentPage'] + pageDelta);
6567	});
6568
6569	$('.product-thumbnails .slide a').on('click', function(event) {
6570		event.preventDefault();
6571
6572		var $this = $(this);
6573		var $currentSlide = $this.closest('.slick-track').find('.slick-current');
6574		var currentSlide = $currentSlide.data('slick-index');
6575		var $nextSlide = $this.closest('.slide');
6576		var nextSlide = $nextSlide.data('slick-index');
6577
6578		$thumbnailImageSlides.find('.slick-current').removeClass('slick-current');
6579		$nextSlide.addClass('slick-current');
6580		$singleImageSlides.slick('slickGoTo', nextSlide);
6581	});
6582
6583	carouselVideo.init();
6584}
6585
6586function initializeSlidesToSide($slide) {
6587	var $leftSlide = $slide.prev();
6588	if ($leftSlide.length > 0) {
6589		initializeImageViewerOnSlide($leftSlide);
6590	}
6591
6592	var $rightSlide = $slide.next();
6593	if ($rightSlide.length > 0) {
6594		initializeImageViewerOnSlide($rightSlide);
6595	}
6596}
6597
6598function setThumbnailPageBySlide(selectedSlide) {
6599	if (selectedSlide >= 1 && selectedSlide <= data['thumbnailsMaximumSlide']) {
6600		setThumbnailPage(Math.ceil(selectedSlide / ImageConstants.SlickSlider.Options.Thumbnail.slidesToShow));
6601	}
6602}
6603
6604function setThumbnailPage(selectedPage) {
6605	if (selectedPage >= 1 && selectedPage <= data['thumbnailsMaximumPage']) {
6606		if (pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS) {
6607			var offsetX = $thumbnailImageSlides.find('.slick-track').offset().left - $thumbnailImageSlides.find('.slick-track .slide[data-slick-index="' + (selectedPage * ImageConstants.SlickSlider.Options.Thumbnail.slidesToShow - ImageConstants.SlickSlider.Options.Thumbnail.slidesToShow) + '"]').offset().left + 'px';
6608			$thumbnailImageSlides.find('.slick-track').css('transform', 'translate3d(' + offsetX + ', 0, 0)');
6609		} else {
6610			var offsetY = $thumbnailImageSlides.find('.slick-track').offset().top - $thumbnailImageSlides.find('.slick-track .slide[data-slick-index="' + (selectedPage * ImageConstants.SlickSlider.Options.Thumbnail.slidesToShow - ImageConstants.SlickSlider.Options.Thumbnail.slidesToShow) + '"]').offset().top + 'px';
6611			$thumbnailImageSlides.find('.slick-track').css('transform', 'translate3d(0, ' + offsetY + ', 0)');
6612		}
6613
6614		if (selectedPage > 1) {
6615			$thumbnailImageContainer.find('.product-thumbnails-arrow.previous').removeClass('disable');
6616		} else {
6617			$thumbnailImageContainer.find('.product-thumbnails-arrow.previous').addClass('disable');
6618		}
6619
6620		if (selectedPage < data['thumbnailsMaximumPage']) {
6621			$thumbnailImageContainer.find('.product-thumbnails-arrow.next').removeClass('disable');
6622		} else {
6623			$thumbnailImageContainer.find('.product-thumbnails-arrow.next').addClass('disable');
6624		}
6625
6626		data['thumbnailsCurrentPage'] = selectedPage;
6627	}
6628}
6629
6630function removeImageViewerOnSlide($slide) {
6631	if ($slide.find('img.high-resolution').length > 0) {
6632		$slide.find('img').removeClass('high-resolution').imgViewer2('destroy');
6633	}
6634}
6635
6636function initializeImageViewerOnSlide($slideToInitialize) {
6637	if ($slideToInitialize.find('img.high-resolution').length == 0) {
6638		$slideToInitialize.find('img').attr('src', $slideToInitialize.find('a.zoomedImage').attr('href')).addClass('high-resolution').imgViewer2({
6639			zoomMax: 4,
6640			dragable: false,
6641			onReady: function() {
6642				var imgViewer2 = this;
6643				var map = this.map;
6644
6645				imgViewer2.setZoom(1);
6646				if (map && map.tap) {
6647					map.tap.disable();
6648				}
6649				if (map && map.dragging) {
6650					map.dragging.disable();
6651				}
6652
6653				$(map.getContainer()).off('wheel').on('wheel', function(event) {
6654					var wheelScrollAnimationTargetDelta = 0;
6655
6656					if (data['wheelScrollAnimation']) {
6657						wheelScrollAnimationTargetDelta = data['wheelScrollAnimationTarget'] - $(window).scrollTop();
6658						data['wheelScrollAnimation'].stop();
6659					}
6660
6661					var zoomLevel = imgViewer2.getZoom();
6662					var zoomMaximum = $slideToInitialize.find('img.high-resolution').imgViewer2('option', 'zoomMax');
6663					if ((zoomLevel == 1 && event.originalEvent.deltaY > 0) || (zoomLevel > (zoomMaximum - 0.5)) && event.originalEvent.deltaY < 0) {
6664						data['wheelScrollAnimationTarget'] = $(window).scrollTop() + event.originalEvent.deltaY + wheelScrollAnimationTargetDelta;
6665						data['wheelScrollAnimation'] = $('html, body').animate({
6666							scrollTop: data['wheelScrollAnimationTarget']
6667						}, {
6668							duration: 'fast',
6669							complete: function() { 
6670								data['wheelScrollAnimation'] = null;
6671							}
6672						});
6673					}
6674				});
6675
6676				map.on('zoom', function(event) {
6677					var zoomLevel = imgViewer2.getZoom();
6678
6679					if (zoomLevel > 1.05) {
6680						$slideToInitialize.find('img.high-resolution').imgViewer2('option', 'dragable', true);
6681
6682						if (map && map.tap) {
6683							map.tap.enable();
6684						}
6685						if (map && map.dragging) {
6686							map.dragging.enable();
6687						}
6688						$('.slider-single-image .slides').slick('slickSetOption', 'draggable', false, true);
6689						$('.slider-single-image .slides').slick('slickSetOption', 'swipe', false, true);
6690						$('.slider-single-image .slides').slick('slickSetOption', 'touchMove', false, true);
6691					} else {
6692						$slideToInitialize.find('img.high-resolution').imgViewer2('option', 'dragable', false);
6693
6694						if (map && map.tap) {
6695							map.tap.disable();
6696						}
6697						if (map && map.dragging) {
6698							map.dragging.disable();
6699						}
6700						$('.slider-single-image .slides').slick('slickSetOption', 'draggable', true, true);
6701						$('.slider-single-image .slides').slick('slickSetOption', 'swipe', true, true);
6702						$('.slider-single-image .slides').slick('slickSetOption', 'touchMove', true, true);
6703					}
6704				});
6705			}
6706		});
6707	} else {
6708		var slideImageMap = $slideToInitialize.find('img.high-resolution').imgViewer2('getMap');
6709		if (slideImageMap) {
6710			slideImageMap.invalidateSize();
6711		}
6712	}
6713}
6714
6715function mapResizeTimerHandler() {
6716	if (data['singleImageSlidesInitialSlide'] != undefined) {
6717		var $initialSlide = $('.slider-single-image .slides').find('[data-slick-index="' + data['singleImageSlidesInitialSlide'] + '"]');
6718		resizeSliderToMatchSlide($initialSlide);
6719
6720        var slick = $singleImageSlides.slick('getSlick');
6721        var $currentSlide = $singleImageSlides.find('[data-slick-index="' + slick.currentSlide + '"]');
6722		var currentSlideImageMap = $currentSlide.find('img.high-resolution').imgViewer2('getMap');
6723		if (currentSlideImageMap) {
6724			currentSlideImageMap.invalidateSize();
6725		}
6726	}
6727}
6728
6729function resizeSliderToMatchSlide($slide) {
6730	var $slideTrack = $slide.closest('.slick-track');
6731	$slideTrack.css('height', 'auto');
6732
6733	var $slideImage = $slide.find('.slide-image > img');
6734	var slideImageHeight = $slide.height();
6735	var slideImageWidth = $slide.width();
6736	var slideAspectRatio = slideImageWidth / slideImageHeight;
6737
6738	$slideTrack.css('height', slideImageHeight);
6739	$slideTrack.find('.slick-slide').not($slide).each(function() {
6740		var $this = $(this);
6741		var $thisImage = $this.find('.slide-image > img');
6742		var thisImageAspectRatio = $thisImage.width() / $thisImage.height();
6743
6744		if ($thisImage.height() > slideImageHeight && thisImageAspectRatio <= slideAspectRatio) {
6745			$this.addClass('constrain-height');
6746		}
6747	});
6748}
6749},{"../../../../../app_storefront_countryoutfitters/cartridge/js/ImageConstants":106,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ImageConstants":116,"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bootbarn_rsp/cartridge/js/ImageConstants":125,"./../CarouselVideo":16}],24:[function(require,module,exports){
6750'use strict';
6751
6752var carouselVideo = require('../CarouselVideo');
6753
6754// Global vars
6755var options;
6756var isMobile = window.innerWidth <= 767;
6757
6758exports.initialize = initialize;
6759
6760function initialize(params) {
6761	if (params) {
6762		options = params;
6763	}
6764	var thumbGallerySwiper, largeSwiper,
6765		thumbGalleryBreakpoints = options && options.thumbGalleryBreakpoints ? options.thumbGalleryBreakpoints : null,
6766		thumbGalleryBreakpointSettings = thumbGalleryBreakpoints ? (isMobile ? thumbGalleryBreakpoints['0'] : thumbGalleryBreakpoints['768']) : null,
6767		enableThumbGalleryForCurrentBreakpoint = thumbGalleryBreakpointSettings && thumbGalleryBreakpointSettings.enabled,
6768		isThumbnailsElm = $('.thumbs-product-swiper').length > 0;
6769
6770	// Check to see if we have any thumbnails at all
6771	if (isThumbnailsElm) {
6772		// Initialize thumbnails gallery and assign instance to 'thumbGallerySwiper'
6773		if (enableThumbGalleryForCurrentBreakpoint) {
6774			thumbGallerySwiper = thumbGallerySwiperInit(options);
6775			// Thumbnail slider modifications - can only make mods after thumbs gallery slider has been initialized.
6776			// No default arrow navigation exists for thumbnails gallery in swiper, so we add our own.
6777			thumbGalleryNavigation(options, thumbGallerySwiper, thumbGalleryBreakpointSettings);
6778			if (isMobile) {
6779				$('.product-image-container-mobile').addClass('swiper-thumbs-mobile-edits');
6780			}
6781		} else {
6782			$('.thumbs-product-swiper').addClass('d-none');
6783		}
6784		// Initialize PDP main image swiper and pass in thumbnails gallery if it exists
6785		largeSwiper = largeSwiperInit(options, thumbGallerySwiper);
6786	} else {
6787		largeSwiper = largeSwiperInit(options, null);
6788	}
6789
6790	if (options.zoom) {
6791		$('#pdpMain').off('click.Variant.Swiper.Zoom.In').on('click.Variant.Swiper.Zoom.In', '.custom.leaflet-control-zoom-in', function (e) {
6792			largeSwiper.zoom.in();
6793		});
6794		
6795		$('#pdpMain').off('click.Variant.Swiper.Zoom.Out').on('click.Variant.Swiper.Zoom.Out', '.custom.leaflet-control-zoom-out', function (e) {
6796			largeSwiper.zoom.out();
6797		});
6798	}
6799
6800	carouselVideo.init();
6801}
6802
6803function thumbGallerySwiperInit(options) {
6804	// Thumbnails swiper options
6805	var thumbnailsSwiperOptions = {
6806		direction: isMobile ? 'horizontal' :'vertical',
6807		slidesPerView: 7,
6808		slidesPerGroup: 7,
6809		loop: false,
6810		slideToClickedSlide: true,
6811		allowTouchMove: true,
6812		a11y: {
6813            enabled: true,
6814            firstSlideMessage: 'This is the first slide',
6815            lastSlideMessage: 'This is the last slide'
6816        },
6817		keyboard: {
6818			enabled: true,
6819			pageUpDown: true
6820		}
6821	};
6822
6823	if (options.thumbGalleryScrollDirection) {
6824		thumbnailsSwiperOptions.direction = isMobile ? 'horizontal' : options.thumbGalleryScrollDirection;
6825	}
6826
6827	// Initiate thumbnails swiper
6828	return new Swiper ('.thumbs-product-swiper', thumbnailsSwiperOptions);
6829}
6830
6831function largeSwiperInit(options, thumbnailsSwiper) {
6832	var largeSwiperOptions;
6833	var mobileDots = options.breakpoints && options.breakpoints['0'] && options.breakpoints['0'].dots ? options.breakpoints['0'].dots : true;
6834	var desktopDots = options.breakpoints && options.breakpoints['768'] && options.breakpoints['768'].dots ? options.breakpoints['768'].dots : false;
6835
6836	// Large product swiper options
6837	largeSwiperOptions = {
6838		slidesPerView: 1,
6839		centeredSlides: true,
6840		loop: false,
6841		a11y: {
6842            enabled: true,
6843            firstSlideMessage: 'This is the first slide',
6844            lastSlideMessage: 'This is the last slide'
6845        },
6846		keyboard: {
6847			enabled: true,
6848			pageUpDown: true
6849		},
6850		breakpoints: {
6851			0: {
6852				pagination: {
6853					enabled: mobileDots,
6854					el: '.swiper-pagination',
6855					clickable: true
6856				}
6857			},
6858			768: {
6859				pagination: {
6860					enabled: desktopDots,
6861					el: '.swiper-pagination',
6862					clickable: true
6863				}
6864			}
6865		}
6866	};
6867
6868	if (desktopDots || mobileDots) {
6869		var modifierClass;
6870		if (!desktopDots && mobileDots) {
6871			modifierClass = 'swiper-paginated-mobile-';
6872		}
6873		if (desktopDots && !mobileDots) {
6874			modifierClass = 'swiper-paginated-desktop-';
6875		}
6876		if (desktopDots && mobileDots) {
6877			modifierClass = 'swiper-paginated-both-';
6878		}
6879		if (modifierClass) {
6880			largeSwiperOptions.containerModifierClass = modifierClass;
6881		}
6882	}
6883
6884	if (options.zoom) {
6885		largeSwiperOptions.zoom = {
6886			maxRatio: 3,
6887			minRatio: 1
6888		}
6889	}
6890
6891	if (thumbnailsSwiper) {
6892		largeSwiperOptions.thumbs = {
6893			swiper: thumbnailsSwiper
6894		}
6895	}
6896
6897	// Initiate large product swiper
6898	return new Swiper ('.large-product-swiper', largeSwiperOptions);
6899}
6900
6901function thumbGalleryNavigation(options, swiperInstance, breakpointSettings) {
6902	var $swiperThumbnailsDomElement = $('.thumbs-product-swiper');
6903	if (!$swiperThumbnailsDomElement || !swiperInstance || !breakpointSettings || $swiperThumbnailsDomElement.find('.swiper-slide').length < 8) {
6904		return;
6905	} else {
6906		var addNavigation = breakpointSettings ? (breakpointSettings.arrows ? breakpointSettings.arrows : false) : null,
6907			isVertical = $swiperThumbnailsDomElement.hasClass('swiper-vertical'),
6908			isHorizontal = $swiperThumbnailsDomElement.hasClass('swiper-horizontal'),
6909			thumbGallerySlides = $swiperThumbnailsDomElement.find('.swiper-slide'),
6910			thumbsDirectionClass = isVertical ? 'thumbs-vertical' : 'thumbs-horizontal', 
6911			$thumbnailsNavigation = $('<div />', { 'class': 'thumbs-product-navigation' }),
6912			carets;
6913
6914			if (addNavigation) {
6915				if (isVertical) {
6916					carets = '<i class="fa-solid fa-angle-up d-none"></i><i class="fa-solid fa-angle-down"></i>';
6917				}
6918				if (isHorizontal) {
6919					carets = '<i class="fa-solid fa-angle-left d-none"></i><i class="fa-solid fa-angle-right"></i>';
6920				}
6921				$thumbnailsNavigation.html(carets).addClass(thumbsDirectionClass).insertBefore($swiperThumbnailsDomElement);
6922				var prevIcons = '.thumbs-product-navigation .fa-angle-up, .thumbs-product-navigation .fa-angle-left';
6923				var nextIcons = '.thumbs-product-navigation .fa-angle-down, .thumbs-product-navigation .fa-angle-right';
6924				$(document).on('click', prevIcons, function() {
6925					swiperInstance.slidePrev(500);
6926					var prevIconsFirstVisibleIndex = thumbGallerySlides.index($swiperThumbnailsDomElement.find('.swiper-slide-visible:first'));
6927					var prevIconsLastVisibleIndex = thumbGallerySlides.index($swiperThumbnailsDomElement.find('.swiper-slide-visible:last'));
6928					if (prevIconsFirstVisibleIndex == 0) {
6929						$(prevIcons).addClass('d-none');
6930					} else {
6931						$(prevIcons).removeClass('d-none');
6932					}
6933					if (prevIconsLastVisibleIndex < 7) {
6934						$(nextIcons).removeClass('d-none');
6935					} else {
6936						$(nextIcons).addClass('d-none');
6937					}
6938				});
6939				$(document).on('click', nextIcons, function() {
6940					swiperInstance.slideNext(500);
6941					var nextIconsLastVisibleIndex = thumbGallerySlides.index($swiperThumbnailsDomElement.find('.swiper-slide-visible:last'));
6942					if (nextIconsLastVisibleIndex == (thumbGallerySlides.length - 1)) {
6943						$(nextIcons).addClass('d-none');
6944					} else {
6945						$(nextIcons).removeClass('d-none');
6946					}
6947					if (nextIconsLastVisibleIndex > 6) {
6948						$(prevIcons).removeClass('d-none');
6949					} else {
6950						$(prevIcons).addClass('d-none');
6951					}
6952				});	
6953			}
6954	}
6955}
6956
6957},{"../CarouselVideo":16}],25:[function(require,module,exports){
6958'use strict';
6959
6960var _ = require('lodash');
6961var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
6962
6963$.widget('bootbarn.loading', {
6964    options: {
6965        IsImageShown: true,
6966        IsLabelShown: true,
6967        Label: Resources.Loading.Loading,
6968        Content: '',
6969        CssClass: '',
6970        BackgroundColor: null
6971    },
6972
6973    _create: function() {
6974        this.element.attr('loading', 'loading');
6975
6976        var loadingIndicatorTemplate = _.template($('#template-loading-indicator').html(), TemplateConstants.Lodash.Settings);
6977        var $loadingIndicatorContainer = $(loadingIndicatorTemplate({
6978        	Content: this.options.Content
6979        }));
6980        var $loadingIndicator = $loadingIndicatorContainer.find('.loading-indicator');
6981        $loadingIndicatorContainer.css('background-color', this.options.BackgroundColor ? this.options.BackgroundColor : this.element.css('background-color'));
6982
6983        if (this.options.CssClass) {
6984            $loadingIndicatorContainer.addClass(this.options.CssClass);
6985        }
6986        
6987        if (this.options.IsLabelShown && !this.options.Content) {
6988            $loadingIndicator.addClass('label');
6989            $loadingIndicator.attr('label', this.options.Label);
6990        }
6991
6992        if (this.options.IsImageShown) {
6993            $loadingIndicator.addClass('image');
6994        }
6995
6996		$loadingIndicatorContainer.appendTo(this.element);
6997	},
6998    
6999    _destroy: function() {
7000		this.element.removeAttr('loading').find('.loading-indicator-container').remove();
7001    }
7002});
7003
7004},{"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"lodash":133}],26:[function(require,module,exports){
7005'use strict';
7006
7007var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
7008
7009exports.Initialize = initialize;
7010exports.CloseNavigation = closeNavigation;
7011
7012var navDataBuffer = {};
7013
7014function initialize() {
7015	var siteName = pageContext && pageContext.site ? pageContext.site : '';
7016	var $navSecondary = $('.nav-secondary');
7017	
7018	$('.nav-secondary-dropdown').parent().addClass('has-dropdown');
7019	if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7020		if (window.innerWidth < 1024) {
7021			$navSecondary.find('li#location').removeClass('has-dropdown');
7022		}
7023	}
7024
7025	// BootBarn 2020 Full width nav drop down helper
7026	if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7027		// main nav hover effects		
7028		$('html #navigation .has-dropdown').hover(function () {
7029			if ($('html').hasClass('size-desktop') || ($('html').hasClass('size-tablet') && window.innerWidth >= 1025)) {
7030				$(this).addClass('active');
7031				$(this).closest('.top-banner').addClass('nav-active');
7032				$('html').addClass('desktop-nav-active');
7033				$('.nav-cover').show();
7034			}
7035		}, function () {
7036			if ($('html').hasClass('size-desktop') || ($('html').hasClass('size-tablet') && window.innerWidth >= 1025)) {
7037				$('.nav-cover').hide();
7038				$('.top-banner').removeClass('nav-active');
7039				$('html').removeClass('desktop-nav-active');
7040				$(this).removeClass('active');
7041			}
7042		});
7043	}
7044	
7045	if ($navSecondary.find('#location-display-caption').length > 0 && $navSecondary.find('#location-display-caption').hasClass('no-store-to-display')) {
7046		if ($navSecondary.find('#location-display-caption').parents('#location').length > 0) {
7047			$navSecondary.find('#location-display-caption').parents('#location').removeClass('has-dropdown');
7048		}
7049	}	
7050	
7051	// collision detection and adjustment for if menu off-screen
7052	// this is bound to document and .size-desktop to avoid iOS issue with first tap triggering mouseover instead of click - dan@redvan
7053	$(document).on('mouseover', 'html.size-desktop #navigation .has-dropdown > a', function(event) {
7054		navigationCollisionDetection($(event.currentTarget));
7055	});
7056	
7057	$('body').on('click', '#navigation .has-dropdown > a, .nav-secondary .has-dropdown > a', function(event) {
7058		event.preventDefault();
7059		
7060		var $this = $(this);
7061		var $parent = $this.closest('.has-dropdown');
7062		var $container = $parent.closest('#navigation, .nav-secondary');
7063		
7064		if ($('html').hasClass('size-mobile') && (siteName != SiteConstants.SiteIds.BootBarnUS || SitePreferences.MOBILE_NAV_REDESIGN != 'redesign')) {
7065			var $scrollingContainer = $('#navigation');
7066			var offsetOpenMenu = 0;
7067			var scrollTopNew = 0;
7068			
7069			if ($('#navigation .has-dropdown.active').first().length > 0 && ($('#navigation .has-dropdown.active').first().offset().top < $this.offset().top)) {
7070				offsetOpenMenu = $('#navigation .has-dropdown.active').first().find('.level-2, .nav-secondary-dropdown').outerHeight();
7071			}
7072			
7073			scrollTopNew = $this.offset().top - $scrollingContainer.offset().top + $scrollingContainer.scrollTop() - offsetOpenMenu;
7074
7075			$scrollingContainer.animate({
7076				scrollTop: scrollTopNew
7077			}, 150);
7078		}
7079		
7080		if (siteName === SiteConstants.SiteIds.SheplersUS && $('html').hasClass('size-tablet')) {
7081			if ($parent.hasClass('active')) {
7082				$parent.removeClass('active');
7083			} else if (!$parent.hasClass('active')) {
7084				$('#navigation .has-dropdown').removeClass('active');
7085				$parent.addClass('active');
7086			}
7087		}
7088
7089		// Mobile nav functionality
7090		if ((!$('html').hasClass('size-desktop') && siteName !== SiteConstants.SiteIds.SheplersUS) || ($('html').hasClass('size-mobile') && siteName === SiteConstants.SiteIds.SheplersUS)) {
7091			
7092			/* -- 2025 Nav Redesign -- */
7093			if (siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.MOBILE_NAV_REDESIGN == 'redesign') {
7094				// Close other header menus
7095				$('.top-banner .nav-secondary .has-dropdown .nav-secondary-dropdown').stop().animate({ left: '-100%' }, 'fast');
7096
7097				// Show/hide sub-menu drawers
7098				if ($parent.hasClass('active')) {
7099					$parent.removeClass('active').find('.level-2, .nav-secondary-dropdown').stop().animate({ left: '-100%' }, 'fast').removeClass('submenu-active');
7100				} else {
7101					$('#navigation .has-dropdown.active').removeClass('active').find('.level-2, .nav-secondary-dropdown').stop().animate({ left: '-100%' }, 'fast').removeClass('submenu-active');
7102					$parent.addClass('active').find('.level-2, .nav-secondary-dropdown').stop().animate({ left: '0px' }, 'fast').addClass('submenu-active');
7103					$('.top-banner-inner').addClass('drawer-open');
7104					$(document).trigger('Mobile.Redesign.AutoScroll');
7105				}
7106
7107				// Toggle active-menu-level class on main navigation element
7108				if ($('#navigation .has-dropdown.active:not(.accordion)').length > 0) {
7109					$container.attr('active-menu-level', '1');
7110				} else {
7111					$container.removeAttr('active-menu-level');
7112				}
7113				
7114				// Page classes based on menu open/close
7115				if ($('#navigation .has-dropdown.active').length > 0) { // Mobile
7116					$('.menu-toggle').addClass('active');
7117					$('html').addClass('menu-active');
7118
7119					if (siteName == SiteConstants.SiteIds.BootBarnUS && $('html').hasClass('size-tablet') && window.innerWidth == 1024) {
7120						$(this).closest('.top-banner').addClass('nav-active');
7121						$('html').addClass('desktop-nav-active');
7122					}
7123				} else if (window.innerWidth >= 1024) { // Desktop
7124					$('.menu-toggle').removeClass('active');
7125					$('html').removeClass('menu-active');
7126
7127					if (siteName == SiteConstants.SiteIds.BootBarnUS && $('html').hasClass('size-tablet') && window.innerWidth == 1024) {
7128						$(this).closest('.top-banner').removeClass('nav-active');
7129						$('html').removeClass('desktop-nav-active');
7130					}
7131				}
7132			/* -- Legacy Nav -- */
7133			} else {
7134				//close other header menus
7135				if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
7136					$('.top-banner-bar .nav-secondary .has-dropdown .nav-secondary-dropdown').hide();
7137				} else {
7138					$('.top-banner-bar .nav-secondary .has-dropdown .nav-secondary-dropdown').stop().slideUp(150);
7139				}
7140				
7141				if ($parent.hasClass('active')) {
7142					if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
7143						$parent.removeClass('active').find('.level-2, .nav-secondary-dropdown').hide();
7144					} else {
7145						$parent.removeClass('active').find('.level-2, .nav-secondary-dropdown').stop().slideUp(150);
7146					}
7147				} else {
7148					if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
7149						$('#navigation .has-dropdown.active').removeClass('active').find('.level-2, .nav-secondary-dropdown').hide();
7150						$parent.addClass('active').find('.level-2, .nav-secondary-dropdown').show();
7151					} else {
7152						$('#navigation .has-dropdown.active').removeClass('active').find('.level-2, .nav-secondary-dropdown').stop().slideUp(150);
7153						$parent.addClass('active').find('.level-2, .nav-secondary-dropdown').stop().slideDown(150);
7154					}
7155				}
7156				
7157				if ($('#navigation .has-dropdown.active:not(.accordion)').length > 0) {
7158					$container.attr('active-menu-level', '1');
7159				} else {
7160					$container.removeAttr('active-menu-level');
7161				}
7162				
7163				if ($('#navigation .has-dropdown.active').length > 0) {
7164					$('.menu-toggle').addClass('active');
7165					$('html').addClass('menu-active');
7166
7167					if (siteName == SiteConstants.SiteIds.BootBarnUS && $('html').hasClass('size-tablet') && window.innerWidth == 1024) {
7168						$(this).closest('.top-banner').addClass('nav-active');
7169						$('html').addClass('desktop-nav-active');
7170					}
7171				} else if (window.innerWidth >= 1024) {
7172					$('.menu-toggle').removeClass('active');
7173					$('html').removeClass('menu-active');
7174
7175					if (siteName == SiteConstants.SiteIds.BootBarnUS && $('html').hasClass('size-tablet') && window.innerWidth == 1024) {
7176						$(this).closest('.top-banner').removeClass('nav-active');
7177						$('html').removeClass('desktop-nav-active');
7178					}
7179				}
7180			}
7181		}
7182
7183		if (!$('html').hasClass('size-mobile')) {
7184			navigationCollisionDetection($this);
7185		}
7186		
7187		if (!$('html').hasClass('size-mobile') && $this.is('[id="location-display-caption"]') && siteName == SiteConstants.SiteIds.BootBarnUS) {
7188			window.location.href = $this.attr('href');
7189		} else {
7190			return false;
7191		}
7192	})
7193	.on('click', '.menu-toggle', function (event) {
7194		event.preventDefault();
7195
7196		var $this = $(this);
7197		if ($('html').hasClass('size-mobile') || $('html').hasClass('size-tablet')) {
7198			var $html = $('html');
7199			var $body = $('body');
7200			var $container = $('#wrapper');
7201			var headerWrapper = $('.header-wrapper');
7202			var jumbotronContainer = headerWrapper.find('.jumbotron-ajax');
7203			var logoAndLinks = headerWrapper.find('.logo-and-links-container');
7204			var navFixedHeaderLimit = 800;
7205			var topBannerBar = headerWrapper.find('.top-banner-bar');
7206	
7207			var tobBannerInnerHeight = window.innerHeight - (parseFloat(topBannerBar.outerHeight()) + parseFloat(jumbotronContainer.outerHeight()));
7208			var navContainerHeight = window.innerHeight - (parseFloat(topBannerBar.outerHeight()) + parseFloat(logoAndLinks.outerHeight()) + parseFloat(jumbotronContainer.outerHeight())); 
7209	
7210			if (!$html.hasClass('menu-active')) {
7211				var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
7212				navDataBuffer['wrapperScrollTop'] = scrollTop;
7213				navDataBuffer['wrapperMarginTop'] = $container.css('margin-top');
7214				$container.attr('buffer-scrollTop', scrollTop);
7215				
7216				$('.top-banner-inner').css('height', tobBannerInnerHeight);
7217				$('.top-banner-inner .navigation-and-jumbotron-container').css('height', navContainerHeight);
7218				
7219				$html.addClass('menu-active');
7220				$this.addClass('active');
7221				$this.attr('aria-label', $this.attr('data-close')); // Change the aria-label on the menu-toggle for screen readers
7222
7223				if (window.innerWidth <= 1023 && scrollTop > navFixedHeaderLimit) {
7224					$('.header-wrapper').css('max-height', window.innerHeight);
7225				}
7226				$('.my-store-panel.mobile-only .show-store-search').focus();
7227			} else {
7228				$html.removeClass('menu-active');
7229				$this.removeClass('active');
7230				$this.attr('aria-label', $this.attr('data-open')); // Change the aria-label on the menu-toggle for screen readers
7231	
7232				if (navDataBuffer['wrapperScrollTop'] > 0) {
7233					document.body.scrollTop = document.documentElement.scrollTop = navDataBuffer['wrapperScrollTop'];
7234				}
7235				$('#navigation').removeAttr('active-menu-level');
7236	
7237				$('.top-banner-inner').css('height', '');
7238				$('.top-banner-inner .navigation-and-jumbotron-container').css('height', 'auto');
7239	
7240				if (window.innerWidth <= 1023) {
7241					$('.header-wrapper').css('transform', 'none');
7242					$('.header-wrapper').css('max-height', 'auto');
7243					$('html, body').animate({ scrollTop: 0 }, 300);
7244				}
7245
7246				$(document).trigger('Mobile.Redesign.Reset', true); // Close other open menu drawers
7247			}
7248			$(document).trigger('Mobile.Redesign.ToggleEllipsis');
7249		}
7250
7251		$('#navigation .level-2').hide();
7252		$('#navigation .level-1').slideDown();
7253		$('#navigation .active').removeClass('active');			
7254		$('.nav-secondary-dropdown').slideUp(150);
7255
7256		if ($('#mini-cart').hasClass('active')) {
7257			$('#mini-cart .mini-cart-link').removeClass('active');
7258			$('#mini-cart').removeClass('active');
7259		}
7260
7261		$(document).trigger('SearchBar.Hide');
7262		
7263		if ($('.second-mobnav').hasClass('active')) {				
7264			$('.second-mobnav').removeClass('active')
7265			$('#second-mobnav').stop().slideUp(150);
7266		}
7267	})
7268	.on('click.MenuItemToggle', '.menu-category li .menu-item-toggle', function (event) {
7269		event.preventDefault();
7270		var $parentLi = $(e.target).closest('li');
7271		$parentLi.siblings('li').removeClass('active').find('.menu-item-toggle').removeClass('fa-chevron-up active').addClass('fa-chevron-right');
7272		$parentLi.toggleClass('active');
7273		$(e.target).toggleClass('fa-chevron-right fa-chevron-up active');
7274	});
7275
7276	/* --- ADA Compliance: Keyboard main menu navigation ---
7277	 * ARROW DOWN: Will open flyouts; focus goes to first clickable link in flyout
7278	 * ARROW UP: Will close flyouts; focus returns to the parent of the flyout
7279	 * TAB: A focus indicator will show up at time of each tab
7280	 * ENTER: By default, hitting enter will engage any link that has current focus
7281	 */
7282	$(document).on('keyup', 'ul.menu-category li.has-dropdown', function (event) { 
7283		// Down Arrow: If sub-menu has been opened...
7284		if (event.which === SiteConstants.KeyCodes.ArrowDown) {
7285			// BootBarn-specific nav features
7286			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7287				$(this).trigger('mouseenter');
7288			}
7289			$(this).find('ul').find('a[href!=""][href]').removeAttr('tabindex'); // Remove tabindex to allow natural tab flow of nav list
7290			var navLinksPopulated = $(this).find('.level-2 ul:first-of-type li:first-of-type').find('a').filter(function(x, navLink) {
7291				if ($(navLink).attr('href') && $(navLink).attr('href').trim().length > 0) {
7292					return navLink;
7293				}
7294			});
7295			$(navLinksPopulated[0]).focus().addClass('focus-visible-indicator'); // Focus on first populated <a> within opened sub-menu and add visual indicator
7296			$(this).attr('aria-expanded','true').addClass('active keyboard-activated'); // Update expanded attribute and add keyboard class
7297		}
7298		// Up Arrow: If sub-menu has been closed...
7299		if (event.which === SiteConstants.KeyCodes.ArrowUp) {
7300			// BootBarn-specific nav features
7301			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7302				$(this).trigger('mouseleave');
7303			}
7304			$(this).find('ul').find('a[href!=""][href]').attr('tabindex','-1'); // Add negative tabindex to skip nested anchors
7305			$(this).attr('aria-expanded','false').removeClass('active keyboard-activated').focus().addClass('focus-visible-indicator'); // Update expanded attribute, remove keyboard class and reposition focus indicator
7306		}
7307		// Tab: Keyboard logic for nav tabbing...
7308		if (event.which === SiteConstants.KeyCodes.Tab) {
7309			if (!$(document.activeElement).hasClass('keyboard-activated') && ($(document.activeElement).closest('li.has-dropdown') && !$(document.activeElement).closest('li.has-dropdown').hasClass('keyboard-activated'))) { // If user tabbed away from activated sub-menu, close the sub-menu
7310				// BootBarn-specific nav features
7311				if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7312					$('li.has-dropdown.keyboard-activated').trigger('mouseleave');
7313				}
7314				$('li.has-dropdown.keyboard-activated').find('ul').find('a[href!=""][href]').attr('tabindex','-1'); // Add negative tabindex to skip nested anchors when submenu is closed
7315				$('li.has-dropdown.keyboard-activated').attr('aria-expanded','false').removeClass('active keyboard-activated'); // Update expanded attribute and remove keyboard class
7316			}
7317		}
7318	})
7319	.on('keydown', '.menu-toggle', function (event) { 
7320		if (($('html').hasClass('size-mobile') || $('html').hasClass('size-tablet')) && event.which === SiteConstants.KeyCodes.Enter) {
7321			$('.my-store-panel.mobile-only a').focus().addClass('focus-visible-indicator');
7322		}
7323	})
7324	.on('mouseover', 'ul.menu-category li.has-dropdown', function (event) {
7325		$(this).attr('aria-expanded','true'); // CSS-expaned sub-menu occurs on hover. Use JS to update the expanded attribute for mouse users.
7326	})
7327	.on('mousemove', function (event) {
7328		$('.focus-visible-indicator').removeClass('focus-visible-indicator');
7329		if ($('li.has-dropdown.keyboard-activated')) {
7330			if (siteName == SiteConstants.SiteIds.BootBarnUS) {
7331				$('li.has-dropdown.keyboard-activated').trigger('mouseleave');
7332			}
7333			$('li.has-dropdown.keyboard-activated').find('ul').find('a[href!=""][href]').attr('tabindex','-1'); // Add negative tabindex to skip nested anchors when submenu is closed
7334			$('li.has-dropdown.keyboard-activated').attr('aria-expanded','false').removeClass('active keyboard-activated'); // Update expanded attribute and remove keyboard class
7335		}
7336	});
7337
7338	/* --- Document Ready --- */
7339	$(document).ready(function() {
7340		var adaSubMenuAttributes = {
7341			'role': 'menuitem',
7342			'aria-haspopup': 'true',
7343			'aria-expanded': 'false'
7344		}
7345		$('ul.menu-category.level-1 ul').attr('role','menu').attr('aria-label', function(n, x) {
7346			return $(this).closest('li').find('.has-sub-menu').first().html();
7347		});
7348		$('ul.menu-category.level-1').find('a').filter(function(n, navLink) {
7349			// Add role="none" to all list items, and then use specificity further down to override if needed
7350			$(navLink).parents('ul:not(.level-1)').find('li').attr('role','none');
7351			// Add tabindex="-1" to all nested anchor tags to force tab order using arrow up and down events
7352			$(navLink).attr('tabindex','-1');
7353			// IF the href attribute is not empty, apply ADA attributes correctly for a menuitem
7354			if ($(navLink).attr('href') && $(navLink).attr('href').trim().length > 0) {
7355				$(navLink).attr('role','menuitem');
7356			// ELSE, if href attribute is empty, then ensure <a> gets skipped in keyboard navigation and screen readers
7357			} else {
7358				$(navLink).parent().attr(adaSubMenuAttributes);
7359			}
7360		});
7361
7362		if (siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.MOBILE_NAV_REDESIGN == 'redesign' && window.innerWidth <= 1024) {
7363			mobileSubMenus();
7364		}
7365	});
7366
7367	if (siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.MOBILE_NAV_REDESIGN != 'redesign' && window.innerWidth == 1024) {		
7368		$('body').on('click', function(e) {
7369			if ($('html').hasClass('size-tablet') && $('#navigation .has-dropdown').hasClass('active')) {
7370				if(e.target.id == '#navigation .has-dropdown') {
7371					return false;
7372				}
7373				if( $(e.target).closest('#navigation .has-dropdown').length > 0 ) {
7374					return false;
7375				}
7376
7377				$('#navigation .has-dropdown').removeClass('active');
7378				$('html').removeClass('menu-active');
7379				$('.header-wrapper .top-banner').removeClass('nav-active');
7380				$('html').removeClass('desktop-nav-active');
7381			}
7382		});
7383	}
7384	
7385	
7386	$('body').on('click', '#navigation .level-3 > li > a.offer-primary', function(event) {
7387		if (siteName == SiteConstants.SiteIds.CountryOutfittersUS && $('html').hasClass('size-mobile')) {
7388			event.preventDefault();
7389			
7390			var $this = $(this);
7391			var $parent = $this.closest('li');
7392			var $container = $parent.closest('.level-2');
7393			
7394			$container.find('.level-3 li').not($parent).removeClass('active');
7395			$parent.toggleClass('active');
7396		}
7397	});
7398}
7399
7400function mobileSubMenus() {
7401	var $navigation = $('.nav-rd-mobile');
7402	var $submenus = $navigation.find('div.level-2');
7403	var $columns = $navigation.find('.navigation-column');
7404	var $currentWrapper = null;
7405	
7406	$columns.each(function () {
7407		var $col = $(this);
7408		var $headers = $col.find('h4.nav-list-header');
7409
7410		if ($headers.length) { // If we have headers...
7411			$headers.each(function(i, header) {
7412				var $header = $(header);
7413				var $newAccordionSection = $('<div class="accordion-section"></div>'); // Create a new accordion section
7414				var $createNavColumn = $('<div class="navigation-column"></div>'); // Create new nav column
7415				var $list = $header.nextAll('ul').first(); // Submenu list of links
7416				$col.before($newAccordionSection); // Insert new accordion section before original nav column
7417				$createNavColumn.append($header, $list); // Append current header and <ul> to new nav column
7418				$newAccordionSection.append($createNavColumn); // Append new nav column to new accordion section
7419				$currentWrapper = $newAccordionSection; // Set the current accordion section to currentWrapper for appends of columns without headers
7420			});
7421			$col.remove(); // Remove original nav column, now that it's empty
7422		} else if ($currentWrapper) {
7423			// Append to the current accordion section
7424			$currentWrapper.append($col);
7425		}
7426	});	
7427	
7428	// Now loop through each section and set up the accordion behavior
7429	$('.accordion-section').each(function () {
7430		var $section = $(this);
7431		var $header = $section.find('h4.nav-list-header').first();
7432
7433		// Wrap the content in a collapsible div
7434		var $content = $('<div class="accordion-content"></div>');
7435		var $navigationColumns = $section.children('.navigation-column');
7436
7437		$navigationColumns.appendTo($content);
7438		$section.append($header).append($content);
7439	});	
7440	
7441	// Set up on-click for accordion headers
7442	$(document).on('click', 'h4.nav-list-header', function () {
7443		$(this).toggleClass('accordion-open');
7444		$(this).siblings('.accordion-content').slideToggle(200);
7445	});
7446
7447	$submenus.each(function() {
7448		var $sub = $(this);
7449		var $submenuContainer = $('<div class="submenu-container"></div>');
7450		var $menuHeader = $('<div class="submenu-header"></div>');
7451		var $returnButton = $('<button class="submenu-header-return" name="submenu-header-return"><span class="visually-hidden">Return to previous menu</span></button>');
7452		var $submenuHeadline = $('<h3 class="submenu-header-text"></h3>')
7453
7454		$submenuHeadline.append($sub.siblings('a.has-sub-menu').text()); //Get text to fill H3
7455		$returnButton.append($submenuHeadline); //Append H3 to return button
7456		$menuHeader.append($returnButton); //Append the return button to header
7457		$sub.prepend($menuHeader); //Append header to div.level-2
7458		$submenuContainer.append($sub.contents()); //Remove submenu HTML and append it to the new submenu-container
7459		$sub.append($submenuContainer); //Append new submenu-container to div.level-2
7460	});
7461
7462	// Set up on-click of submenu return buttons
7463	$(document).on('click', '.submenu-header-return', function () {
7464		var $this = $(this);
7465		$this.closest('.level-2, .stores-drawer').animate({
7466            "left": "-100%" // Slide left 100%
7467        }, 500, "linear", function() {
7468			$('.top-banner-inner').removeClass('drawer-open');
7469			$this.closest('li.has-dropdown').removeClass('active');
7470			$('.store-location-drawer-open').removeClass('store-location-drawer-open');
7471			$(document).trigger('Mobile.Redesign.Reset'); // Close other open menu drawers
7472		}).removeClass('submenu-active');
7473	});
7474}
7475
7476function navigationCollisionDetection($navigationEventTarget) {
7477	var $container = $navigationEventTarget.closest('.has-dropdown');
7478	
7479	$container.removeClass('collision-right collision-both');
7480		
7481	var $menu = $container.find('> .level-2');
7482	
7483	if ($menu.length > 0) {
7484		var menuLeft = $menu.offset().left;
7485		var menuRight = menuLeft + $menu.outerWidth();
7486		
7487		var $boundaryContainer = $('.top-banner-inner');
7488		
7489		if ($boundaryContainer.find('> .shell').length > 0) {
7490			$boundaryContainer = $boundaryContainer.find('> .shell');
7491		}
7492		
7493		var boundaryLeft = $boundaryContainer.offset().left;
7494		var boundaryRight = boundaryLeft + $boundaryContainer.outerWidth();
7495		
7496		var boundaryBuffer = 30;
7497
7498		if (menuRight >= (boundaryRight + boundaryBuffer)) {
7499			var buttonRight = $navigationEventTarget.offset().left + $navigationEventTarget.outerWidth();
7500			
7501			if (buttonRight - $menu.outerWidth() <= (boundaryLeft - boundaryBuffer)) {
7502				$container.addClass('collision-both');
7503			} else {
7504				$container.addClass('collision-right');
7505			}
7506		}
7507	}
7508}
7509
7510function closeNavigation() {
7511	$('html').removeClass('menu-active');
7512	$('.menu-toggle').removeClass('active');
7513	if (siteName == SiteConstants.SiteIds.BootBarnUS && SitePreferences.MOBILE_NAV_REDESIGN == 'redesign') {
7514		$('.nav-rd-mobile .has-dropdown.active').removeClass('active');
7515		$('.nav-rd-mobile .submenu-active, .nav-rd-mobile .stores-drawer').css('left', '-100%');
7516		$('.nav-rd-mobile .submenu-active, .nav-rd-mobile .accordion-content').hide();
7517	} else {
7518		$('#navigation .has-dropdown.active').removeClass('active').find('.level-2, .nav-secondary-dropdown').stop().slideUp(150);
7519	}
7520}
7521},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120}],27:[function(require,module,exports){
7522'use strict';
7523
7524exports.initialize = initialize;
7525
7526function initialize() {
7527	$(document).off('click.HeaderBanner.Close').on('click.HeaderBanner.Close', '.header-banner .close', function(event) {
7528		event.preventDefault();
7529		var $this = $(this);
7530		
7531		var contentAssetId = $this.closest('[content-asset-id]').attr('content-asset-id');
7532		if (contentAssetId) {
7533			acknowledge(contentAssetId, 'Yes', function() {
7534				$('.header-banner').hide();
7535				$(document).trigger('app.resize');
7536			});
7537		}
7538	});
7539
7540	$(document).off('click.NotificationYes').on('click.NotificationYes', '.notification .yes', function(event) {
7541		event.preventDefault();
7542		var $this = $(this);
7543		var contentAssetId = $this.closest('[content-asset-id]').attr('content-asset-id');
7544		acknowledge(contentAssetId, 'Yes');
7545		$this.closest('.notification').addClass('hidden');
7546	});
7547	
7548	$(document).off('click.NotificationNo').on('click.NotificationNo', '.notification .no', function(event) {
7549		event.preventDefault();
7550		var $this = $(this);
7551		var contentAssetId = $this.closest('[content-asset-id]').attr('content-asset-id');
7552		acknowledge(contentAssetId, 'No');
7553		window.location.href = $this.attr('href');
7554	});
7555}
7556
7557function acknowledge(notification, acknowledgement, callbackSuccessParameter, callbackErrorParameter) {
7558	var callbackSuccess = callbackSuccessParameter || function() { };
7559	var callbackError = callbackErrorParameter || function() { };
7560
7561	if (notification && acknowledgement) {
7562		$.ajax({
7563			url: Urls.Notification.Acknowledge,
7564			type: 'POST',
7565			data: {
7566				Notification: notification,
7567				Acknowledgement: acknowledgement
7568			},
7569			dataType: 'json',
7570			success: callbackSuccess,
7571			error: callbackError
7572		});
7573	}
7574}
7575},{}],28:[function(require,module,exports){
7576'use strict';
7577
7578var ValidationSettings = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
7579var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
7580var ValidationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
7581
7582exports.initialize = initialize;
7583
7584function initialize($orderStatusForm) {
7585	$.validator.addMethod('orderNumber', ValidationHelper.ValidateOrderNumber, Resources.INVALID_ORDER_NUMBER);
7586	$.validator.addMethod('postalCode', ValidationHelper.ValidatePostalCode, Resources.INVALID_POSTAL_CODE);
7587	
7588	var $orderStatusFormOrderNumber = $orderStatusForm.find('input[name$="_orderNumber"]');
7589	var $orderStatusFormOrderEmail = $orderStatusForm.find('input[name$="_orderEmail"]');
7590	var $orderStatusFormOrderPostalCode = $orderStatusForm.find('input[name$="_postalCode"]');
7591	
7592	var formValidator = $orderStatusForm.validate(ValidationSettings.Global);
7593	$orderStatusFormOrderNumber.rules('add', ValidationRules.OrderNumber);
7594	$orderStatusFormOrderEmail.rules('add', ValidationRules.Email);
7595	$orderStatusFormOrderPostalCode.rules('add', ValidationRules.PostalCode);
7596}
7597},{"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124}],29:[function(require,module,exports){
7598'use strict';
7599
7600var Promise = require('promise');
7601
7602exports.initialize = initialize;
7603exports.verify = verify;
7604
7605function initialize() {
7606	window.recaptchaLoaded = function () {
7607		$(document).trigger('Recaptcha.Loaded');
7608	};
7609
7610	window.emailSignup = function (token) {
7611		return verifyAndTriggerEvent(token, 'Global.EmailSignup');
7612	};
7613
7614	window.accountLogin = function (token) {
7615		return verifyAndTriggerEvent(token, 'Account.SubmitLogin');
7616	};
7617
7618	window.resetPassword = function (token) {
7619		return verifyAndTriggerEvent(token, 'Account.ResetPassword');
7620	};
7621
7622	window.submitOPCPayment = function (token) {
7623		return verifyAndTriggerEvent(token, 'Checkout.SubmitPayment');
7624	};
7625	
7626	window.submitOPCPaymentPrivateLabel = function (token) {
7627		return verifyAndTriggerEvent(token, 'Checkout.SubmitPaymentPrivateLabel');
7628	};
7629	
7630	window.submitRewardsCredit = function (token) {
7631		return verifyAndTriggerEvent(token, 'RewardsCredit.Add');
7632	};
7633	
7634	window.submitLoyaltyCredit = function (token) {
7635		return verifyAndTriggerEvent(token, 'LoyaltyCredit.Add');
7636	};
7637	
7638	window.submitGiftCard = function (token) {
7639		return verifyAndTriggerEvent(token, 'GiftCard.Apply');
7640	};
7641
7642	window.submitPaymentVoucher = function (token) {
7643		return verifyAndTriggerEvent(token, 'PaymentVoucher.Apply');
7644	};
7645};
7646
7647function verifyAndTriggerEvent(token, eventName) {
7648	return $.when(verify(token)).then(function(data, textStatus, xmlHttpRequest) {
7649		return new Promise(function(resolve, reject) {
7650			if (data.success) {
7651				$(document).trigger(eventName);
7652				resolve();	
7653			} else {
7654				reject();
7655			}
7656		});
7657	});	
7658}
7659
7660function verify(token) {
7661	return $.ajax({
7662		method: 'POST',
7663		url: Urls.verifyRecaptcha,
7664		contentType: 'application/json',
7665		data: JSON.stringify({
7666			token: token
7667		})
7668	});
7669}
7670},{"promise":134}],30:[function(require,module,exports){
7671'use strict';
7672
7673var loadingIndicator = require('./LoadingIndicator');
7674var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
7675
7676exports.initialize = initialize;
7677
7678var productTileContainers = [];
7679
7680function initialize(options) {
7681	if (options.Url && options.Containers && options.Containers.length > 0) {
7682		for (var index = 0; index < options.Containers.length; index++) {
7683			var container = options.Containers[index];
7684			
7685			productTileContainers[productTileContainers.length] = new ProductTileContainer(options.Url, container).load();
7686		}
7687	}
7688}
7689
7690function ProductTileContainer(url, container, requestData) {
7691	var $container;
7692	var $containerBody;
7693	var dataBuffer = {};
7694	var htmlBuffer = {};
7695	var currentHtmlBufferCount = 0;
7696	var expectedHtmlBufferCount = 0;
7697
7698	this.load = function() {
7699		$container = $(container.Selector);
7700		$containerBody = $container.find('.accordion-body');
7701		$container.hide();
7702		
7703		if ($container.length > 0) {
7704			$containerBody.loading();
7705			$container.show();
7706						
7707			$.ajax({
7708				method: 'POST',
7709				url: url,
7710				data: JSON.stringify({
7711					addProductId: container.addProductId,
7712					excludeProductId: container.excludeProductId
7713				}),
7714				contentType: 'application/json',
7715				dataType: 'json',
7716				success: function(data) {
7717					if (data && data.ProductIds && data.ProductIds.length > 0) {
7718						dataBuffer = data;
7719						expectedHtmlBufferCount = data.ProductIds.length;
7720
7721						for (var index = 0; index < data.ProductIds.length; index++) {
7722							getProductHitTile(data.ProductIds[index]);
7723						}
7724					} else {
7725						$(document).trigger('RecentlyViewedProducts.Done', $container);
7726						$container.hide();
7727						finish();
7728					}
7729				},
7730				error: function() {
7731					$container.hide();
7732					finish();
7733				}
7734			});
7735		} else {
7736			$(document).trigger('RecentlyViewedProducts.Done', $container);
7737		}
7738	}
7739
7740	var getProductHitTile = function(productId) {
7741		var requestData = {
7742			pid: productId,
7743			showswatches: 'true',
7744			showpricing: 'true',
7745			showpromotion: 'true',
7746			showrating: 'true',
7747			showbadges: 'false',
7748			showaffirm: 'false',
7749		}
7750
7751		$.ajax({
7752			method: 'GET',
7753			url: Urls.hitTile,
7754			data: requestData,
7755			dataType: 'html',
7756			success: function (data) {
7757				if (data) {
7758					htmlBuffer[productId] = '<li class="slide">' + data + '</li>';
7759				}
7760			},
7761			complete: function () {
7762				currentHtmlBufferCount++;
7763				render();
7764			}
7765		});
7766	}
7767
7768	var render = function() {
7769		if (currentHtmlBufferCount == expectedHtmlBufferCount) {
7770			var html = '';
7771
7772			for (var index = 0; index < dataBuffer.ProductIds.length; index++) {
7773				if (htmlBuffer[dataBuffer.ProductIds[index]]) {
7774					html += htmlBuffer[dataBuffer.ProductIds[index]];
7775				}
7776			}
7777
7778			var $html = $(html);
7779			if (pageContext.site != SiteConstants.SiteIds.CountryOutfittersUS) {
7780				$html.find('.product-tile').addClass('product');
7781			}
7782			$container.find('.slides').html($html);
7783			$container.find('.slider-products-primary').trigger('init-slider');
7784			finish();
7785
7786			if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui) {
7787			    affirm.ui.refresh();
7788			}
7789		}
7790	}
7791
7792	var finish = function() {
7793		$containerBody.loading('destroy');
7794		$(document).trigger('RecentlyViewedProducts.Done', $container);
7795		$(document).trigger('Currency.Update', $container);
7796	}
7797}
7798},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"./LoadingIndicator":25}],31:[function(require,module,exports){
7799'use strict';
7800
7801var loadingIndicator = require('./LoadingIndicator');
7802
7803exports.initialize = initialize;
7804
7805var recommendationsContainers = [];
7806
7807function initialize(options) {
7808	if (options.Url && options.Containers && options.Containers.length > 0) {
7809		for (var index = 0; index < options.Containers.length; index++) {
7810			recommendationsContainers[recommendationsContainers.length] = new RecommendationsContainer(options.Url, options.Containers[index]).load();
7811		}
7812	}
7813}
7814
7815function RecommendationsContainer(url, container) {
7816	var $container;
7817	var $containerBody;
7818	var dataBuffer = {};
7819	var htmlBuffer = {};
7820	var currentHtmlBufferCount = 0;
7821	var expectedHtmlBufferCount = 0;
7822
7823	this.load = function() {
7824		$container = $(container.Selector);
7825		$containerBody = $container.find('.accordion-body');
7826		
7827		$container.hide();
7828		
7829		if ($container.length > 0 && container.ProductId && container.RelationType) {
7830			$containerBody.loading();
7831			$container.show();
7832			
7833			var requestData = {
7834				'styles': container.ProductId,
7835				'relationtype': container.RelationType
7836			}
7837
7838			$.ajax({
7839				method: 'GET',
7840				url: url,
7841				data: requestData,
7842				dataType: 'json',
7843				success: function(data) {
7844					if (data && data.products && data.products.length > 0) {
7845						dataBuffer = data;
7846						expectedHtmlBufferCount = data.products.length;
7847
7848						for (var index = 0; index < data.products.length; index++) {
7849							getProductHitTile((index + 1), data.products[index].productid);
7850						}
7851					} else {
7852						$container.hide();
7853						finish();
7854					}
7855				},
7856				error: function() {
7857					$container.hide();
7858					finish();
7859				}
7860			});
7861		}
7862	}
7863
7864	var getProductHitTile = function(position, productId) {
7865		var requestData = {
7866			'position': position,
7867			'pid': productId,
7868			'showswatches': 'true',
7869			'showpricing': 'true',
7870			'showpromotion': 'true',
7871			'showrating': 'true',
7872			'showbadges': 'false',
7873			'showaffirm': 'false',
7874		}
7875
7876		$.ajax({
7877			method: 'GET',
7878			url: Urls.hitTile,
7879			data: requestData,
7880			dataType: 'html',
7881			success: function (data) {
7882				if (data) {
7883					htmlBuffer[productId] = '<li class="slide">' + data + '</li>';
7884				}
7885			},
7886			complete: function () {
7887				currentHtmlBufferCount++;
7888				render();
7889			}
7890		});
7891	}
7892
7893	var render = function() {
7894		if (currentHtmlBufferCount == expectedHtmlBufferCount) {
7895			var html = '';
7896
7897			for (var index = 0; index < dataBuffer.products.length; index++) {
7898				if (htmlBuffer[dataBuffer.products[index].productid]) {
7899					html += htmlBuffer[dataBuffer.products[index].productid];
7900				}
7901			}
7902
7903			var $html = $(html);
7904			$html.find('.product-tile').addClass('product');
7905
7906			$container.find('.slides').html($html);
7907			$container.find('.slider-products-primary').trigger('init-slider');
7908			finish();
7909
7910			if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui) {
7911			    affirm.ui.refresh();
7912			}
7913		}
7914	}
7915
7916	var finish = function() {
7917		$containerBody.loading('destroy');
7918		$(document).trigger('RelevantProductRecommendations.Done', $container);
7919		$(document).trigger('Currency.Update', $container);
7920	}
7921}
7922},{"./LoadingIndicator":25}],32:[function(require,module,exports){
7923'use strict';
7924
7925var _ = require('lodash');
7926var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
7927
7928exports.initialize = initialize;
7929exports.render = render;
7930
7931var data;
7932var loaded;
7933var options;
7934var $container;
7935
7936function initialize($containerObject, optionsObject) {
7937	if (Urls && Urls.getSameDayShippingCountdownData && Urls.getSameDayShippingCountdownContentSlot) {
7938		$container = $containerObject;
7939		options = optionsObject;
7940		data = {};
7941		loaded = {
7942			ContentSlot: false,
7943			Data: false
7944		}
7945		
7946		$(document).off('SameDayShippingCountdown.Update').on('SameDayShippingCountdown.Update', update);
7947		
7948		$.ajax({
7949			method: 'POST',
7950			url: Urls.getSameDayShippingCountdownData,
7951			dataType: 'json',
7952			success: function (response) {
7953				loaded.Data = true;
7954				data.Response = response;
7955				render();
7956			}
7957		});
7958		
7959		$.ajax({
7960			method: 'POST',
7961			url: Urls.getSameDayShippingCountdownContentSlot,
7962			dataType: 'html',
7963			success: function (html) {
7964				loaded.ContentSlot = true;
7965				data.Html = html;
7966				render();
7967			}
7968		});
7969	}
7970}
7971
7972function render() {
7973	if (loaded && loaded.ContentSlot && loaded.Data && data) {
7974		var countdownText = '';
7975		
7976		if (data.Response && data.Response.TimeLeft) {
7977			if (data.Response.TimeLeft.Hours > 0) {
7978				countdownText += data.Response.TimeLeft.Hours + ' ' + (data.Response.TimeLeft.Hours > 1 ? Resources.HOURS_ABBREVIATED : Resources.HOUR_ABBREVIATED);
7979			}
7980			
7981			if (data.Response.TimeLeft.Minutes > 0) {
7982				if (countdownText != '') {
7983					countdownText += ' ';
7984				}
7985				
7986				countdownText += data.Response.TimeLeft.Minutes + ' ' + (data.Response.TimeLeft.Minutes > 1 ? Resources.MINUTES_ABBREVIATED : Resources.MINUTE_ABBREVIATED);
7987			}
7988		}
7989
7990		if (data.Html) {
7991			$container.html(data.Html).find('input[name="initTime"]').remove();
7992		}
7993		
7994		if (countdownText) {
7995			$container.html(_.template($container.html(), TemplateConstants.Lodash.Settings)({
7996				countdown: countdownText
7997			}));
7998			
7999			update();
8000		} else {
8001			$(document).off('SameDayShippingCountdown.Update');
8002		}
8003	}
8004};
8005
8006function update() {
8007	if ($container.html().replace(/<!--([^\d]*)-->/g, '').trim().length > 0) {
8008		var $selectedVariation = $('.product-variations .attribute-size .select-text [same-day-shipping]');
8009		
8010		if ($selectedVariation.length > 0) {
8011			if ($selectedVariation.attr('same-day-shipping').toLowerCase() == 'true') {
8012				$container.show();
8013			} else {
8014				$container.hide();
8015			}
8016		} else if (parseFloat(options.percentageOfVariantsFromWarehouse) >= parseFloat(options.sameDayShippingWindowWarehousePercentage)) {
8017			$container.show();
8018		} else {
8019			$container.hide();
8020		}
8021	}
8022}
8023},{"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"lodash":133}],33:[function(require,module,exports){
8024'use strict';
8025
8026var dialog = require('./dialog');
8027
8028/**
8029 * @function cookieprivacy    Used to display/control the scrim containing the cookie privacy code
8030 **/
8031module.exports = function () {
8032    /**
8033     * If we have not accepted cookies AND we're not on the Privacy Policy page, then show the notification
8034     * NOTE: You will probably want to adjust the Privacy Page test to match your site's specific privacy / cookie page
8035     */
8036    if (SitePreferences.COOKIE_HINT === true && document.cookie.indexOf('dw_cookies_accepted') < 0) {
8037        // check for privacy policy page
8038        if ($('.privacy-policy').length === 0) {
8039            dialog.open({
8040                url: Urls.cookieHint,
8041                options: {
8042                    closeOnEscape: false,
8043                    dialogClass: 'no-close',
8044                    buttons: [{
8045                        text: Resources.I_AGREE,
8046                        click: function () {
8047                            $(this).dialog('close');
8048                            enableCookies();
8049                        }
8050                    }]
8051                }
8052            });
8053        }
8054    } else {
8055        // Otherwise, we don't need to show the asset, just enable the cookies
8056        enableCookies();
8057    }
8058
8059    function enableCookies() {
8060        if (document.cookie.indexOf('dw=1') < 0) {
8061            document.cookie = 'dw=1; path=/';
8062        }
8063        if (document.cookie.indexOf('dw_cookies_accepted') < 0) {
8064            document.cookie = 'dw_cookies_accepted=1; path=/';
8065        }
8066    }
8067};
8068
8069},{"./dialog":36}],34:[function(require,module,exports){
8070'use strict';
8071
8072exports.init = function init () {
8073
8074    $('.countdown-wrapper').each(function() {
8075        var $this = $(this),
8076          countdownStr = $this.data('countdown-date'),
8077          countdownDate = new Date(countdownStr);
8078
8079        var ts = countdown(new Date(), countdownDate, countdown.DAYS | countdown.HOURS | countdown.MINUTES);
8080        var showCountdown = ts.value > 0;
8081
8082        if (showCountdown) {
8083          $this.find('.countdown-text').text(ts.toString());
8084        }
8085
8086
8087    });
8088
8089};
8090
8091},{}],35:[function(require,module,exports){
8092'use strict';
8093
8094exports.init = function init () {
8095    $('.country-selector .current-country').on('click', function () {
8096        $('.country-selector .selector').toggleClass('active');
8097        $(this).toggleClass('selector-active');
8098    });
8099};
8100
8101},{}],36:[function(require,module,exports){
8102'use strict';
8103
8104var ajax = require('./ajax'),
8105    util = require('./util'),
8106    _ = require('lodash'),
8107    imagesLoaded = require('imagesloaded');
8108
8109var dialog = {
8110    /**
8111     * @function
8112     * @description Appends a dialog to a given container (target)
8113     * @param {Object} params  params.target can be an id selector or an jquery object
8114     */
8115    create: function (params) {
8116        var $target, id;
8117
8118        if (_.isString(params.target)) {
8119            if (params.target.charAt(0) === '#') {
8120                $target = $(params.target);
8121                // if no element found, create one
8122                if ($target.length === 0) {
8123                    id = params.target.substr(1);
8124                    $target = $('<div>').attr('id', id).addClass('dialog-content').appendTo('body');
8125                }
8126            }
8127        } else if (params.target instanceof jQuery) {
8128            $target = params.target;
8129        } else if ($('#dialog-container').length > 0) {
8130            $target = $('#dialog-container');
8131        } else {
8132            $target = $('<div id="dialog-container"/>').appendTo('body');
8133        }
8134
8135        if ((params.options != undefined && params.options != null) && (params.options.title == undefined || ('title' in params.options && params.options.title.length == 0))) {
8136            params.options.dialogClass += ' ui-dialog-title-collapse';
8137        }
8138
8139        // create the dialog
8140        this.$container = $target;
8141        this.$container.dialog(_.merge({}, this.settings, params.options || {}));
8142
8143        /* --- BOOT-5386 ---
8144         * This is a workaround for a known jquery Dialog UI bug with the maxWidth option. By setting the Dialog width option to 100% and setting the CSS max-width
8145         * to 800px, this allows the dialogs to be responsive width. This fix is necessary to create responsive Dialogs for smaller screens, but also be backwards 
8146         * compatible with existing dialogs on the site that have been previously set to the default size of 800px.
8147         * https://stackoverflow.com/questions/16471890/responsive-jquery-ui-dialog-and-a-fix-for-maxwidth-bug
8148         */
8149        var maxWidth = params.options != undefined && params.options != null && 'maxWidth' in params.options && params.options.maxWidth ? params.options.maxWidth : "800px";
8150        this.$container.parent().css("max-width", maxWidth);
8151
8152        // Adding keyboard support for Enter key on close button
8153        this.$container.find(".ui-dialog-titlebar-close").on("keydown", function (e) {
8154            if (e.key === "Enter") {
8155                e.preventDefault();
8156                $(this).trigger("click");
8157            }
8158        });
8159    },
8160    /**
8161     * @function
8162     * @description Opens a dialog using the given url (params.url) or html (params.html)
8163     * @param {Object} params
8164     * @param {Object} params.url should contain the url
8165     * @param {String} params.html contains the html of the dialog content
8166     */
8167    open: function (params) {
8168        if (this.$container && this.$container.closest('.ui-dialog-close-on-retrigger').length && this.$container.dialog('isOpen')) {
8169            this.close();
8170            return;
8171        }
8172        // close any open dialog
8173        this.close();
8174        this.create(params);
8175        this.replace(params);
8176    },
8177    /**
8178     * @description populate the dialog with html content, then open it
8179     **/
8180    openWithContent: function (params) {
8181        var content, position, callback;
8182
8183        if (!this.$container) { return; }
8184        content = params.content || params.html;
8185        if (!content) { return; }
8186        this.$container.empty().html(content);
8187        if (!this.$container.dialog('isOpen')) {
8188            this.$container.dialog('open');
8189        }
8190
8191        if (params.options) {
8192            position = params.options.position;
8193        }
8194        if (!position) {
8195            position = this.settings.position;
8196        }
8197        imagesLoaded(this.$container).on('done', function () {
8198            this.$container.dialog('option', 'position', position);
8199        }.bind(this));
8200
8201        callback = (typeof params.callback === 'function') ? params.callback : function () {};
8202        callback();
8203    },
8204    /**
8205     * @description Replace the content of current dialog
8206     * @param {object} params
8207     * @param {string} params.url - If the url property is provided, an ajax call is performed to get the content to replace
8208     * @param {string} params.html - If no url property is provided, use html provided to replace
8209     */
8210    replace: function (params) {
8211        if (!this.$container) {
8212            return;
8213        }
8214        if (params.url) {
8215            params.url = util.appendParamToURL(params.url, 'format', 'ajax');
8216            ajax.load({
8217                url: params.url,
8218                data: params.data,
8219                callback: function (response) {
8220                    params.content = response;
8221                    this.openWithContent(params);
8222                }.bind(this)
8223            });
8224        } else if (params.html) {
8225            this.openWithContent(params);
8226        }
8227    },
8228    /**
8229     * @function
8230     * @description Closes the dialog
8231     */
8232    close: function () {
8233        if (!this.$container || !this.$container.dialog("instance") || this.$container.find('[data-multi-layered="true"]').length) {
8234            return;
8235        }
8236        this.$container.dialog('close');
8237    },
8238    /**
8239     * @function
8240     * @description Submits the dialog form with the given action
8241     * @param {String} The action which will be triggered upon form submit
8242     */
8243    submit: function (action, callback) {
8244        var $form = this.$container.find('form:first');
8245        // set the action
8246        $('<input/>').attr({
8247            name: action,
8248            type: 'hidden'
8249        }).appendTo($form);
8250        // serialize the form and get the post url
8251        var data = $form.serialize();
8252        var url = $form.attr('action');
8253        // make sure the server knows this is an ajax request
8254        if (data.indexOf('ajax') === -1) {
8255            data += '&format=ajax';
8256        }
8257        // post the data and replace current content with response content
8258        $.ajax({
8259            type: 'POST',
8260            url: url,
8261            data: data,
8262            dataType: 'html',
8263            success: function (html) {
8264                this.$container.html(html);
8265                if (callback) {
8266                    callback();
8267                }
8268            }.bind(this),
8269            failure: function () {
8270                window.alert(Resources.SERVER_ERROR);
8271            }
8272        });
8273    },
8274    exists: function () {
8275        return this.$container && (this.$container.length > 0);
8276    },
8277    isActive: function () {
8278        return this.exists() && (this.$container.children.length > 0);
8279    },
8280    settings: {
8281        autoOpen: false,
8282        height: 'auto',
8283        modal: true,
8284        overlay: {
8285            opacity: 0.5,
8286            background: 'black'
8287        },
8288        resizable: false,
8289        title: '',
8290        width: '100%',
8291        open: function(event, ui) {
8292            var $dialog = $(this);
8293            if ($dialog.closest('.ui-dialog-outer-click-close').length) {
8294                setTimeout(() => {
8295                    $dialog.off('outside.element.clicked');
8296                    $dialog.on('outside.element.clicked', function () {
8297                        $dialog.dialog('close');
8298                        $dialog.off('outside.element.clicked');
8299                    });
8300                }, 0);
8301            }
8302            $dialog.parent().addClass('ui-dialog-open');
8303            $('body').addClass('modal-open');
8304        },
8305        close: function () {
8306            var $dialog = $(this);
8307            $dialog.parent().removeClass('ui-dialog-open');
8308            $dialog.dialog('close');
8309            $dialog.off('outside.element.clicked');
8310            $('body').removeClass('modal-open');
8311        },
8312        position: {
8313            my: 'center',
8314            at: 'center',
8315            of: window,
8316            collision: 'flipfit'
8317        }
8318    }
8319};
8320
8321module.exports = dialog;
8322
8323},{"./ajax":3,"./util":102,"imagesloaded":132,"lodash":133}],37:[function(require,module,exports){
8324'use strict';
8325
8326var _ = require('lodash'),
8327	dialog = require('./dialog');
8328
8329var $dialogContent,
8330	state = {},
8331	ready = {},
8332	templates = {},
8333	html = {};
8334
8335var templateSettings = { interpolate: /{{([\s\S]+?)}}/g };
8336
8337function initialize(options) {
8338	state['options'] = options || {};
8339	
8340	loadTemplates();
8341	loadStores();
8342	loadStates();
8343	
8344	openDialog();	
8345}
8346
8347function openDialog() {
8348	ready['dialog'] = false;
8349	
8350	dialog.open({
8351		html: '<div class="dialog-loading"></div>',
8352		options: {
8353			title: Resources.FIND_MORE_STORES,
8354			dialogClass: 'dialog-find-more-stores',
8355			resizable: false,
8356			draggable: false,
8357			open: function() {
8358				$dialogContent = $(this);
8359				ready['dialog'] = true;
8360				
8361				events();
8362				render();
8363			}
8364		}
8365	});
8366}
8367
8368function events() {
8369	$dialogContent.off('click.findMoreStores.go').on('click.findMoreStores.go', '.search button', function () {
8370		loadStores(true);
8371	});
8372	
8373	$dialogContent.off('click.findMoreStores.submit').on('click.findMoreStores.submit', '.submit', function () {
8374		var selectedStore = $dialogContent.find('.stores-list input:checked').val();
8375		
8376		if (selectedStore) {
8377			$(document).trigger('opc.SetShippingAddress', state['stores'].addresses[selectedStore]);
8378			dialog.close();
8379		}
8380	});
8381}
8382
8383function loadStates() {
8384	ready['states'] = false;
8385	
8386	$.ajax({ method: 'POST', url: Urls.getCountryStates, data: 'countryCode=US', dataType: 'json', success: function (data) {		
8387		state['states'] = JSON.parse(data.statesList);
8388		ready['states'] = true;
8389				
8390		render();
8391	}});
8392}
8393
8394function renderStates() {
8395	html['states'] = '<option class="placeholder"></option>';
8396	
8397	for (var stateElement in state['states']) {
8398		var stateOption = state.states[stateElement];
8399		
8400		html['states'] += '<option value="' + stateOption.label + '">' + stateOption.label + '</option>';
8401	}
8402	
8403	$dialogContent.find('select[name="StateCode"]').html(html['states']);
8404}
8405
8406function loadStores(update) {
8407	ready['stores'] = false;
8408	
8409	var payload = {};
8410	
8411	if ($dialogContent) {
8412		payload.PostalCode = $dialogContent.find('.search [name="PostalCode"]').val();
8413		payload.StateCode = $dialogContent.find('.search [name="StateCode"]').val();
8414	}
8415	
8416	payload.storeQuery = state.options['storeQuery'] ? state.options['storeQuery'] : {};
8417	
8418	$.ajax({ method: 'POST', url: Urls.getMoreStoresAddressList, data: JSON.stringify(payload), contentType: 'application/json', dataType: 'json', success: function (data) {		
8419		state['stores'] = data;
8420		ready['stores'] = true;
8421		
8422		if (update) {
8423			renderStores();
8424		} else {
8425			render();
8426		}
8427	}});
8428}
8429
8430function renderStores() {
8431	html['stores'] = '';
8432	
8433	if (state['stores'].addresses.length > 0) {
8434		var index = 0;
8435		for (var storeAddressesElement in state['stores'].addresses) {
8436			var storeAddress = state['stores'].addresses[storeAddressesElement];
8437			
8438			var streetAddress = storeAddress.address1;
8439			if (storeAddress.address2) {
8440				streetAddress += storeAddress.address2;
8441			}
8442			
8443			html['stores'] += templates['location']({
8444				id: index,
8445				name: storeAddress.fullName + ' ' + storeAddress.lastName,
8446				address: streetAddress,
8447				city: storeAddress.city,
8448				state: storeAddress.stateCode,
8449				postal: storeAddress.postalCode,
8450				phone: storeAddress.phone
8451			});
8452			
8453			index++;
8454		}
8455		
8456		$dialogContent.find('.no-results').hide();
8457		$dialogContent.find('.stores-list').html(html['stores']).show();
8458	} else {
8459		$dialogContent.find('.no-results').show();
8460		$dialogContent.find('.stores-list').hide();
8461	}
8462	
8463	
8464}
8465
8466function loadTemplates() {
8467	templates = {};
8468	
8469	$.ajax({ method: 'POST', url: Urls.getMoreStoresDialogTemplate, dataType: 'html', success: function (data) {
8470		templates['dialog'] = _.template(data, templateSettings);
8471		render();
8472	}});
8473	
8474	$.ajax({ method: 'POST', url: Urls.getMoreStoresLocationTemplate, dataType: 'html', success: function (data) {
8475		templates['location'] = _.template(data, templateSettings);
8476		render();
8477	}});
8478}
8479
8480function render(update) {
8481	if (ready['dialog'] && ready['stores'] && ready['states'] && templates['dialog'] && templates['location']) {
8482		$dialogContent.html(templates['dialog']());
8483		
8484		if (state.options && state.options.view) {
8485			$dialogContent.attr('view', state.options.view);
8486		} else {
8487			$dialogContent.removeAttr('view');
8488		}
8489		
8490		renderStores();
8491		renderStates();
8492	}	
8493}
8494
8495exports.initialize = initialize;
8496},{"./dialog":36,"lodash":133}],38:[function(require,module,exports){
8497'use strict';
8498
8499var ajax = require('./ajax'),
8500    util = require('./util');
8501/**
8502 * @function
8503 * @description Load details to a given gift certificate
8504 * @param {String} id The ID of the gift certificate
8505 * @param {Function} callback A function to called
8506 */
8507exports.checkBalance = function (id, callback) {
8508    // load gift certificate details
8509    var url = util.appendParamToURL(Urls.giftCardCheckBalance, 'giftCertificateID', id);
8510
8511    ajax.getJson({
8512        url: url,
8513        callback: callback
8514    });
8515};
8516
8517},{"./ajax":3,"./util":102}],39:[function(require,module,exports){
8518'use strict';
8519
8520var ajax = require('./ajax'),
8521    minicart = require('./minicart'),
8522    util = require('./util');
8523
8524var setAddToCartHandler = function (e) {
8525    e.preventDefault();
8526    var form = $(this).closest('form');
8527
8528    var options = {
8529        url: util.ajaxUrl(form.attr('action')),
8530        method: 'POST',
8531        cache: false,
8532        data: form.serialize()
8533    };
8534    $.ajax(options).done(function (response) {
8535        if (response.success) {
8536            ajax.load({
8537                url: Urls.minicartGC,
8538                data: {lineItemId: response.result.lineItemId},
8539                callback: function (response) {
8540                    minicart.show(response);
8541                    form.find('input,textarea').val('');
8542                }
8543            });
8544        } else {
8545            form.find('span.error').hide();
8546            for (var id in response.errors.FormErrors) {
8547                var $errorEl = $('#' + id).addClass('error').removeClass('valid').next('.error');
8548                if (!$errorEl || $errorEl.length === 0) {
8549                    $errorEl = $('<span for="' + id + '" generated="true" class="error" style=""></span>');
8550                    $('#' + id).after($errorEl);
8551                }
8552                $errorEl.text(response.errors.FormErrors[id].replace(/\\'/g, '\'')).show();
8553            }
8554        }
8555    }).fail(function (xhr, textStatus) {
8556        // failed
8557        if (textStatus === 'parsererror') {
8558            window.alert(Resources.BAD_RESPONSE);
8559        } else {
8560            window.alert(Resources.SERVER_CONNECTION_ERROR);
8561        }
8562    });
8563};
8564
8565exports.init = function () {
8566    $('#AddToBasketButton').on('click', setAddToCartHandler);
8567};
8568
8569},{"./ajax":3,"./minicart":45,"./util":102}],40:[function(require,module,exports){
8570'use strict';
8571
8572var giftCardBalanceInquiryComponent = require('../components/GiftCardBalanceInquiryComponent');
8573
8574exports.initializeAllFoundComponents = initializeAllFoundComponents;
8575
8576function initializeAllFoundComponents($containerParameter) {
8577	var $container = $containerParameter || $(document);
8578	
8579	var $balanceInquiryComponents = $container.find('.gift-card-balance-inquiry-component');
8580	if ($balanceInquiryComponents.length > 0) {
8581		$balanceInquiryComponents.each(function() {
8582			giftCardBalanceInquiryComponent.initialize($(this).parent());
8583		});
8584	}
8585}
8586},{"../components/GiftCardBalanceInquiryComponent":20}],41:[function(require,module,exports){
8587'use strict';
8588// jQuery extensions
8589
8590module.exports = function () {
8591    // params
8592    // toggleClass - required
8593    // triggerSelector - optional. the selector for the element that triggers the event handler. defaults to the child elements of the list.
8594    // eventName - optional. defaults to 'click'
8595    $.fn.toggledList = function (options) {
8596        if (!options.toggleClass) { return this; }
8597        var list = this;
8598        return list.on(options.eventName || 'click', options.triggerSelector || list.children(), function (e) {
8599            e.preventDefault();
8600            var classTarget = options.triggerSelector ? $(this).parent() : $(this);
8601            classTarget.toggleClass(options.toggleClass);
8602            // execute callback if exists
8603            if (options.callback) {options.callback();}
8604        });
8605    };
8606
8607    $.fn.syncHeight = function () {
8608		this.height('auto');
8609        var arr = $.makeArray(this);
8610        arr.sort(function (a, b) {
8611            return $(a).height() - $(b).height();
8612        });
8613        return this.height($(arr[arr.length - 1]).height());
8614    };
8615
8616	$.fn.syncHeightToMinimum = function () {
8617		this.height('auto');
8618        var arr = $.makeArray(this);
8619        arr.sort(function (a, b) {
8620            return $(a).height() - $(b).height();
8621        });
8622        return this.height($(arr[0]).height());
8623    };
8624
8625	$.fn.syncHeightToAverage = function () {
8626		this.height('auto');
8627        var arr = $.makeArray(this);
8628        arr.sort(function (a, b) {
8629            return $(a).height() - $(b).height();
8630        });
8631        return this.height($(arr[parseInt(arr.length/2, 10)]).height());
8632    };
8633};
8634
8635},{}],42:[function(require,module,exports){
8636'use strict';
8637
8638var _ = require('lodash');
8639var sliders = require('./sliders');
8640var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
8641
8642exports.init = function init () {
8643	slickSlideJumbotron();
8644
8645	// $('.jumbotron-ajax').ready(function(){
8646	// 	renderJumbotronSlot();
8647	// });
8648	
8649	var renderJumbotronSlot = function(){
8650		if (Urls && Urls.getJumbotronSlot) {
8651			$.ajax({ method: 'POST', url: Urls.getJumbotronSlot, dataType: 'html', success: function (data) {
8652				var html = '';
8653				var contentSlot;
8654				var isJson = false;
8655				
8656				var templateSettings = {
8657			    	interpolate: /{{([\s\S]+?)}}/g
8658			    };
8659				
8660				var template = _.template($('#template-jumbotron').html(), templateSettings);
8661				
8662				var dataPayload = data.substr(data.indexOf('{'), data.length);
8663				
8664				var endPayloadIndex = dataPayload.length;
8665				if (dataPayload.indexOf('<!-- dwMarker') > 0) {
8666					endPayloadIndex = dataPayload.indexOf('<!-- dwMarker');
8667				}
8668				dataPayload = dataPayload.substr(0, endPayloadIndex);
8669								
8670				try {
8671					contentSlot = JSON.parse(dataPayload);
8672					isJson = true;
8673				} catch (e) {
8674					console.warn('ContentSlot template not set to JSON');
8675				}
8676				
8677				if (contentSlot) {
8678					if (contentSlot.content) {
8679						html = template({ jumbotron: buildContentSlotFromJSON(contentSlot) });
8680					}
8681				} else if (!isJson) {
8682					html = template({ jumbotron: data });
8683				}
8684				
8685				$('.jumbotron-ajax').addClass('active').html(html);
8686				if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
8687					slickSlideJumbotron();
8688				}
8689				$(document).trigger('app.resize');
8690			}});
8691		}
8692	}
8693
8694	//fishtag rigging content asset 'cat-global-header'
8695	//$('.cat-global-header').ready(function(){
8696	//	renderFishtag();
8697	//});
8698	
8699	function renderFishtag(){
8700		if (Urls && Urls.getFishtag) {
8701			$.ajax({ method: 'POST', url: Urls.getFishtag, dataType: 'html', success: function(data){
8702				var html = '';
8703				var contentSlot;
8704				var isJson = false;
8705				
8706				try {
8707					contentSlot = JSON.parse(data.substr(data.indexOf('{'), data.length));
8708					isJson = true;
8709				} catch (e) {
8710					console.warn('ContentSlot template not set to JSON');
8711				}
8712				
8713				if (contentSlot && contentSlot.content) {
8714					html = buildContentSlotFromJSON(contentSlot);
8715				} else if (!isJson) {
8716					html = data;
8717				}
8718				
8719				$('.fishtag-slot').html(html);
8720                rotateText($('.top-banner-message'), $('.top-banner-message').data('duration'));
8721			}});
8722		}
8723	}
8724	
8725	function buildContentSlotFromJSON(contentSlotJSON) {
8726		var body = '';
8727		
8728		if (contentSlotJSON && contentSlotJSON.content) {
8729			for (var contentSlotItem in contentSlotJSON.content) {
8730				body += contentSlotJSON.content[contentSlotItem].body;
8731			}
8732		
8733			if (contentSlotJSON.countdownTo) {
8734				body = '<div class="countdown-wrapper" data-countdown-date="' + contentSlotJSON.countdownTo + '">' + body + '</div>'; 
8735			}
8736		}
8737		
8738		return body;
8739	}
8740	
8741	// Rotate Text for fishtag
8742	function rotateText($element, duration) {
8743		var lengthSlider 			= $element.find('.rotate-element').length;
8744		var $rotateElement 			= $element.find('.rotate-element');
8745
8746		setInterval(function() {
8747			var $rotateElementActive 	= $element.find('.active');
8748			var $nextElement = $rotateElementActive.next();
8749
8750			if($nextElement.index() === -1) {
8751				$nextElement = $element.find('.rotate-element:first-child');
8752
8753				$nextElement
8754					.addClass('active')
8755					.siblings()
8756					.removeClass('active');
8757			} else {
8758				$nextElement
8759					.addClass('active')
8760					.siblings()
8761					.removeClass('active');
8762			}
8763		}, duration);
8764	}
8765
8766	function slickSlideJumbotron() {
8767		var slidesMobile = $('.gbcolumns').closest('[data-slides-mobile]').length > 0 ? parseInt($('.gbcolumns').closest('[data-slides-mobile]').attr('data-slides-mobile')) : 1;
8768		var slidesDesktop = $('.gbcolumns').closest('[data-slides-desktop]').length > 0 ? parseInt($('.gbcolumns').closest('[data-slides-desktop]').attr('data-slides-desktop')) : 4;
8769	
8770		sliders.init('.gbcolumns', {dots: false, breakpoints: {0: {slidesToShow: slidesMobile, arrows: true}, 768: {slidesToShow: slidesDesktop}}});
8771	}
8772}
8773},{"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"./sliders":95,"lodash":133}],43:[function(require,module,exports){
8774'use strict';
8775
8776/* -------------------------
8777 * LOGIN
8778 * -------------------------
8779 */
8780
8781var loadingIndicator = require('./components/LoadingIndicator');
8782var dialog = require('./dialog');
8783var loginDialog = require('./loginDialog');
8784var recaptcha = require('./components/Recaptcha');
8785var tooltip = require('./tooltip');
8786var util = require('./util'); //util reference
8787var	validator = require('./validator');
8788
8789var GoogleTagManagerClient = require('../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
8790var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
8791var TPromise = require('promise');
8792
8793var dataBuffer = {};
8794var options = {};
8795var loaded = {};
8796var loadingIndicatorSettings = {
8797    IsLabelShown: false,
8798    CssClass: 'fullscreen',
8799    BackgroundColor: 'transparent'
8800};
8801var opcLoggedInCustomerAddresses = [];
8802
8803/**
8804 * @private
8805 * @function
8806 * @description init events for the loginPage
8807 */
8808function initLoginEvents() {
8809    //toggle the value of the rememberme checkbox
8810    $(document).on('change', '.login-rememberme input', function () {
8811        if ($(this).prop('checked')) {
8812            $('#rememberme').val('true');
8813        } else {
8814            $('#rememberme').val('false');
8815        }
8816    });
8817    $(document).on('click', '#optin-privacy-policy', function(e) {
8818        e.preventDefault();
8819        location.href = Urls.privacyPage;
8820    });
8821    $(document).on('click', '#reward-learn-more', function(e) {
8822        e.preventDefault();
8823        location.href = Urls.learnFAQ;
8824    });
8825    $(document).on('click', '.movetoaddr', function(e){
8826        e.preventDefault();
8827        $('html, body').animate({
8828            scrollTop: $('.billing-address-form').eq(0).offset().top
8829        }, 1000);
8830    });
8831    $(document).on('click', '#attach-rewards-form label', function() {
8832        $('#attach-rewards-form input').removeAttr('checked');
8833        $(this).siblings('[name="attach-rewards-options"]').attr('checked', 'checked');
8834    });
8835}
8836
8837function validateLoginForm($form, checkFields) {
8838    if (!$form || !checkFields) { return false }
8839
8840    var $emailField = $form.find('input[name*="_username"]');
8841    var $passwordField = $form.find('input[name*="_password"]');
8842    var emailValue = $emailField.length > 0 ? $emailField.val().trim() : null;
8843	var passwordValue = $passwordField.length > 0 ? $passwordField.val().trim() : null;
8844	var formValidation = true;
8845
8846    for (var i = 0; i < checkFields.length; i++) {
8847        var currentField = checkFields[i];
8848
8849        if (currentField.toUpperCase() == 'EMAIL' && (!emailValue || emailValue.length == 0)) {
8850            formValidation = false;
8851            $form.find('input[name*="_username"]').addClass('error');
8852        }
8853        if (currentField.toUpperCase() == 'PASSWORD' && (!passwordValue || passwordValue.length == 0)) {
8854            formValidation = false;
8855            $form.find('input[name*="_password"]').addClass('error');
8856        }
8857    }
8858
8859	if (!formValidation) {
8860        $form.find('.login-dialog-error .invalid-missing-fields').removeClass('d-none');
8861        return false;
8862    } else {
8863        $form.find('.login-dialog-error span').addClass('d-none');
8864        return true;
8865    }
8866}
8867
8868function errorHandling(data, dialogTitle, dialogHtml) {
8869    if (data && typeof data === 'object' && 'message' in data) {
8870        console.log(data.message);
8871    }
8872    dialog.open({
8873        html: dialogHtml,
8874        options: {
8875            title: dialogTitle,
8876            draggable: false,
8877            open: function(event, ui) {
8878                $('body').addClass('dialog-open');
8879            },
8880            close: function(event, ui) {
8881                $('body').removeClass('dialog-open');
8882            }
8883        }
8884    });
8885}
8886
8887// Cordial tracking for loginPage and loginDialog
8888function loginCordialAccountEvent($form, isLoginDialog) {
8889    var username = $form.find('input[name*="_username"]').val().trim();
8890
8891    var auth_data = {
8892        email: username
8893    }
8894
8895    var contact_data = {
8896        'channels': {
8897            'email': {
8898                'address': username
8899            }
8900        }
8901    };
8902
8903    var properties = {
8904        "email": username,
8905        "loginType": isLoginDialog ? "login-dialog" : "login"
8906    };
8907
8908    if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
8909        crdl([
8910            ['contact', auth_data, contact_data],
8911            ['event', 'login', properties]
8912        ]);
8913    }
8914}
8915
8916// Cordial password reset - no account event
8917function passwordResetNoAccountCordialEvent(container) {
8918    if (container && $(container).find('.reset-password .no-account').length > 0) {
8919        var email = $('.no-account').text();
8920
8921		var auth_data = {
8922			email: email
8923		}
8924		var contact_data = {
8925			'channels': {
8926			    'email': {
8927			        'address': email
8928			    }
8929			}
8930		};
8931
8932		var properties = {
8933			"email": email
8934		};
8935
8936		if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
8937			crdl([
8938				['contact', auth_data, contact_data],
8939				['event', 'reset-password-no-account', properties]
8940			]);
8941		}
8942	}
8943}
8944
8945function initializeRecaptchaForLogin() {
8946	renderRecaptchaForLogin();
8947	$(document).on('Account.SubmitLogin', function(event) {
8948		$('#accountLoginButton').click();
8949	});
8950}
8951
8952function renderRecaptchaForLogin() {
8953	if (loaded.Recaptcha && $('#accountLoginButton').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForAccountLogin && options.Recaptcha.GoogleSiteKey) {
8954		dataBuffer.LoginRecaptcha = grecaptcha.render('accountLoginButton', {
8955	        'sitekey': options.Recaptcha.GoogleSiteKey,
8956	        'callback': accountLogin,
8957	        'badge': 'bottomleft'
8958	    });
8959	}
8960}
8961
8962function renderRecaptchaForPasswordReset() {
8963	if (loaded.Recaptcha && $('#request-password-send-btn').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForPasswordReset && options.Recaptcha.GoogleSiteKey) {
8964		if (dataBuffer.ResetPasswordRecaptcha) {
8965			grecaptcha.reset(dataBuffer.ResetPasswordRecaptcha);
8966		}
8967
8968		dataBuffer.ResetPasswordRecaptcha = grecaptcha.render('request-password-send-btn', {
8969	        'sitekey': options.Recaptcha.GoogleSiteKey,
8970	        'callback': window.resetPassword,
8971	        'badge': 'bottomleft'
8972	    });
8973	}
8974}
8975
8976var updateCustomerDetails = function (form, name) {
8977    var $form = $(form);
8978
8979    return TPromise.resolve($.ajax({
8980        type: 'POST',
8981        url: util.ajaxUrl(Urls.onePageLogin),
8982        data: $form.serialize() + '&' + name + '=' + name
8983    }));
8984 }
8985
8986function showNotification(text, selector, msgType) {
8987    var $selector = $(selector);
8988	if ($selector.length == 0) {
8989        var $form = $('form.opc-login'),
8990            $selector = $('<span/>', {'class': 'login-notification', 'tabindex': '0'}).wrap('<div class="formfield clearfix error-box"><div class="form-row required"><div class="field-wrapper"></div></div></div>');
8991
8992        $selector.parent().parent().parent().insertBefore('form.opc-login .form-row-button');
8993        $selector = $(selector);
8994    }
8995
8996    $selector.removeClass('success error').addClass(msgType).text(text).focus();
8997}
8998
8999function clearNotification(selector) {
9000     if(selector.length){
9001        $('.formfield.error-box').remove();
9002		selector.removeClass('success error');
9003     }
9004}
9005
9006// GTM and Cordial tagging for cart opc-login
9007function opcLoginTagging($form, callback) {
9008    var email =  $form.find('input[name*="username"]').val();
9009    var auth_data = {
9010        email: email
9011    }
9012    var contact_data = {
9013        'channels': {
9014            'email': {
9015                'address': email
9016            }
9017        }
9018    };
9019    var properties = {
9020        "email": email
9021    };
9022    GoogleTagManagerClient.PushDataLayerForDataLayerType('CustomerLogin', { method: 'cart' }).then(function() {
9023        if (SitePreferences.CORDIAL_ENABLED) {
9024            if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
9025                crdl([
9026                    ['contact', auth_data, contact_data],['event', 'login', properties]
9027                ]);
9028            }
9029        }
9030        if (callback) callback();
9031    });
9032}
9033
9034/**
9035 * Login reporting - increments counters in custom object
9036 * @param {String} type - login type
9037 * @param {String} source - Source of login, which can be original, cart, cart-modal, or wishlist-modal
9038 */
9039function trackLoginActions(type, source) {
9040    if (!type || !source) {
9041        return;
9042    }
9043
9044    $.ajax({
9045        type: 'POST',
9046        url: Urls.loginTracking,
9047        contentType: 'application/json',
9048        data: JSON.stringify({
9049            type: type,
9050            source: source
9051        }),
9052        error: function(jqXHR, textStatus, errorThrown) {
9053            console.log('Login-TrackActions: Error in incrementing login action counters. ERROR TEXT: '+textStatus+', ERROR THROWN: '+errorThrown+'.');
9054        },
9055        success: function(response) {
9056            if (response) {}
9057        }
9058    });
9059}
9060
9061/**
9062 * @private
9063 * @function
9064 * @description init login button event for the loginPage, loginDialog and checkout login
9065 */
9066function initLoginButtons() {
9067	$(document).on('click', '#accountLoginButton', function(event) {
9068		event.preventDefault();
9069        var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with login. Please try again later.</p>';
9070        var $accountLoginButton = $(this);
9071        var $accountLoginForm = $accountLoginButton.closest('form#dwfrm_login');
9072        var formRememberMe = $accountLoginForm.find('input[name*="dwfrm_login_rememberme"]').is(':checked');
9073        var formUsername = formRememberMe ? '=' + $accountLoginForm.find('input[name*="dwfrm_login_username"]').val() : '';
9074        var formUrl = $accountLoginForm.attr('action');
9075
9076        // Clear previous form errors
9077        $accountLoginForm.find('input').removeClass('error');
9078        $accountLoginForm.find('.login-dialog-error span').addClass('d-none');
9079
9080        var isValidForm = validateLoginForm($accountLoginForm, ['email','password']);
9081        var isLoginDialog = formUrl.indexOf('loginDialog') >= 0;
9082        var formData = $accountLoginForm.serialize();
9083
9084        if (!isLoginDialog) {
9085            $('[data-login-source]').attr('data-login-source', 'original'); // For reporting
9086        }
9087
9088        // Using AJAX instead of default HTML button submit for login page and dialog
9089        if (isValidForm) {
9090            $('body').loading(loadingIndicatorSettings);
9091            $.ajax({
9092                type: 'POST',
9093                url: formUrl,
9094                data: formData,
9095                success: function(data, textStatus, jqXHR) {
9096                    if (typeof data === 'object' && 'result' in data) {
9097                        if (data.result == 'error') {
9098                            $('body').loading('destroy');
9099                            if (isLoginDialog) {
9100                                $accountLoginForm.find('.login-dialog-error .invali
9100d-login').removeClass('d-none');
9101                            } else {
9102                                window.location.href = data.loginDialogContinueUrl + '?retry';
9103                            }
9104                        } else if (data.result == 'success' || data.result == 'complete') {
9105                            $('body').loading('destroy');
9106                            // 'validateCustomer' property only returns in the data if this is a dialog. Otherwise, rewards validation happens through the URL using loginDialogContinueUrl.
9107                            // If this is a rewards customer, and the cusomter account requires validation...
9108                            if ('validateCustomer' in data && data.validateCustomer) {
9109                                if (SitePreferences.CORDIAL_ENABLED) {
9110                                    loginCordialAccountEvent($accountLoginForm, isLoginDialog);
9111                                }
9112                                var newAttributes = {
9113                                    "data-modal-type": "login",
9114                                    "data-continue-url": 'loginDialogContinueUrl' in data ? data.loginDialogContinueUrl : '',
9115                                    "data-first-name": 'customerFirstName' in data ? data.customerFirstName : '',
9116                                    "data-store-number": 'storeFromProfile' in data ? data.storeFromProfile : '',
9117                                    "data-cart-quantity": 'cartQuantity' in data ? data.cartQuantity : ''
9118                                }
9119                                $('.login-dialog-validate-customer').html(data.validateCustomer);
9120                                $('.login-dialog-validate-customer').addClass('validate-customer-show follow-account-dialog-path').attr(newAttributes);
9121                                // Do this in two places:
9122                                // (1) for login-dialog here because the element is only loaded into page via AJAX,
9123                                // (2) init of page load for regular rewards verification pages through normal login flow
9124                                initAddRulesForCodeInput();
9125                            } else {
9126                                // Fire the cordial login event
9127                                if (SitePreferences.CORDIAL_ENABLED) {
9128                                    loginCordialAccountEvent($accountLoginForm, isLoginDialog);
9129                                }
9130                                trackLoginActions('password', $('[data-login-source]').attr('data-login-source'));
9131
9132                                // Handle follow-up actions accordingly
9133                                $(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: data, isAccountDialog: isLoginDialog, displayMessage: 'login' });
9134                            }
9135                        } else {
9136                            $('body').loading('destroy');
9137                            errorHandling(data, 'Login - Error', dialogHtml);
9138                        }
9139                    } else {
9140                        $('body').loading('destroy');
9141                        if (isLoginDialog) {
9142                            errorHandling(data, 'Login - Error', dialogHtml);
9143                        } else {
9144                            window.location.href = location.href.split('?')[0]+'?retry';
9145                        }
9146                    }
9147                },
9148                error: function(jqXHR, textStatus, errorThrown) {
9149                    $('body').loading('destroy');
9150                    if (!isLoginDialog) {
9151                        window.location.href = location.href.split('?')[0]+'?retry';
9152                    }
9153                }
9154            });
9155        }
9156	});
9157
9158    $(document).on('click', '.opc-login-submit-button', function(event) {
9159        event.preventDefault();
9160        var $opcLoginButton = $(this);
9161        var $opcLoginForm = $opcLoginButton.closest('form.opc-login');
9162        var notificationSelector = $('.login-notification');
9163
9164        $('body').loading(loadingIndicatorSettings);
9165
9166        $('[data-login-source]').attr('data-login-source', 'cart'); // For reporting
9167        $opcLoginForm.validate().form();
9168        clearNotification(notificationSelector);
9169        updateCustomerDetails($opcLoginForm, $(this).attr('name')).then(function (response) {
9170            if (typeof response != 'object') {
9171                try {
9172                    response = JSON.parse(response);
9173                } catch(e) {
9174                    console.log(e);
9175                    response = {};
9176                }
9177            }
9178            if ('viewModel' in response && 'Customer' in response.viewM
9178odel && response.viewModel.Customer.authenticated == true) {
9179                Customer.authenticated = response.viewModel.Customer.authenticated;
9180                opcLoggedInCustomerAddresses = response.viewModel.Addresses;
9181                if ('validateCustomer' in response) {
9182                    $.ajax({
9183                        type: 'GET',
9184                        url: Urls.renderDialogTemplate,
9185                        error: function(jqXHR, textStatus, errorThrown) {
9186                            $('body').loading('destroy');
9187                            console.log(errorThrown);
9188                        },
9189                        success: function(data) {
9190                            if (data) {
9191                                $('body').loading('destroy');
9192                                opcLoginTagging($opcLoginForm);
9193                                var opcLoginContinueUrl = $('#cart-items-form').length ? Urls.onePage : Urls.account;
9194
9195                                dialog.open({
9196                                    html: data,
9197                                    options: {
9198                                        dialogClass: 'login-dialog',
9199                                        draggable: false,
9200                                        height: 600,
9201                                        open: function() {
9202                                            $('body').addClass('dialog-open');
9203                                            $(this).scrollTop(0);
9204                                            if (SitePreferences.CORDIAL_ENABLED) {
9205                                                loginCordialAccountEvent($opcLoginForm, false);
9206                                            }
9207
9208                                            $('.ui-dialog.login-dialog').css('height', '430px');
9209                                            $('.ui-dialog.login-dialog .login-dialog-forms-container').css('margin', '0 -8px');
9210                                            $('.login-dialog-register-container').hide();
9211
9212                                            var newAttributes = {
9213                                                "data-modal-type": "login",
9214                                                "data-continue-url": opcLoginContinueUrl,
9215                                                "data-first-name": 'customerFirstName' in response ? response.customerFirstName : '',
9216                                                "data-store-number": 'storeFromProfile' in response ? response.storeFromProfile : '',
9217                                                "data-cart-quantity": 'cartQuantity' in response ? response.cartQuantity : '',
9218                                                "data-opc-login": true
9219                                            }
9220                                            $('.login-dialog-validate-customer').html(response.validateCustomer);
9221                                            $('.login-dialog-validate-customer').addClass('validate-customer-show follow-account-dialog-path').attr(newAttributes);
9222
9223                                            initAddRulesForCodeInput();
9224
9225                                            var $passwordReset = $(this).find('.login-dialog-forms-container #password-reset-button');
9226                                            $passwordReset.attr('data-target', $passwordReset.attr('data-target') + '?loginDialog=true');
9227                                        },
9228                                        close: function() {
9229                                            $('body').removeClass('dialog-open');
9230                                        }
9231                                    }
9232                                });
9233                            }
9234                        }
9235                    });
9236                } else {
9237                    $('body').loading('destroy');
9238                    opcLoginTagging($opcLoginForm);
9239                    trackLoginActions('password', $('[data-login-source]').attr('data-login-source'));
9240                    location.href = Urls.onePage;
9241                }
9242            } else {
9243                $('body').loading('destroy');
9244                $('.opc-login .field-wrapper input').addClass('error');
9245                showNotification(Resources.PWD_EMAIL_ERROR, '.login-notification', 'error');
9246            }
9247        }.bind(this));
9248    });
9249
9250	// OwnID OTP flow
9251	$(document).on('click', '#ownid-otp', function(event) {
9252		event.preventDefault();
9253		$('input[type="password"]').val('');	// clears out password
9254
9255		// Clear errors and re-validate
9256		var $accountLoginButton = $(this);
9257		var $accountLoginForm = $accountLoginButton.closest('form#dwfrm_login');
9258		$accountLoginForm.find('input').removeClass('error');
9259		$accountLoginForm.find('.login-dialog-error span').addClass('d-none');
9260
9261		var isValidEmail = validateLoginForm($accountLoginForm, ['email']);
9262		if (isValidEmail) {
9263			window.ownidStartOtp();
9264		}
9265	});
9266
9267	// OwnID passkey tooltip
9268	tooltip.init({
9269		items: '.tooltip'
9270	});
9271}
9272
9273function callNextController(url, callback) {
9274    $.ajax({
9275        type: 'GET',
9276        url: url,
9277        contentType: 'application/json',
9278        error: function(jqXHR, textStatus, errorThrown) {
9279        },
9280        success: function(response) {
9281            if (response) {
9282                if (callback) callback(response);
9283            }
9284        }
9285    });
9286}
9287
9288/**
9289 * @private
9290 * @function
9291 * @description register on-click event for rewards validation > send code button
9292 * @notes changed the button.on('click') to a document.on('click') for HTML that is dynamically loaded in page with AJAX well after initial page load
9293 */
9294function initValidateRewardsAccount() {
9295	$(document).on('click', '#validate-account button#send-code', function(event) {
9296		event.preventDefault();
9297
9298        var $validateAccountForm = $('#validate-account');
9299        var isAccountDialog = $('.login-dialog-validate-customer').hasClass('follow-account-dialog-path');
9300        var sendType = $validateAccountForm.find('input[name=validate-account]:checked').attr('id').indexOf('none') > -1 ? 'none' : 'other';
9301
9302		if (sendType == "none") {
9303			$.ajax({
9304				type: 'post',
9305				url: Urls.createRewardsAccount,
9306				contentType: 'application/json',
9307				data: JSON.stringify({
9308					firstName: $('.customer-account .first-name').text(),
9309					lastName: $('.customer-account .last-name').text(),
9310					emailAddress: $('.customer-account .email-address').text(),
9311					phoneNumber: $('.customer-account .phone-number').text(),
9312					zipCode: $('.customer-account .zip-code').text(),
9313					birthDate: $('.customer-account .birth-date').text(),
9314					autoEnroll: false
9315				}),
9316				success: function (data) {
9317					console.log('Rewards account created.');
9318					initVerificationDirection();
9319				}
9320			});
9321		} else {
9322			$.ajax({
9323				url: Urls.sendValidationCode,
9324				dataType: 'JSON',
9325				method: 'POST',
9326				data: {
9327					'sendMethod': $validateAccountForm.find('input[name=validate-account]:checked').val(),
9328					'sendTo': $validateAccountForm.find('input[name=validate-account]:checked').attr('data-value')
9329				},
9330				success : function(data) {
9331					var resultStatus = 'status' in data && data.status != 'pending' ? true : 'result' in data && 'status' in data.result && data.result.status != 'pending' ? true : false;
9332					if (('error' in data && data.error) || resultStatus) {
9333						$('.further-info').hide();
9334						$('.verify-form').hide();
9335						$('.verify-fail').removeClass('hidden');
9336					}
9337					$('.account-recognized').hide();
9338					if (isAccountDialog) {
9339						$('.verify-account').removeClass('hidden').addClass('validate-customer-show');
9340					} else {
9341						$('.verify-account').removeClass('hidden');
9342					}
9343					$('.' + sendType + '-method').removeClass('hidden');
9344				},
9345				complete: function(data, status, xhr) {
9346					if (data.status == 302 && data.responseText) {
9347						location.href = data.responseText;
9348					}
9349				}
9350			});
9351		}
9352	});
9353
9354    $(document).on('click', '#attach-rewards-button', function(event) {
9355        event.preventDefault();
9356        var errorMessage = '<p style="padding: 10px;">Sorry, we were unable to finalize your rewards account updates.</p>';
9357        var $attachRewardsForm = $('#attach-rewards-form');
9358        var $selectedOption = $attachRewardsForm.find('input[name=attach-rewards-options]:checked');
9359        var submittedEmail = $('.login-dialog-passcode-container, .otp-login-form-container').attr('data-email');
9360
9361        if (!$selectedOption) {
9362            $('.attach-rewards-form-errors').removeClass('d-none');
9363            return;
9364        }
9365
9366        $.ajax({
9367            url: Urls.attachRewards,
9368            dataType: 'JSON',
9369            method: 'POST',
9370            data: {
9371                'option': $selectedOption.val(),
9372                'email': submittedEmail
9373            },
9374            success : function(data) {
9375                if (data && data.result && data.result == 'success') {
9376                    console.log('Rewards attachment complete.');
9377                    initVerificationDirection();
9378                } else {
9379                    errorHandling(data, 'Rewards Account - Error', errorMessage);
9380                }
9381            },
9382            error: function(jqXHR, textStatus, errorThrown) {
9383                errorHandling(data, 'Rewards Account - Error', errorMessage);
9384            }
9385        });
9386    });
9387}
9388
9389/**
9390 * @private
9391 * @function
9392 * @description register on-click event for rewards validation > verify code button
9393 */
9394function initVerifyCodeSent() {
9395    $(document).on('click', '#verify-code button#verify-user', function(event) {
9396        event.preventDefault();
9397
9398        var $verifyCodeForm = $('#verify-code');
9399        if (!$verifyCodeForm.valid()) {
9400            return false;
9401        }
9402
9403        $.ajax({
9404            url: Urls.verifySubmittedCode,
9405            dataType: 'JSON',
9406            method: 'POST',
9407            data: {
9408                'code':  $verifyCodeForm.find('input[name=enter-code]', '#verify-code').val(),
9409                'sendTo': $('#validate-account').find('input[name=validate-account]:checked').attr('data-value')
9410            },
9411            success : function(data) {
9412                if (('error' in data && data.error) || ('status' in data && data.status != 'approved')) {
9413                    $('.further-info').hide();
9414                    $('.verify-form').hide();
9415                    $('.verify-fail').removeClass('hidden');
9416                } else {
9417                    console.log('Rewards verification complete.');
9418                    initVerificationDirection();
9419                }
9420            },
9421            complete: function(data, status, xhr) {
9422                if (data.status == 302 && data.responseText) {
9423                    location.href = data.responseText;
9424                }
9425            }
9426        });
9427    });
9428}
9429
9430/**
9431 * @private
9432 * @function
9433 * @description directs the page load or the page redraw after rewards verification screen
9434 */
9435function initVerificationDirection() {
9436    var $validateDialog = $('.login-dialog-validate-customer');
9437    var isAccountDialog = $('.login-dialog-validate-customer').hasClass('follow-account-dialog-path');
9438    var dataModalType, dataNextControllerUrl, dataContinueUrl, dataFirstName, dataStore, dataCartQuantity, isOpcLogin;
9439
9440    if ($validateDialog.length) {
9441        dataModalType = $validateDialog.attr('data-modal-type');
9442        dataNextControllerUrl = $validateDialog.attr('data-next-controller-url');
9443        dataContinueUrl = $validateDialog.attr('data-continue-url');
9444        dataFirstName = $validateDialog.attr('data-first-name');
9445        dataStore = $validateDialog.attr('data-store-number');
9446        dataCartQuantity = $validateDialog.attr('data-cart-quantity');
9447        isOpcLogin = $validateDialog.attr('data-opc-login') == 'true';
9448
9449        if (isOpcLogin) {
9450            dialog.close();
9451            location.href = Urls.onePage;
9452        }
9453        if (isAccountDialog) {
9454            if (dataNextControllerUrl) {
9455                callNextController(dataNextControllerUrl, function(response) {
9456                    if (response && 'result' in response && (response.result == 'success' || response.result == 'complete')) {
9457                        response.customerFirstName = dataFirstName;
9458                        response.storeFromProfile = dataStore;
9459                        response.cartQuantity = dataCartQuantity;
9460                        // Handle follow-up actions accordingly
9461                        $(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: response, isAccountDialog: isAccountDialog, displayMessage: dataModalType });
9462                    }
9463                });
9464            } else {
9465                if (dataContinueUrl) {
9466                    location.href = dataContinueUrl;
9467                } else {
9468                    location.href = Urls.account;
9469                }
9470            }
9471        }
9472    } else {
9473        location.href = Urls.account;
9474    }
9475}
9476
9477/**
9478 * @private
9479 * @function
9480 * @description add rules to code input field
9481 */
9482function initAddRulesForCodeInput() {
9483    $('form#verify-code').validate();
9484    $('#verify-code #enter-code').rules('add', {
9485        required: true,
9486        digits: true,
9487        minlength: 6,
9488        messages: {
9489            required: Resources.VALIDATE_CODE,
9490            minlength: Resources.VALIDATE_CODE
9491        },
9492        normalizer: function(value) {
9493            return $.trim(value);
9494        }
9495    });
9496}
9497
9498/**
9499 * @private
9500 * @function
9501 * @description register on-click event for rewards validation > verify code > resend code link
9502 */
9503function initResendValidationCode() {
9504    $(document).on('click', '.verify-account a.resend-code', function(event) {
9505        event.preventDefault();
9506        if ($('.login-dialog-validate-customer').hasClass('follow-account-dialog-path')) {
9507            $('.verify-account, .further-info span').addClass('hidden');
9508            $('.account-recognized').css('left','-9999px').removeClass('hidden').show().addClass('validate-customer-show');
9509        } else {
9510            location.reload();
9511        }
9512    });
9513}
9514
9515/**
9516 * @private
9517 * @function
9518 * @description register on-click events for reset-password flow
9519 */
9520function initResetPasswordEvents() {
9521    // Show reset password form
9522	$(document).on('click', '#password-reset-button', function(event) {
9523        event.preventDefault();
9524        var $resetPasswordButton = $(this);
9525        var resetDialogUrl = $resetPasswordButton.attr('data-target');
9526        var isLoginDialog = resetDialogUrl.indexOf('loginDialog') >= 0;
9527
9528        if (isLoginDialog) {
9529            $('body').loading(loadingIndicatorSettings);
9530            var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with Password Reset. Please try again later.</p>';
9531
9532            $.ajax({
9533                type: 'GET',
9534                url: resetDialogUrl,
9535                success: function(data, textStatus, jqXHR) {
9536                    if (typeof data === 'object' && 'result' in data) {
9537                        if (data.result == 'complete') {
9538                            $('body').loading('destroy');
9539                            // If we received HTML in the data for displaying the reset password form, then dis
9539play it...
9540                            if ('resetPasswordForm' in data && data.resetPasswordForm) {
9541                                $('.login-dialog-reset-password').html(data.resetPasswordForm);
9542                                $('.login-dialog-reset-password').addClass('reset-password-show');
9543                                $('.login-dialog-reset-password').find('#PasswordResetForm').attr('action', $('.login-dialog-reset-password').find('#PasswordResetForm').attr('action') + '?loginDialog=true');
9544								renderRecaptchaForPasswordReset();
9545                            } else {
9546                                $('body').loading('destroy');
9547                                errorHandling(data, 'Reset Password - Error', dialogHtml);
9548                            }
9549                        } else {
9550                            $('body').loading('destroy');
9551                            errorHandling(data, 'Reset Password - Error', dialogHtml);
9552                        }
9553                    } else {
9554                        $('body').loading('destroy');
9555                        if (isLoginDialog) {
9556                            errorHandling(data, 'Reset Password - Error', dialogHtml);
9557                        } else {
9558                            window.location.href = location.href;
9559                        }
9560                    }
9561                },
9562                error: function(jqXHR, textStatus, errorThrown) {
9563                    $('body').loading('destroy');
9564                    if (!isLoginDialog) {
9565                        window.location.href = location.href;
9566                    }
9567                }
9568            });
9569        } else if (SitePreferences.RESET_POPUP_ENABLED) {
9570			dialog.open({
9571	            url: resetDialogUrl,
9572	            options: {
9573	            	dialogClass: 'resetpwdpopup',
9574	            	width: 385,
9575	                open: function () {
9576						renderRecaptchaForPasswordReset();
9577	                    $(".reset-password-heading-close-btn, .ui-widget-overlay").on('click',function(){
9578	                        $("button.ui-dialog-titlebar-close").click();
9579	                    });
9580	                }
9581	            }
9582			});
9583        } else {
9584            window.location.href = resetDialogUrl;
9585        }
9586	});
9587
9588    // Submit reset password form
9589    $(document).on('click.ResetPassword', '#request-password-send-btn, [name$="_requestpassword_send"]', function(event) {
9590    	if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForPasswordReset) {
9591			resetPassword(event, $(this));
9592		}
9593	});
9594
9595    $(document).on('Account.ResetPassword', function (event, data) {
9596		resetPassword(event, null, true);
9597	});
9598}
9599
9600function resetPassword(event, $resetPasswordSendButton, isRecaptchaEvent) {
9601	if (!$resetPasswordSendButton) {
9602		$resetPasswordSendButton = $('#request-password-send-btn');
9603	}
9604
9605	var $resetPasswordSendForm = $resetPasswordSendButton.closest('form#PasswordResetForm');
9606	var isLoginDialog = $resetPasswordSendForm.attr('action').indexOf('loginDialog') >= 0;
9607
9608	// Try to prevent the form submission as quickly as possible before doing all of the other logic
9609	if (isLoginDialog || (SitePreferences.RESET_POPUP_ENABLED && dialog.isActive())) {
9610		event.preventDefault();
9611	}
9612
9613	validator.init();
9614	var isValidForm = $resetPasswordSendForm.valid();
9615
9616	if (isValidForm) {
9617		if (isLoginDialog) {
9618			$('body').loading(loadingIndicatorSettings);
9619			var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with Password Reset. Please try again later.</p>';
9620			var formData = $resetPasswordSendForm.serialize();
9621			formData += '&' + $resetPasswordSendButton.attr('name') + '=' + $resetPasswordSendButton.attr('name');
9622
9623			$.ajax({
9624				type: 'POST',
9625				url: $resetPasswordSendForm.attr('action'),
9626				data: formData,
9627				success: function(data, textStatus, jqXHR) {
9628					if (typeof data === 'object' && 'result' in data) {
9629						if (data.result == 'complete') {
9630							$('body').loading('destroy');
9631							// If reset password email was sent, show confirmation within the dialog
9632							if ('resetPasswordConfirmation' in data && data.resetPasswordConfirmation) {
9633								$('.login-dialog-reset-password').html(data.resetPasswordConfirmation);
9634								$('.login-dialog-reset-password').addClass('reset-password-show');
9635								passwordResetNoAccountCordialEvent('.login-dialog-reset-password');
9636							} else {
9637								$('body').loading('destroy');
9638								errorHandling(data, 'Reset Password - Error', dialogHtml);
9639							}
9640						} else {
9641							$('body').loading('destroy');
9642							errorHandling(data, 'Reset Password - Error', dialogHtml);
9643						}
9644					} else {
9645						$('body').loading('destroy');
9646						if (isLoginDialog) {
9647							errorHandling(data, 'Reset Password - Error', dialogHtml);
9648						} else {
9649							window.location.href = location.href;
9650						}
9651					}
9652				},
9653				error: function(jqXHR, textStatus, errorThrown) {
9654					$('body').loading('destroy');
9655					if (!isLoginDialog) {
9656						window.location.href = location.href;
9657					}
9658				}
9659			});
9660		} else if (SitePreferences.RESET_POPUP_ENABLED && dialog.isActive()) {
9661			dialog.submit($resetPasswordSendButton.attr('name'), function() {
9662				passwordResetNoAccountCordialEvent('.resetpwdpopup');
9663			});
9664		} else if (isRecaptchaEvent) {
9665			$resetPasswordSendForm.append('<input type="hidden" name="' + $resetPasswordSendButton.attr('name') + '" value="' + $resetPasswordSendButton.val() + '"/>')
9666			$resetPasswordSendForm.submit();
9667		}
9668	}
9669}
9670
9671module.exports = {
9672    init: function (optionsParameter) {
9673		options = optionsParameter;
9674
9675        initLoginEvents();
9676		if (options && options.Recaptcha && (options.Recaptcha.EnabledForAccountLogin || options.Recaptcha.EnabledForPasswordReset)) {
9677			$(document).on('Recaptcha.Loaded', function(event) {
9678				if (!loaded.Recaptcha) {
9679					loaded.Recaptcha = true;
9680					renderRecaptchaForLogin();
9681					renderRecaptchaForPasswordReset();
9682				}
9683			});
9684
9685			recaptcha.initialize();
9686			initializeRecaptchaForLogin();
9687			renderRecaptchaForPasswordReset();
9688		}
9689		initLoginButtons();
9690        initValidateRewardsAccount();
9691        initVerifyCodeSent();
9692        initResendValidationCode();
9693        initAddRulesForCodeInput();
9694        initResetPasswordEvents();
9695    }
9696};
9697},{"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"./components/LoadingIndicator":25,"./components/Recaptcha":29,"./dialog":36,"./loginDialog":44,"./tooltip":101,"./util":102,"./validator":103,"promise":134}],44:[function(require,module,exports){
9698'use strict';
9699
9700var dialog = require('./dialog');
9701
9702/* -------------------------
9703 * LOGIN MODAL
9704 * -------------------------
9705 */
9706
9707var loginDialog = {
9708    /**
9709     * @function
9710     * @description Shows the account-login form in a dialog
9711     */
9712    show: function(callback, controller, action, productID, params, completeUrl) {
9713        $.ajax({
9714            type: 'POST',
9715            url: Urls.loginDialog,
9716            data: JSON.stringify({
9717                controller: controller,
9718                action: action,
9719                passedQueryString: params
9720            }),
9721            contentType: 'application/json',
9722            error: function(jqXHR, textStatus, errorThrown) {
9723                console.log(errorThrown);
9724            },
9725            success: function(response) {
9726				if (response) {
9727
9728                    dialog.open({
9729                        html: response,
9730                        options: {
9731                            dialogClass: 'login-dialog',
9732                            draggable: false,
9733                            open: function() {
9734                                $('body').addClass('dialog-open');
9735                                $(this).scrollTop(0);
9736                                $(this).find('.login-dialog-forms-container').attr('data-controller', controller).attr('data-action', action).attr('data-product', productID);
9737                                $(this).find('.login-dialog-forms-container form').each(function(i, form) {
9738                                    var actionUrl = $(form).attr('action') + '?loginDialog=true';
9739                                    $(form).attr('action', actionUrl);
9740                                });
9741                                $(this).find('.login-dialog-validate-customer').attr('data-next-controller-url', completeUrl);
9742                                var $passwordReset = $(this).find('.login-dialog-forms-container #password-reset-button');
9743                                $passwordReset.attr('data-target', $passwordReset.attr('data-target') + '?loginDialog=true');
9744                                if (callback) {
9745                                    callback();
9746                                }
9747                            },
9748                            close: function() {
9749                                $('body').removeClass('dialog-open');
9750                            }
9751                        }
9752                    });
9753                }
9754			}
9755		});
9756    },
9757    /**
9758     * @function
9759     * @description Closes the account-login dialog
9760     */
9761    close: function() {
9762        dialog.close();
9763    }
9764}
9765
9766module.exports = loginDialog;
9767},{"./dialog":36}],45:[function(require,module,exports){
9768'use strict';
9769
9770var util = require('./util'),
9771    bonusProductsView = require('./bonus-products-view');
9772
9773var timer = {
9774    id: null,
9775    clear: function () {
9776        if (this.id) {
9777            window.clearTimeout(this.id);
9778            delete this.id;
9779        }
9780    },
9781    start: function (duration, callback) {
9782        this.id = setTimeout(callback, duration);
9783    }
9784};
9785
9786var minicart = {
9787    init: function () {
9788        this.$el = $('#mini-cart');
9789        this.$content = this.$el.find('.mini-cart-content');
9790
9791        $('.mini-cart-product').eq(0).find('.mini-cart-toggle').addClass('fa-caret-down');
9792        $('.mini-cart-product').not(':first').addClass('collapsed')
9793            .find('.mini-cart-toggle').addClass('fa-caret-right');
9794
9795        $('.mini-cart-toggle').on('click', function () {
9796            $(this).toggleClass('fa-caret-down fa-caret-right');
9797            $(this).closest('.mini-cart-product').toggleClass('collapsed');
9798        });
9799
9800        // events
9801        this.$el.find('.mini-cart-total').on('mouseenter', function () {
9802            if (this.$content.not(':visible')) {
9803                this.slide();
9804            }
9805        }.bind(this));
9806
9807        this.$content.on('mouseenter', function () {
9808            timer.clear();
9809        }).on('mouseleave', function () {
9810            timer.clear();
9811            timer.start(30, this.close.bind(this));
9812        }.bind(this));
9813        
9814        $(document).on('minicart:update', function(){
9815        	//implement minicart update here
9816        });
9817    },
9818    /**
9819     * @function
9820     * @description Shows the given content in the mini cart
9821     * @param {String} A HTML string with the content which will be shown
9822     */
9823    show: function (html) {
9824        this.$el.html(html);
9825        util.scrollBrowser(0);
9826        this.init();
9827        this.slide();
9828        bonusProductsView.loadBonusOption();
9829    },
9830    /**
9831     * @function
9832     * @description Slides down and show the contents of the mini cart
9833     */
9834    slide: function () {
9835        timer.clear();
9836        // show the item
9837        this.$content.slideDown('slow');
9838        // after a time out automatically close it
9839        timer.start(6000, this.close.bind(this));
9840    },
9841    /**
9842     * @function
9843     * @description Closes the mini cart with given delay
9844     * @param {Number} delay The delay in milliseconds
9845     */
9846    close: function (delay) {
9847        timer.clear();
9848        this.$content.slideUp(delay);
9849    }
9850};
9851
9852module.exports = minicart;
9853
9854},{"./bonus-products-view":6,"./util":102}],46:[function(require,module,exports){
9855'use strict';
9856
9857var util = require('./util');
9858
9859var page = {
9860    title: '',
9861    type: '',
9862    params: util.getQueryStringParams(window.location.search.substr(1)),
9863    redirect: function (newURL) {
9864        setTimeout(function () {
9865            window.location.href = newURL;
9866        }, 0);
9867    },
9868    refresh: function () {
9869        setTimeout(function () {
9870            window.location.assign(window.location.href);
9871        }, 500);
9872    }
9873};
9874
9875module.exports = page;
9876
9877},{"./util":102}],47:[function(require,module,exports){
9878'use strict';
9879
9880var util = require('../../util');
9881
9882var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
9883
9884var $authenticationContainer = $('.authentication-form-container');
9885var $authenticationForm = $authenticationContainer.closest('form');
9886var $authenticationEmailAddress = $authenticationForm.find('input[name="AuthenticationEmailAddress"]');
9887var authenticationValidator;
9888
9889exports.init = function(options) {
9890	$authenticationContainer.on('click', 'input[type="submit"]', function (event) {
9891    	event.preventDefault();
9892    	
9893	    authenticationValidator = $authenticationForm.validate();
9894	    $authenticationEmailAddress.rules('add', ValidationRules.Email);
9895	    authenticationValidator.form();
9896	    
9897	    if (authenticationValidator.valid()) {
9898	    	$('<input />').attr('type', 'hidden').attr('name', 'isMobile').attr('value', $('html.size-mobile').length > 0).appendTo($authenticationForm);
9899	    	$authenticationForm.submit();
9900	    }	
9901    });
9902};
9903},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../util":102}],48:[function(require,module,exports){
9904'use strict';
9905
9906var AddressAutocompleteDialog;
9907var AddressVerificationDialog;
9908var AddressVerificationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper');
9909var	dialog = require('../dialog');
9910var	findMoreStores = require('../findMoreStores');
9911var	orderStatusForm = require('../components/OrderStatusForm');
9912var	giftcert = require('../giftcert');
9913var	page = require('../page');
9914var recaptcha = require('../components/Recaptcha');
9915var	tooltip = require('../tooltip');
9916var	util = require('../util');
9917var	validator = require('../validator');
9918
9919var PaymentMethodConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
9920var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
9921
9922var dataBuffer = {};
9923var options = {};
9924var loaded = {};
9925
9926/**
9927 * @function
9928 * @description Initializes the events on the address form (apply, cancel, delete)
9929 * @param {Element} form The form which will be initialized
9930 */
9931function initializeAddressForm() {
9932    var $addressEditForm = $('#edit-address-form');
9933	$addressEditForm.on('click', '.submit-shipping', function (e) {
9934        e.preventDefault();
9935
9936		var addressType = $addressEditForm.attr('address-type') ? $addressEditForm.attr('address-type') : 'saved';
9937		var addressDeliverable = function () {
9938			$addressEditForm.submit();
9939		}
9940		var addressNotDeliverable = function (addressVerifyData) {
9941			AddressVerificationDialog.initialize(options, addressType, addressVerifyData);			
9942		}
9943		var country = $addressEditForm.find('[id*="_country"]').val();
9944		if (options && options.Lob && options.Lob.AddressVerification.Enabled && country.toUpperCase() == 'US' && $addressEditForm.find('.submit-shipping').attr('verified') != 'true') {
9945			AddressVerificationHelper.VerifyAddress($addressEditForm, options).then(addressDeliverable, addressNotDeliverable);
9946		} else {
9947			addressDeliverable();
9948		}
9949    });
9950
9951	if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
9952		$(document).on('keyup.AddressAutocomplete', '#edit-address-form input[type="text"]', function (event) {
9953			var $this = $(this);
9954			var formValidator = $addressEditForm.validate();
9955
9956			if (event.keyCode == 8 || event.keyCode == 46) {
9957				$this.parent().find('.address-autocomplete-dialog').remove();
9958			} else if (!formValidator.checkForm() && ($this.attr('id').indexOf('_address1') >= 0 || $this.attr('id').indexOf('_address2') >= 0 || $this.attr('id').indexOf('_city') >= 0 || $this.attr('id').indexOf('_postal') >= 0)) {
9959				var addressAutocompleteHandler = function (addressAutocompleteData) {
9960					AddressAutocompleteDialog.initialize(options, addressAutocompleteData);
9961				}
9962			
9963				AddressVerificationHelper.AutocompleteAddress($addressEditForm, $this, options).then(addressAutocompleteHandler);
9964			} else {
9965				$this.parent().find('.address-autocomplete-dialog').remove();
9966			}
9967		});
9968		
9969		$(document).on('SetAddressFromAddressAutocomplete', function (event, data) {
9970			AddressVerificationHelper.SetAddressFromAddressVerificationDialog(data.$form, data.Address);
9971		});
9972	}
9973
9974	if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
9975		$(document).on('SetAddressFromAddressVerification', function (event, addressData) {
9976			AddressVerificationHelper.SetAddressFromAddressVerificationDialog($addressEditForm, addressData);
9977			$addressEditForm.submit();
9978		});
9979	}
9980
9981    validator.init();
9982}
9983/**
9984 * @private
9985 * @function
9986 * @description Toggles the list of Orders
9987 */
9988function toggleFullOrder () {
9989    $('.order-items')
9990        .find('li.hidden:first')
9991        .prev('li')
9992        .append('<a class="toggle">View All</a>')
9993        .children('.toggle')
9994        .click(function () {
9995            $(this).parent().siblings('li.hidden').show();
9996            $(this).remove();
9997        });
9998}
9999/**
10000 * @private
10001 * @function
10002 * @description Binds the events on the address form (edit, create, delete)
10003 */
10004function initAddressEvents() {
10005    $(document).ready(function(){
10006    	if ( $('.address-field-container form select').length > 0 ) {
10007        	$('.address-field-container form select').each(function(){
10008        		$(this).selectmenu({});
10009        	});
10010        }
10011    	
10012    	$("#dwfrm_profile_customer_berewarded").on("click",function(){
10013        	if ( $(this).is(':checked') ) {
10014        		$('.rewardsNumberContainer').slideDown();
10015        	} else {
10016        		$('.rewardsNumberContainer').slideUp();
10017        	}
10018    	});
10019
10020		if ($('#edit-address-form').length > 0) {
10021			initializeAddressForm();
10022		}
10023    });
10024    
10025    $(window).on('load', function() {
10026    	$('#primary #edit-address-form input[name="dwfrm_profile_address_remove"]').off('click');
10027    	$('#primary #edit-address-form input[name="dwfrm_profile_address_remove"]').on('click', function(e){
10028    		e.preventDefault();
10029    		var $form = $(this).parents('form');
10030            if (window.confirm(String.format(Resources.CONFIRM_DELETE, Resources.TITLE_ADDRESS))) {
10031                var url = util.appendParamsToUrl(Urls.deleteAddress, {
10032                    AddressID: $form.find('#addressid').val(),
10033                    format: 'ajax'
10034                });
10035                $.ajax({
10036                    url: url,
10037                    method: 'POST',
10038                    dataType: 'json'
10039                }).done(function (data) {
10040                    if (data.status.toLowerCase() === 'ok') {
10041                        dialog.close();
10042                        var qParams = util.getQueryStringParams(window.location.search.substr(1));
10043                        if(qParams.cid !== null && qParams.cid == 'billing'){
10044                        	location.href = Urls.paymentsList;
10045                        } else {
10046                        	location.href = Urls.addressesList;
10047                        }                      
10048                    } else if (data.message.length > 0) {
10049                        window.alert(data.message);
10050                        return false;
10051                    } else {
10052                        dialog.close();
10053                        location.href = Urls.addressesList;
10054                    }
10055                });
10056            }
10057    	});
10058    });
10059}
10060/**
10061 * @private
10062 * @function
10063 * @description Binds the events of the payment methods list (delete card)
10064 */
10065function initPaymentEvents() {
10066	var $creditCardTypeSelect = $('select[id*="creditcard_type"]');
10067	
10068	// Remove dialog payment add/edit implementation to use page redirect instead
10069    /*$('.add-card').on('click', function (e) {
10070        e.preventDefault();
10071        dialog.open({
10072            url: $(e.target).attr('href')
10073        });
10074    });*/
10075
10076	$creditCardTypeSelect.off('change.CreditCardTypeDetection').on('change.CreditCardTypeDetection', function (e) {
10077		var $this = $(this);
10078		var paymentInstrumentType = $this.val();
10079		var $allianceDataFieldDelta = $('input[name*="_owner"], select[name*="_month"], select[name*="_year"], label.expiration-date');
10080		var $allianceDataDisplayDelta = $('label.expiration-date');
10081		
10082	    switch (paymentInstrumentType) {
10083			case PaymentMethodConstants.AllianceData:
10084				$allianceDataFieldDelta.removeClass('required').closest('.formfield').hide();
10085				$allianceDataDisplayDelta.hide();
10086				break;
10087				
10088			default:
10089				$allianceDataFieldDelta.addClass('required').closest('.formfield').show();
10090				$allianceDataDisplayDelta.show();
10091				break;
10092		}
10093	});
10094	
10095	$('.cc-number-container input[type="text"]').off('keyup.CreditCardTypeDetection').on('keyup.CreditCardTypeDetection', function (e) {
10096		var selectedCreditCardType = util.getCreditCardType($(this).val());
10097		
10098		if (selectedCreditCardType) {
10099			$creditCardTypeSelect.val(selectedCreditCardType.PaymentCardDisplayKey);
10100		} else {
10101			$creditCardTypeSelect.val('');
10102		}
10103		
10104		$creditCardTypeSelect.trigger('change.CreditCardTypeDetection');
10105	});
10106    
10107    $creditCardTypeSelect.trigger('change.CreditCardTypeDetection');
10108	
10109	var paymentList = $('.payment-list');
10110    if (paymentList.length === 0) { return; }
10111	
10112    $('form[name="payment-remove"] button[type="submit"]').on('click', function (e) {
10113        e.preventDefault();
10114        
10115        // override form submission in order to prevent refresh issues
10116        var $button = $(e.target);
10117        
10118        // confirm delete
10119        if ($button.hasClass('delete')) {
10120        	if (!window.confirm(String.format(Resources.CONFIRM_DELETE, Resources.TITLE_CREDITCARD))) {
10121        		return false;
10122        	}
10123        }
10124        
10125        var $form = $(this).closest('form'); 
10126        
10127        $('<input/>').attr({
10128            type: 'hidden',
10129            name: $button.attr('name'),
10130            value: $button.attr('value') || 'delete card'
10131        }).appendTo($form);
10132        var data = $form.serialize();
10133        $.ajax({
10134            type: 'POST',
10135            url: $form.attr('action'),
10136            data: data
10137        })
10138        .done(function () {
10139            page.redirect(Urls.paymentsList);
10140        });
10141    });
10142}
10143/**
10144 * @private
10145 * @function
10146 * @description init events for the loginPage
10147 */
10148function initLoginPage() {
10149    //o-auth binding for which icon is clicked
10150    $('.oAuthIcon').bind('click', function () {
10151        $('#OAuthProvider').val(this.id);
10152    });
10153    $('.btn-lookup-order').on('click', function (e) {
10154        e.preventDefault();
10155        $('div.check-order-content').slideDown();
10156        $(this).slideUp();
10157    });
10158    $('#password-reset-rewards').on('click', function (e) {
10159        e.preventDefault();
10160        dialog.open({
10161            url: $(e.target).attr('href'),
10162            target : $(".password-reset-dialog-container"),
10163            options: {
10164            	dialogClass: 'select-rewards-number-popup',
10165            	width: 385,
10166                open: function () {
10167                    validator.init();
10168                    var $requestPasswordForm = $('[name$="_requestpassword"]'),
10169                        $submit = $requestPasswordForm.find('[name$="_requestpassword_send"]'),
10170                    	$resetPasswordForm = $('[name$="_resetpassword"]');
10171                    $($submit).on('click', function (e) {
10172                        if (!$requestPasswordForm.valid()) {
10173                            return;
10174                        }
10175                    });
10176                    if ( $resetPasswordForm.length > 0 ) {
10177                    	$resetPasswordForm.find("button[type=submit]").on('click', function (e) {
10178                    		e.preventDefault();
10179                    		var nameAttr = $(this).attr('name');
10180                    		dialog.submit(nameAttr);
10181                    	});
10182                    }
10183                    $(".reset-password-heading-close-btn").on('click',function(){
10184                        $("button.ui-dialog-titlebar-close").click();
10185                    });
10186                    execBarCodeGeneration();
10187                }
10188            }
10189        });
10190    });
10191    if ( $('#password-reset-rewards').length > 0 ) {
10192    	var ct = 0;
10193    	while(ct < 2) {
10194    		ct++;
10195    		$('#password-reset-rewards').click();
10196    	}
10197    }
10198}
10199
10200/**
10201 * @private
10202 * @function
10203 * @description Binds the events of the order, address and payment pages
10204 */
10205function initializeEvents() {
10206    toggleFullOrder();
10207    initAddressEvents();
10208    initPaymentEvents();
10209    initLoginPage();
10210	initCreateAccountEventHandlers();
10211    if (util.isMobile()) {
10212    	util.limitInputToLength('#CreditCardForm input[name*="_number"]', 16);
10213    }
10214
10215	var blurFields = "#dwfrm_profile_customer_email, #dwfrm_profile_customer_emailconfirm, input[id*=dwfrm_profile_login_password_], input[id*=dwfrm_profile_login_passwordconfirm_]";
10216    $(blurFields).blur(function(){
10217        if ( $(this).val() == '' ) {
10218            if ( $(this).parents('.form-row').find( '.form-caption.error-message' ).length ) {
10219                $(this).parents('.form-row').find( '.form-caption.error-message' ).html('');
10220            }
10221        }
10222    });
10223}
10224
10225function execBarCodeGeneration() {
10226	if ($('.rewardsregistrationconfirmation').length > 0) {
10227		var barcodeNumber = ""+$('.rewardsregistrationconfirmation').find("[data-rewardsnumber]").data('rewardsnumber');
10228		var barcodeType = 'code128';
10229		
10230		$("#barcodeTarget").barcode(barcodeNumber, barcodeType, {
10231			barHeight:'75',
10232			barWidth:'2',
10233			fontSize:'14',
10234			fontWeight:'bold',
10235			output:'bmp'
10236		});
10237	}
10238}
10239
10240function initRewardsPage() {
10241	$(window).on('load', function() {
10242		execBarCodeGeneration();
10243	});
10244}
10245
10246$(window).on("message", function(e) {
10247    var data = e.originalEvent.data;
10248    if(data == "error"){
10249    	$(".menu-utility-user").addClass("loginError");
10250    }
10251});
10252
10253function renderOptions(optionParent, optionsStoreId) {
10254	var options = '';
10255	var statesList = JSON.parse($(optionsStoreId + ' option:selected').data('options').replace(/\'/g, '\"'));
10256	
10257	statesList.forEach(function(state){
10258		options += '<option class="select-option" label="'+state.label+'" value="'+state.code+'">'+state.label+'</option>';
10259	});
10260	$(optionParent).empty().append(options);
10261	
10262	if (statesList.length <= 1) {
10263		$(optionParent).parent().parent().parent().hide();
10264		$(optionParent + '_sbm').val($(optionParent).val()).attr('disabled', false);
10265	} else {
10266		$(optionParent).parent().parent().parent().show();
10267		$(optionParent + '_sbm').attr('disabled', true);
10268	}
10269}
10270
10271function initRewardRegisterForm() {
10272	$(document).ready(function() {
10273		var $rewardRegisterForm = $('#dwfrm_rewardsregistration');
10274		if ($rewardRegisterForm.length > 0) {
10275			$rewardRegisterForm.find('#dwfrm_rewardsregistration_rewardscustomer_phone').rules('add', {
10276				phone: true
10277			});
10278			$rewardRegisterForm.find('#dwfrm_rewardsregistration_rewardscustomeraddress_zipcode').rules('add', {
10279				postal: true
10280			});
10281			$rewardRegisterForm.find('#dwfrm_rewardsregistration_rewardscustomer_email').rules('add', {
10282				requireEmailExtension: true
10283			});
10284			$rewardRegisterForm.find('#dwfrm_rewardsregistration_rewardscustomer_emailconfirm').rules('add', {
10285				requireEmailExtension: true,
10286				equalTo: '#dwfrm_rewardsregistration_rewardscustomer_email'
10287			});
10288
10289			$rewardRegisterForm.on('keyup', '#dwfrm_rewardsregistration_rewardscustomer_email', function(event) {
10290				var serverErrorMessage = $(event.target).closest('.form-row').find('.error-message');
10291				if (serverErrorMessage.length > 0) {
10292					serverErrorMessage.remove();
10293				}
10294			});
10295		}
10296	});
10297}
10298
10299function initRewardsAttached() {
10300	$(window).on('load', function() {
10301		if ($('#rewardsCheckBalance #fieldRewardsNumber').val() != '') {
10302			$('#rewardsCheckBalance input[type="submit"]').trigger('click');
10303		}
10304	});
10305}
10306
10307function getStatusDetails(status) {
10308	var statusLevel = "B.REWARDED";
10309	var tooltip = "Earn 1 point for every dollar spent."
10310	var rewardsStatus = status.toLowerCase();
10311	if (rewardsStatus != "bbreward" && rewardsStatus != "basic"){
10312		statusLevel = "B.REWARDED " + status;
10313		if (rewardsStatus == "gold"){
10314			tooltip = "Earn 1.25 points for every dollar spent."
10315		} else {
10316			tooltip = "Earn 2 points for every dollar spent."
10317		}
10318	}
10319	
10320	return { 
10321		statusLevel: statusLevel, 
10322		tooltip: tooltip
10323	};
10324}
10325
10326function initDisplayRewardsData() {
10327	$.ajax({
10328		url: Urls.getRewardInfoForRegistered,
10329		dataType: 'JSON',
10330		method: 'POST',
10331		data : {
10332			'csrf_token':  $('.account-overview input[name="csrf_token"]').val(),
10333			'rewardsNumber': $('.attached-rewards-account .rewards-number').text(),			
10334			'email': $('.attached-rewards-account.email-address').text(),
10335			'zipcode': $('.attached-rewards-account.zip-code').text(),
10336			'phone': $('.attached-rewards-account.phone-number').text()
10337		},
10338		success : function(data) {
10339			if ('error' in data && data['error']) {
10340				$('.myaccount-contact-custserv').removeClass('hidden');				
10341			} else {			
10342				if (data.securityRisk) {					
10343					$('.myaccount-contact-custserv').removeClass('hidden');
10344				} else {
10345					var statusDetails = getStatusDetails(data.rewardsStatus);
10346
10347					$('#active-rewards').removeClass('hidden');
10348					$('.attached-rewards-account .tooltiptext .points-earned').html(statusDetails.tooltip);
10349					$('.attached-rewards-account.status').html(statusDetails.statusLevel);
10350					$('.attached-rewards-account.available-rewards').html("$" + parseFloat(data.creditAmount).toFixed(0));
10351					$('.attached-rewards-account.current-points').html(data.pointBalance);
10352				}		
10353			}
10354		},
10355		complete: function(data, status, xhr) {
10356			if (data.status == 302 && data.responseText) {
10357				location.href = data.responseText;
10358			}
10359		}
10360	});
10361}
10362
10363function initRewardCheckForm() {
10364	$(document).ready(function() {
10365		$('#rewardsSelect1').selectmenu();
10366		$('#rewardsSelect2').selectmenu();
10367		$('body').on('click', '#rewardsSelect1-menu .ui-menu-item-wrapper', function(){
10368			var select1text = $(this).text();
10369			
10370			if (select1text == "B Rewarded" || select1text == "Rewards Number" ) {
10371				$('#rewardsCheckBalance .brewarded-number').removeClass('hidden');
10372				$('#rewardsCheckBalance .rewards-number').removeClass('hidden');
10373				$('#rewardsCheckBalance .email-address').addClass('hidden');
10374				$('#rewardsSelect1-button').addClass('hidden');
10375			}
10376			
10377			if (select1text == "Email Address") {
10378				$('#rewardsCheckBalance .email-address').removeClass('hidden');
10379				$('#rewardsCheckBalance .brewarded-number').addClass('hidden');
10380				$('#rewardsCheckBalance .rewards-number').addClass('hidden');
10381				$('#rewardsSelect1-button').addClass('hidden');
10382			}	
10383		});
10384		
10385		$('body').on('click', '#rewardsSelect2-menu .ui-menu-item-wrapper', function(){
10386			var select2text = $(this).text();
10387			
10388			if (select2text == "Zip Code") {
10389				$('#rewardsCheckBalance .zip-code').removeClass('hidden');
10390				$('#rewardsCheckBalance .phone-number').addClass('hidden');
10391				$('#rewardsSelect2-button').addClass('hidden');
10392			}
10393			
10394			if (select2text == "Phone Number") {
10395				$('#rewardsCheckBalance .phone-number').removeClass('hidden');
10396				$('#rewardsCheckBalance .zip-code').addClass('hidden');
10397				$('#rewardsSelect2-button').addClass('hidden');
10398			}	
10399		});
10400		
10401		var $rewardCheckForm = $('#rewardsCheckBalance');
10402		if ($rewardCheckForm.length > 0) {
10403			$rewardCheckForm.find('#fieldRewardsNumber').rules('add', {
10404				positivenumber: true
10405			});
10406			$rewardCheckForm.find('#fieldRewardsEmail').rules('add', {
10407				requireEmailExtension: true
10408			});
10409			$rewardCheckForm.find('#fieldRewardsZipcode').rules('add', {
10410				postal: true
10411			});
10412			$rewardCheckForm.find('#fieldRewardsPhone').rules('add', {
10413				phone: true
10414			});
10415			
10416			$('#rewardsCheckBalance #resetRewardsInquiry').on('click',  function(event) {
10417				event.preventDefault();
10418				$('.ui-selectmenu-text').text('Select One');
10419				$('#rewardsSelect1-button').removeClass('hidden');
10420				$('#rewardsSelect2-button').removeClass('hidden');
10421				$('#rewardsCheckBalance .form-row').addClass('hidden').find('input.input-text').val('');
10422				$('#sendRewardsInquiry').attr('disabled', true);
10423				$('.rewards-results').html('');
10424			});
10425			
10426			$('#rewardsCheckBalance').on('change', 'input.input-text', function() {
10427				if ( $('#fieldRewardsNumber').val() || $('#fieldRewardsEmail').val() ) {
10428					if ( $('#fieldRewardsZipcode').val() || $('#fieldRewardsPhone').val() ){					
10429						$('#sendRewardsInquiry').removeAttr('disabled');
10430					}
10431				}
10432			});
10433
10434			$rewardCheckForm.find('input#sendRewardsInquiry').on('click', function(event) {
10435				event.preventDefault();
10436				var $resultsContainer = $('.rewards-results');
10437
10438				$resultsContainer.html('');
10439
10440				if (!$rewardCheckForm.valid()) {
10441					return false;
10442				}
10443				
10444				if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
10445					$.ajax({
10446						url: Urls.getRewardInfo,
10447						dataType: 'JSON',
10448						method: 'POST',
10449						data: {
10450							'csrf_token':  $rewardCheckForm.find('input[name="csrf_token"]').val(),
10451							'rewardsNumber': $rewardCheckForm.find('#fieldRewardsNumber').val(),
10452							'email': $rewardCheckForm.find('#fieldRewardsEmail').val(),
10453							'zipcode': $rewardCheckForm.find('#fieldRewardsZipcode').val(),
10454							'phone': $rewardCheckForm.find('#fieldRewardsPhone').val()
10455						},
10456						success : function(data) {
10457							if ('error' in data && data['error']) {
10458								$resultsContainer.append('<p class="error-block">'+data['message']+'</p>');
10459								$('.rewards-contact-custserv').removeClass('hidden');
10460							} else {							
10461								$('#rewards-dashboard').show();
10462								if (data.securityRisk) {
10463									$('.rewards-contact-custserv').removeClass('hidden');
10464								} else {
10465									$('.display-rewards').removeClass('hidden');
10466									if ( $('#goToCreditTwo').length < 1 ) {
10467										renderRewardCheckResults(data, $resultsContainer);
10468									}
10469								}
10470								$('.rewards-details').hide();
10471							}
10472						},
10473						complete: function(data, status, xhr) {
10474							if (data.status == 302 && data.responseText) {
10475								location.href = data.responseText;
10476							}
10477						}
10478					});
10479				}
10480			});
10481		}
10482	});
10483}
10484
10485function renderRewardCheckResults(data, $resultsContainer) {
10486	var rewardsCreditAmount = $('.available-rewards').data('credit');
10487	var pointsRequired = $('.available-rewards').data('required');
10488	var pointsNeeded = pointsRequired-data.pointBalance;
10489	var pointsText = data.pointBalance == 1 ? ' PT ' : ' PTS ';
10490	var statusDetails = getStatusDetails(data.rewardsStatus);
10491	
10492	
10493	$('#rewards-number').text("#" + data.rewardsNumber).removeClass('loader');
10494	$('.rewards-status div').text(statusDetails.statusLevel).removeClass('loader');
10495	$('#rewards-earned').text("$" + (Math.floor(parseFloat(data.pointsEarnedInLastYear/pointsRequired))*15).toFixed(2));
10496	$('.rewards-message div').removeClass('loader');
10497	$('#rewards-credit').show().html("$" + parseFloat(data.creditAmount).toFixed(0)).removeClass('loader');
10498	if (data.creditAmount != 0){		
10499		$('#rewards-expiration').show().find('span').text(data.creditExpirationDate);		
10500	} else {
10501		$('.rewards-zero-message').show();
10502	}
10503	$('.available-points div').removeClass('loader');
10504	$('.points-accrued').removeClass('loader').html(data.pointBalance);
10505	$('.tooltiptext .points-earned').text(statusDetails.tooltip);
10506}
10507
10508function initDisplayLoyaltyData() {
10509	if ($('.pt_account').length > 0) {
10510		$.ajax({
10511			url: Urls.loyaltyCheck,
10512			dataType: 'JSON',
10513			method: 'POST',
10514			data: {
10515				'loyaltyNumber': $('.settings--loyalty').attr('data-number'),
10516				'loyaltyZip': $('.settings--loyalty').attr('data-zip')
10517			},						
10518			success : function(data) {
10519				if (('error' in data && data['error'])) {
10520					$('.loyalty-error').text(data.message);
10521				} else {
10522					renderLoyaltyInfo(data.status);
10523				}
10524			},
10525			complete: function(data, status, xhr) {
10526				if (data.status == 302 && data.responseText) {
10527					location.href = data.responseText;
10528				}
10529			}
10530		});
10531	}
10532}
10533
10534function renderLoyaltyInfo(data) {
10535	var optinStatus = data.optIn;
10536	
10537	if (optinStatus){
10538		$('.loyalty-number span.value').text(data.loyaltyMemberNumber);
10539		$('.current-points span.value').text(Math.floor(data.points));
10540		$('.available-rewards span.value').text("$" + data.totalRewards);
10541	} else {
10542		$('.settings--loyalty').hide();
10543	}
10544}
10545
10546function clearLoyaltyForm() {
10547	$('.check-my-balance').show();
10548	$('.my-balance').hide();
10549	$('.ui-selectmenu-text').text('Select One');
10550	$('#loyaltySelect1-button').removeClass('hidden');
10551	$('#loyaltySelect2-button').removeClass('hidden');
10552	$('#loyaltyCheckBalance .form-row').addClass('hidden').find('input.input-text').val('');
10553	$('#sendLoyaltyInquiry').attr('disabled', true);
10554	$('.loyalty-results span').text('');
10555	$('.loyalty-error').html('');
10556}
10557
10558function initLoyaltyCheckForm() {
10559	if ($('.pt_loyalty').length > 0) {
10560		$('#loyaltySelect1').selectmenu();
10561		$('#loyaltySelect2').selectmenu();
10562		
10563		$('body').on('click', '#loyaltySelect1-menu .ui-menu-item-wrapper', function(){
10564			var select1text = $(this).text();
10565			
10566			if (select1text == "Loyalty Number" ) {
10567				$('#loyaltyCheckBalance .brewarded-number').removeClass('hidden');
10568				$('#loyaltyCheckBalance .loyalty-number').removeClass('hidden');
10569				$('#loyaltyCheckBalance .email-address').addClass('hidden');
10570				$('#loyaltySelect1-button').addClass('hidden');
10571			}
10572			
10573			if (select1text == "Email Address") {
10574				$('#loyaltyCheckBalance .email-address').removeClass('hidden');
10575				$('#loyaltyCheckBalance .brewarded-number').addClass('hidden');
10576				$('#loyaltyCheckBalance .loyalty-number').addClass('hidden');
10577				$('#loyaltySelect1-button').addClass('hidden');
10578			}	
10579		});
10580		
10581		$('body').on('click', '#loyaltySelect2-menu .ui-menu-item-wrapper', function(){
10582			var select2text = $(this).text();
10583			
10584			if (select2text == "Zip Code") {
10585				$('#loyaltyCheckBalance .zip-code').removeClass('hidden');
10586				$('#loyaltyCheckBalance .phone-number').addClass('hidden');
10587				$('#loyaltySelect2-button').addClass('hidden');
10588			}
10589			
10590			if (select2text == "Phone Number") {
10591				$('#loyaltyCheckBalance .phone-number').removeClass('hidden');
10592				$('#loyaltyCheckBalance .zip-code').addClass('hidden');
10593				$('#loyaltySelect2-button').addClass('hidden');
10594			}	
10595		});
10596		
10597		var $loyaltyCheckForm = $('#loyaltyCheckBalance');
10598		if ($loyaltyCheckForm.length > 0) {
10599			$loyaltyCheckForm.find('#fieldLoyaltyNumber').rules('add', {
10600				positivenumber: true
10601			});
10602			$loyaltyCheckForm.find('#fieldLoyaltyEmail').rules('add', {
10603				requireEmailExtension: true
10604			});
10605			$loyaltyCheckForm.find('#fieldLoyaltyZipcode').rules('add', {
10606				postal: true
10607			});
10608			$loyaltyCheckForm.find('#fieldLoyaltyPhone').rules('add', {
10609				phone: true
10610			});
10611			
10612			$('#loyaltyCheckBalance #resetLoyaltyInquiry').on('click',  function(event) {
10613				event.preventDefault();
10614				clearLoyaltyForm();			
10615			});
10616			
10617			var scrollMinus = 0;
10618			if ($('.voucher').length > 8 ){
10619				scrollMinus = 100;
10620			}
10621			if ($(window).width() < 767) {
10622				scrollMinus = 190;
10623			}		
10624			
10625			$('.my-balance #startOverLoyalty').on('click',  function() {						
10626				clearLoyaltyForm();
10627				if (scrollMinus > 0) {
10628					$('html,body').animate({scrollTop: $(".tab").offset().top - scrollMinus}, 200);
10629				}
10630			});
10631			
10632			$('#loyaltyCheckBalance').on('change', 'input.input-text', function() {
10633				if ( $('#fieldLoyaltyNumber').val() || $('#fieldLoyaltyEmail').val() ) {
10634					if ( $('#fieldLoyaltyZipcode').val() || $('#fieldLoyaltyPhone').val() ){					
10635						$('#sendLoyaltyInquiry').removeAttr('disabled');
10636					}
10637				}
10638			});
10639	
10640			$loyaltyCheckForm.find('input#sendLoyaltyInquiry').on('click', function(event) {
10641				event.preventDefault();
10642	
10643				if (!$loyaltyCheckForm.valid()) {
10644					return false;
10645				}
10646				
10647				$.ajax({
10648					url: Urls.loyaltyCheck,
10649					dataType: 'JSON',
10650					method: 'POST',
10651					data: {
10652						'loyaltyNumber': $loyaltyCheckForm.find('#fieldLoyaltyNumber').val(),
10653						'loyaltyEmail': $loyaltyCheckForm.find('#fieldLoyaltyEmail').val(),
10654						'loyaltyZip': $loyaltyCheckForm.find('#fieldLoyaltyZipcode').val(),
10655						'loyaltyPhone': $loyaltyCheckForm.find('#fieldLoyaltyPhone').val()
10656					},						
10657					success : function(data) {
10658						if (('error' in data && data['error'])) {
10659							$('.loyalty-error').text(data.message);
10660						} else {													
10661							renderLoyaltyCheckResults(data.status);
10662						}
10663					},
10664					complete: function(data, status, xhr) {
10665						if (data.status == 302 && data.responseText) {
10666							location.href = data.responseText;
10667						}
10668					}
10669				});
10670			});
10671		}
10672	}
10673}
10674
10675function renderLoyaltyCheckResults(data) {
10676	var optinStatus = data.optIn;
10677	var currentSite = '';
10678	if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
10679    	currentSite = Resources.LoyaltyCredit.NotOptedInSHEP;
10680    }
10681	if (pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS) {
10682    	currentSite = Resources.LoyaltyCredit.NotOptedInCO;
10683    }
10684	
10685	if (optinStatus){
10686		$('.check-my-balance').hide();
10687		$('.my-balance').removeClass('hidden').show();
10688		$('.loyalty-number span').text(data.loyaltyMemberNumber);
10689		$('.current-points span').text(Math.floor(data.points));
10690		$('.available-rewards span').text("$" + data.totalRewards);
10691		var details = '';
10692		var vouchers = data.rewardInfo;
10693		
10694		vouchers.forEach(function(voucher){
10695			details += '<div class="voucher"><span class="reward-amount">Reward Amount: <b>$'+voucher.rewardAmount+'</b></span><span class="expiration">Expiration Date: <b>'+voucher.expirationDate+'</b></span></div>';
10696		});
10697		
10698		$('.details').html(details);
10699	} else {
10700		$('.loyalty-error').text(currentSite);
10701	}
10702}
10703
10704function initLoyaltySocial() {
10705	var handle = '';
10706	
10707	$('body').on('focus', '#fieldLoyaltySocialHandleInstagram', function() {
10708		$('.handle-error').text('');
10709	});
10710
10711	$('body').on('click', '#updateInstagramHandle', function(){
10712		$('.handle-display').removeClass('show-handle').addClass('show-form');
10713		$('.form-display').removeClass('show-handle').addClass('show-form');
10714	});
10715	
10716	$('body').on('click', '#addInstagramHandle', function(){
10717		event.preventDefault();
10718		
10719		var formValidation = true;
10720		var handle = $('#fieldLoyaltySocialHandleInstagram').val();
10721		var regexHandle = /^@([a-zA-Z0-9]|[._](?![._])){2,30}$/;
10722		if (String(handle).match(regexHandle) == null ) {
10723			formValidation = false;
10724		}
10725
10726		if (formValidation) {	
10727			$.ajax({
10728				url: Urls.loyaltyOptin,
10729				dataType: 'JSON',
10730				method: 'POST',
10731				data: {
10732					'email': $('.settings--loyalty').attr('data-email'),
10733					'dwCustomerID': $('.settings--loyalty').attr('data-dwid'),
10734					'instaHandle': handle,
10735					'optStatus': $('.settings--loyalty').attr('data-optin')
10736				},						
10737				success : function(data) {
10738					if (('error' in data && data['error'])) {
10739						$('.handle-error').text(data.message);
10740					} else {													
10741						$('.handle-display').removeClass('show-form').addClass('show-handle');
10742						$('.handle-display .value').text(handle);
10743						$('.form-display').removeClass('show-form').addClass('show-handle');
10744					}
10745				}
10746			});
10747		} else {
10748			$('.handle-error').text('Please enter a valid social handle.');
10749		}
10750	});
10751}
10752
10753function initAddressFormEvents() {
10754	$(document).ready(function(){
10755		$(document).on('change', '#dwfrm_profile_address_country', function(){
10756			renderOptions('#dwfrm_profile_address_states_state', '#dwfrm_profile_address_country');
10757			var $initialState = $('#dwfrm_profile_address_states_state_sbm');
10758			
10759			if ($initialState.length > 0 && $initialState.val() != '') {
10760				$('#dwfrm_profile_address_states_state').val($initialState.val());
10761			}
10762		});
10763		$('#dwfrm_profile_address_country').trigger('change');
10764		
10765		$(document).on('click', '.find-more-stores a', function () {
10766			findMoreStores.initialize({ view: 'account' });
10767		});
10768		
10769		$(document).on('opc.SetShippingAddress', function (e, address) {
10770			var $closestStores = $('#closestStores');
10771			var $makeThisMystore = $('#storeControls').find('[data-action="makethismystore"]');
10772			
10773			$closestStores.find('option.find-more-stores').remove();
10774			if ($closestStores.find('option[value="' + address.storeId + '"]').length == 0) {
10775				$closestStores.find('option').first().after('<option class="find-more-stores" value="' + address.storeId + '">' + address.firstName + ' - ' + address.labelText + '</option>');
10776			}
10777			
10778			$closestStores.val(address.storeId);
10779			$closestStores.trigger('change');
10780			if ($makeThisMystore.length > 0) {
10781				$makeThisMystore.trigger('click');
10782			}
10783		});
10784
10785		$(document).on('click', '.address-tile .address-delete', function(event) {
10786			event.preventDefault();
10787
10788			// Clear the previous data in the attributes
10789			$('.address-select-new-dialog .address-dialog-confirm').removeAttr('data-url data-default');
10790
10791			var $deleteButton = $(this);
10792			var $addressTile = $deleteButton.closest('.address-tile');
10793			var isDefault = $addressTile.hasClass('default');
10794			var addressCount = $deleteButton.closest('.address-tiles').find('.address-tile').length;
10795			var addressID = $addressTile.attr('data-address-id');
10796			var addressSectionType = $deleteButton.closest('.address-tiles').next('.account-address-select').attr('data-address-type');
10797			var $dialogContent = addressSectionType === 'billing' ? $('#dialog-billing-addresses') : $('#dialog-shipping-addresses');
10798
10799			if (isDefault) {
10800				$dialogContent.find('.delete-address-select select option[value="'+ addressID +'"]').attr('disabled','disabled').hide();
10801			}
10802
10803			dialog.open({
10804				html: $dialogContent.html(),
10805				options: {
10806					dialogClass: 'address-select-new-dialog',
10807					draggable: false,
10808					open: function(event, ui) {
10809						var $this = $(this);
10810						$this.find('.address-dialog-confirm').attr({
10811							'data-url': $deleteButton.attr('data-url'),
10812							'data-default': isDefault
10813						});
10814						if (isDefault && addressCount > 1) {
10815							$this.find('.replace-default-address').removeClass('d-none');
10816						} else {
10817							$this.find('.confirm-delete-address').removeClass('d-none');
10818						}
10819					},
10820					close: function(event, ui) {
10821						$('body').removeClass('dialog-open');
10822					}
10823				}
10824			});
10825		});
10826
10827		$(document).on('click', '.delete-address-actions button.address-dialog-confirm', function(event) {
10828			event.preventDefault();
10829
10830			var $deleteConfirmButton = $(this);
10831			var $dialogContent = $deleteConfirmButton.closest('.address-select-new-dialog');
10832			var isDefault = $deleteConfirmButton.attr('data-default');
10833			var url = $deleteConfirmButton.attr('data-url');
10834
10835			if (isDefault === 'true' && $dialogContent.find('.replace-default-address').is(':visible')) {
10836				var newDefaultAddress = $dialogContent.find('.replace-default-address select').val();
10837				url = url + '&newDefault=' + newDefaultAddress;
10838			}
10839
10840			window.location.href = url;
10841		});
10842
10843		$(document).on('click', '.delete-address-actions button.address-dialog-cancel', function(event) {
10844			// Clear the previous data
10845			$('.address-select-new-dialog .address-dialog-confirm').removeAttr('data-url data-default');
10846
10847			dialog.close();
10848		});
10849	});
10850}
10851
10852function verifyAccountCode() {  
10853	// 'enter-code' rules will be applied to rewards verification page with this code. See loginDialog.js for the rules added after account dialog is opened.	
10854	$('#verify-code').find('#enter-code').rules('add', {
10855		required: true,
10856		digits: true,
10857		minlength: 6,
10858		messages: {
10859			required: Resources.VALIDATE_CODE,
10860			minlength: Resources.VALIDATE_CODE
10861		},
10862		normalizer: function(value) {
10863			return $.trim(value);
10864		}
10865	});
10866}
10867
10868/* On the Account registration (aka settings) page, hook up the event handler to update the hidden form field
10869 * for 'My Store Number' when the (dynamically populated) Closest Stores drop down is changed. 
10870 * Also, set the value of the selected option in the drop down, or the value in 'My Store Number'
10871 * appropriately on load (based on if creating an Account or editing one. 
10872 */
10873function initCreateAccountEventHandlers(){
10874	var $closestStores = $('#closestStores');
10875	var $storeControls = $('#storeControls');
10876	if ($closestStores != undefined && $closestStores != null){
10877		var lengthStores = $closestStores.find('option').length;
10878		if (lengthStores > 0) {
10879			var storedValue = $('#dwfrm_profile_customer_myStoreNumber').val();
10880			$closestStores.on('change', function(e) {
10881				var newValue = $(this).val();
10882				$('#dwfrm_profile_customer_myStoreNumber').val(newValue);
10883				updateSelectedStorePreview($(e.target).find(':selected'));
10884
10885				if (storedValue !== newValue) {
10886					$storeControls.removeClass('hidden');
10887				} else {
10888					$storeControls.addClass('hidden');
10889				}
10890			});
10891			
10892			setSelectedStoreDropdownOrMyStoreNumber();
10893		}
10894		$storeControls.on('click', 'a', function(e) {
10895			e.preventDefault();
10896			var storeId = $('#dwfrm_profile_customer_myStoreNumber').val();
10897			$.ajax({
10898				url : Urls.assignStoreId,
10899				data : { store_id : storeId },
10900				method : "POST",
10901				dataType : "JSON",
10902				success : function(data) {
10903					if ( 'success' in data && data['success'] == true ) {
10904						location.reload();
10905					}
10906				}
10907			});
10908		});
10909	}
10910}
10911
10912/* Check and see if the profile has a value for 'My Store Number', which it might if on the Account Edit page.
10913 * If so, then set the store drop down to the store on the profile.
10914 * If not, then set the hidden form field for 'My Store Number' to the selected store in the drop down, which will be the first one when on the Create Account page.  
10915 */
10916function setSelectedStoreDropdownOrMyStoreNumber() {
10917	var $myStoreNumberSelected = $('#dwfrm_profile_customer_myStoreNumber');
10918	if ($myStoreNumberSelected != undefined && $myStoreNumberSelected != null){
10919		var currentMyStoreValue = $myStoreNumberSelected.val(); 
10920		if (currentMyStoreValue == undefined || currentMyStoreValue == null || currentMyStoreValue.length == 0){
10921			$myStoreNumberSelected.val($('#closestStores').val());	
10922		} else {
10923			$('#closestStores option[value=' + currentMyStoreValue + ']').attr('selected','selected');
10924			updateSelectedStorePreview($('#closestStores option[value=' + currentMyStoreValue + ']'));
10925		}
10926        $('#storeSection').removeClass('hidden');
10927	}
10928}
10929
10930function updateSelectedStorePreview($store) {
10931	if ($('#selectedStore').length > 0) {
10932		$('#selectedStore .name').text($store.data('name'));
10933		$('#selectedStore .address1').text($store.data('address1'));
10934		if ($store.data('address2') && $store.data('address2').length > 0) {
10935			$('#selectedStore .address2').text($store.data('address2')).removeClass('hidden');
10936		} else {
10937			$('#selectedStore .address2').text('').addClass('hidden');
10938		}
10939		
10940		$('#selectedStore .city').text($store.data('city'));
10941		$('#selectedStore .stateCode').text($store.data('statecode'));
10942		$('#selectedStore .postalCode').text($store.data('postalcode'));
10943		$('#selectedStore .countryCode').text($store.data('countrycode'));
10944		$('#selectedStore .phone').text($store.data('phone'));
10945	}
10946}
10947
10948
10949
10950module.exports = {
10951    init: function (optionsParameter) {
10952		options = optionsParameter;
10953
10954		if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
10955			AddressVerificationDialog = require('../Lob/AddressVerification/AddressVerificationDialog');
10956		}
10957
10958		if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
10959			AddressAutocompleteDialog = require('../Lob/AddressAutocomplete/AddressAutocompleteDialog');
10960		}
10961
10962        initializeEvents();
10963        giftcert.init();
10964        initAddressFormEvents();
10965        initRewardRegisterForm();
10966        initRewardCheckForm();
10967        initLoyaltyCheckForm();
10968        if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
10969        	initDisplayRewardsData();
10970        }
10971        if ((pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS || pageContext.site === SiteConstants.SiteIds.SheplersUS) && SitePreferences.LOYALTY_ENABLED) {
10972        	initDisplayLoyaltyData();
10973        	initLoyaltySocial();
10974        }
10975        initRewardsAttached();
10976		verifyAccountCode();
10977        orderStatusForm.initialize($('#dwfrm_ordertrack'));      
10978    },
10979    initCartLogin: function () {
10980        initLoginPage();
10981        initRewardsPage();
10982    }
10983};
10984},{"../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper":112,"../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../Lob/AddressAutocomplete/AddressAutocompleteDialog":1,"../Lob/AddressVerification/AddressVerificationDialog":2,"../components/OrderStatusForm":28,"../components/Recaptcha":29,"../dialog":36,"../findMoreStores":37,"../giftcert":39,"../page":46,"../tooltip":101,"../util":102,"../validator":103}],49:[function(require,module,exports){
10985'use strict';
10986
10987var _ = require('lodash');
10988var GoogleTagManagerClient = require('../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
10989var loadingIndicator = require('../components/LoadingIndicator');
10990var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
10991var util = require('../util');
10992var ValidationSettings = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
10993var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
10994var ValidationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
10995
10996var options;
10997var dataBuffer;
10998var $container;
10999var $step;
11000var templates;
11001var loadingIndicatorSettings;
11002
11003exports.init = function (optionsParameter) {
11004	if (optionsParameter) {
11005		options = optionsParameter;
11006	} else {
11007		options = {};
11008	}
11009	$container = $('.bootfinder');
11010	$step = $container.find('[step]');
11011	dataBuffer = {
11012		step: 0,
11013		stepData: {}
11014	};
11015	templates = {
11016		sectionContainer: _.template($('#template-bootfinder-section-container').html(), TemplateConstants.Lodash.Settings),
11017		section: _.template($('#template-bootfinder-section').html(), TemplateConstants.Lodash.Settings),
11018		option: _.template($('#template-bootfinder-option').html(), TemplateConstants.Lodash.Settings),
11019		optionImage: _.template($('#template-bootfinder-option-image').html(), TemplateConstants.Lodash.Settings),
11020		results: _.template($('#template-bootfinder-results').html(), TemplateConstants.Lodash.Settings)
11021	}
11022	loadingIndicatorSettings = {
11023		IsLabelShown: false
11024	};
11025	
11026	$container.off('click.BootFinderStepDelta').on('click.BootFinderStepDelta', '[step-delta]', function(event) {
11027		var $this = $(this);
11028		var stepDelta = parseInt($this.attr('step-delta'), 10);
11029		dataBuffer.stepDelta = stepDelta;
11030		var step = dataBuffer.step + dataBuffer.stepDelta;
11031		if (step >= 0 && step <= options.Configuration.Steps.length) {
11032			dataBuffer.step = step;
11033		}
11034		render();
11035	});
11036
11037	// $container.off('click.BootFinderStepIndex').on('click.BootFinderStepIndex', '[step-index]', function(event) {
11038	// 	var $this = $(this);
11039	// 	var step = parseInt($this.attr('step-index'), 10);
11040	// 	if (step >= 0 && step <= options.Configuration.Steps.length) {
11041	// 		dataBuffer.step = step;
11042	// 	}
11043	// 	render();
11044	// });
11045
11046	$container.off('click.BootFinderStepOptions').on('click.BootFinderStepOptions', '[step] .options .option', function(event) {
11047		var $this = $(this);
11048		var sectionIndex = parseInt($this.closest('[section]').attr('section'), 10);
11049		if ($this.hasClass('selected') && !options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].SelectRequired) {
11050			$this.removeClass('selected');
11051			if (dataBuffer.stepData[dataBuffer.step] && dataBuffer.stepData[dataBuffer.step][sectionIndex]) {
11052				var selectedIndex = dataBuffer.stepData[dataBuffer.step][sectionIndex].indexOf($this.attr('value'));
11053				if (selectedIndex > -1) {
11054					dataBuffer.stepData[dataBuffer.step][sectionIndex].splice(selectedIndex, 1);
11055				}
11056			}
11057			if (options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'size' || options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'width') {
11058				updateSizes(sectionIndex, options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex]);
11059			}
11060		} else {
11061			if (!options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].SelectMultiple) {
11062				$this.closest('.options').find('.option').removeClass('selected');
11063			}
11064			if (options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].SelectReset && (dataBuffer.step + 1) <= options.Configuration.Steps.length) {
11065				for (var stepIndex = (dataBuffer.step + 1); stepIndex <= options.Configuration.Steps.length; stepIndex++) {
11066					dataBuffer.stepData[stepIndex] = {};
11067				}
11068			}
11069			$this.addClass('selected');
11070			if (!dataBuffer.stepData[dataBuffer.step]) {
11071				dataBuffer.stepData[dataBuffer.step] = {}
11072			}
11073			if (!dataBuffer.stepData[dataBuffer.step][sectionIndex] || !options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].SelectMultiple) {
11074				dataBuffer.stepData[dataBuffer.step][sectionIndex] = [];
11075			}
11076			var sectionData = dataBuffer.stepData[dataBuffer.step][sectionIndex];
11077			sectionData[sectionData.length] = $this.attr('value');
11078			if (options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'size' || options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'width') {
11079				updateSizes(sectionIndex, sectionData);
11080			}
11081		}
11082
11083		if (!options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].SelectMultiple && $this.closest('.options').find('.selected').length > 0 && dataBuffer.step < (options.Configuration.Steps.length - 1)) {
11084			dataBuffer.stepDelta = 1;
11085			dataBuffer.step += dataBuffer.stepDelta;
11086			render();
11087		}
11088	});
11089
11090	$container.off('click.BootFinderSendResultsEmail').on('click.BootFinderSendResultsEmail', '.send-results-email', function(event) {
11091		event.preventDefault();
11092		
11093		var $this = $(this);
11094		var $form = $this.closest('form');
11095		if (dataBuffer.$emailForm && dataBuffer.$emailForm.valid()) {
11096			var url = buildUrl();
11097			url = util.appendParamToURL(url, 'bootfinderemail');
11098
11099			var payload = {
11100				email: $form.find('input[type="email"]').val(),
11101				url: url
11102			}
11103			$step.loading($.extend(loadingIndicatorSettings, {
11104				IsLabelShown: true,
11105				Content: 'Sending Email...'
11106			}));
11107			$.ajax({
11108				type: 'post',
11109				data: payload,
11110				url: buildUrl(Urls.BootFinder.SendResultsEmail),
11111				success: function(data) {
11112					if (data && data.success) {
11113						$step.find('.loading-indicator').find('.content').html('Email Sent!<br/>Loading Results...');
11114						window.location = $('.view-results').attr('href');
11115					} else {
11116						$container.find('.validation-error-container').html('<span class="error">An error occurred sending the email. Please try again.</span>');
11117					}
11118				}
11119			});
11120		}
11121	});
11122
11123	$container.off('click.BootFinderViewResults').on('click.BootFinderViewResults', '.view-results', function(event) {
11124		if (!$step.attr('loading')) {
11125			$step.loading($.extend(loadingIndicatorSettings, {
11126				Content: 'Loading Results...'
11127			}));
11128			var url = buildUrl();
11129			url = util.appendParamToURL(url, 'bootfinderviewresults');
11130			window.location = url;
11131		}
11132	});
11133
11134	$(window).off('load.BootFinder resize.BootFinder').on('load.BootFinder resize.BootFinder', function(event) {
11135		if ($(window).outerWidth() <= 768) {
11136			$container.css('min-height', $(window).outerHeight() - $('.header-wrapper').height());
11137		} else {
11138			$container.css('min-height', 0);
11139		}
11140	});
11141
11142	var stepListHtml = '';
11143	for (var stepIndex in options.Configuration.Steps) {
11144		stepListHtml += '<a step-index="' + stepIndex + '"></a>';
11145	}
11146	$container.find('.step-list').html(stepListHtml);
11147	render();
11148};
11149
11150function render() {
11151	$step.height($step.outerHeight());
11152	$step.html('');
11153	$step.loading(loadingIndicatorSettings);
11154	$('html').animate({ scrollTop: 0 }, 250);
11155	if (dataBuffer.step == (options.Configuration.Steps.length - 1)) {
11156		$container.find('.next').hide();
11157		$container.find('.view-results').show();
11158	} else {
11159		$container.find('.next').show();
11160		$container.find('.view-results').hide();
11161	}
11162	renderStep();
11163	sendDataLayer();
11164	if ($container.hasClass('results')) {
11165		$container.removeClass('results');
11166	}
11167
11168	$container.find('.step-list a').removeClass('active');
11169	$container.find('.step-list a:nth-child(' + (dataBuffer.step + 1) + ')').addClass('active');
11170	$container.find('[step]').attr('step', dataBuffer.step);
11171}
11172
11173function updateSizes(sectionIndex, sectionData) {
11174	return getRefinementCountsForSection(sectionIndex, sectionData).then(function(data) {
11175		if (options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex] && options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'size') {
11176			var $selectedLengths = $('[section="' + sectionIndex + '"] .options').find('.option.selected');
11177			var $widths = $('[section="' + (sectionIndex + 1) + '"] .options .option');
11178			if ($selectedLengths && $selectedLengths.length > 0) {
11179				$widths.addClass('disabled');
11180				$selectedLengths.each(function() {
11181					var selectedLength = $(this).attr('value');
11182					$widths.each(function() {
11183						var width = $(this).attr('value');
11184						var size = selectedLength + ' ' + width;
11185						if (dataBuffer.Refinements && dataBuffer.Refinements.size && dataBuffer.Refinements.size[size]) {
11186							$widths.filter('[value="' + width + '"]').removeClass('disabled');
11187						}
11188					});
11189				});
11190			} else {
11191				$widths.removeClass('disabled');
11192			}
11193		} else if (options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex] && options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex].Refinement == 'width') {
11194			var $selectedWidths = $('[section="' + sectionIndex + '"] .options').find('.option.selected');
11195			var $lengths = $('[section="' + (sectionIndex - 1) + '"] .options .option');
11196			if ($selectedWidths && $selectedWidths.length > 0) {
11197				$lengths.addClass('disabled');
11198				$selectedWidths.each(function() {
11199					var selectedWidth = $(this).attr('value');
11200					$lengths.each(function() {
11201						var length = $(this).attr('value');
11202						var size = length + ' ' + selectedWidth;
11203						if (dataBuffer.Refinements && dataBuffer.Refinements.size && dataBuffer.Refinements.size[size]) {
11204							$lengths.filter('[value="' + length + '"]').removeClass('disabled');
11205						}
11206					});
11207				});
11208			} else {
11209				$lengths.removeClass('disabled');
11210			}
11211		}
11212	});
11213}
11214
11215function renderStep() {
11216	var step = options.Configuration.Steps[dataBuffer.step];
11217	var stepData;
11218	if (dataBuffer.stepData && dataBuffer.stepData[dataBuffer.step]) {
11219	 	stepData = dataBuffer.stepData[dataBuffer.step];
11220	}
11221	dataBuffer.sectionRenderCount = 0;
11222	for (var sectionIndex in step.Sections) {
11223		var section = step.Sections[sectionIndex];
11224		var columns = 1;
11225		if (section.Columns) {
11226			columns = section.Columns;
11227		}
11228		$container.find('[step]').append(templates.sectionContainer({
11229			Section: sectionIndex,
11230			Columns: columns
11231		}));
11232
11233		var optionsHtml = '';
11234		if (section.Options) {
11235			var sectionOptions;
11236			if (typeof section.Options == 'object' && section.OptionsRelatedSelectionStep != undefined && section.OptionsRelatedSelectionSection != undefined && dataBuffer.stepData && dataBuffer.stepData[section.OptionsRelatedSelectionStep] && dataBuffer.stepData[section.OptionsRelatedSelectionStep][section.OptionsRelatedSelectionSection]) {
11237				var optionsRelatedSelectionValue = dataBuffer.stepData[section.OptionsRelatedSelectionStep][section.OptionsRelatedSelectionSection];
11238				sectionOptions = section.Options[optionsRelatedSelectionValue];
11239			} else {
11240				sectionOptions = section.Options;
11241			}
11242			for (var optionIndex in sectionOptions) {
11243				var option = sectionOptions[optionIndex];
11244				if (option.Text) {
11245					var cssClass = '';
11246					if (stepData && stepData[sectionIndex] && 'indexOf' in stepData[sectionIndex] && stepData[sectionIndex].indexOf(option.Text) > -1) {
11247						cssClass = 'selected';
11248					}
11249
11250					optionsHtml += templates.option({
11251						Image: '',
11252						Text: option.Text,
11253						Value: option.Text,
11254						CssClass: cssClass
11255					});
11256				}
11257			}
11258			renderSection(sectionIndex, section.Header, section.Text, optionsHtml);
11259		} else if (section.Refinement && section.Refinement != 'width') {
11260			getRefinementCountsForSection(sectionIndex, section).then(function(data) {
11261				var optionsHtmlInner = '';
11262				if (data.Section.Refinement == 'size') {
11263					var lengths = [];
11264					var widths = [];
11265					var widthRegex = / [\w\/]+/gi;
11266					for (var refinementKey in data.Refinements) {
11267						var refinementCount = data.Refinements[refinementKey];
11268						if (refinementCount) {
11269							var widthRegexResult = refinementKey.match(widthRegex);
11270							var length;
11271							var width;
11272							if (widthRegexResult && widthRegexResult.length) {
11273								width = widthRegexResult[widthRegexResult.length - 1].trim();
11274							}
11275							if (width) {
11276								length = refinementKey.replace(width, '').trim();
11277							}
11278							if (length && lengths.indexOf(length) < 0) {
11279								lengths[lengths.length] = length;
11280							}
11281							if (width && widths.indexOf(width) < 0) {
11282								widths[widths.length] = width;
11283							}
11284						}
11285					}
11286					lengths.sort(function(left, right) {
11287						var leftValue = left.replace(/ *1\/2/, '.5');
11288						var rightValue = right.replace(/ *1\/2/, '.5');
11289						return parseFloat(leftValue) - parseFloat(rightValue);
11290					});
11291
11292					widths.sort(function(left, right) {
11293						var leftValue = parseInt(left, 10);
11294						var rightValue = parseInt(right, 10);
11295						if (leftValue) {
11296							leftValue = ''.padStart(leftValue, left.replace(/\d/g, '')[0]);
11297						} else {
11298							leftValue = left;
11299						}
11300						if (rightValue) {
11301							rightValue = ''.padStart(rightValue, right.replace(/\d/g, '')[0]);
11302						} else {
11303							rightValue = right;
11304						}
11305
11306						var leftWidthDescription;
11307						if (options.WidthDescriptionConfiguration && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][left]) {
11308							leftWidthDescription = options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][left];
11309						}
11310
11311						var rightWidthDescription;
11312						if (options.WidthDescriptionConfiguration && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][right]) {
11313							rightWidthDescription = options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][right];
11314						}
11315
11316						if (leftWidthDescription == 'Medium' && rightWidthDescription != 'Medium') {
11317							return -1;
11318						} else if (leftWidthDescription != 'Medium' && rightWidthDescription == 'Medium') {
11319							return 1;
11320						}
11321						if (leftWidthDescription == 'Wide' && rightWidthDescription != 'Wide') {
11322							return -1;
11323						} else if (leftWidthDescription != 'Wide' && rightWidthDescription == 'Wide') {
11324							return 1;
11325						}
11326						return leftValue.localeCompare(rightValue);
11327					});
11328
11329					for (var lengthIndex in lengths) {
11330						var length = lengths[lengthIndex];
11331						var cssClass = '';
11332						if (stepData && stepData[data.SectionIndex] && 'indexOf' in stepData[data.SectionIndex] && stepData[data.SectionIndex].indexOf(length) > -1) {
11333							cssClass = 'selected';
11334						}
11335						optionsHtmlInner += templates.option({
11336							Image: '',
11337							Text: length,
11338							Value: length,
11339							CssClass: cssClass
11340						});
11341					}
11342					dataBuffer.lengths = lengths;
11343					renderSection(data.SectionIndex, data.Section.Header, data.Section.Text, optionsHtmlInner);
11344
11345					var widthsHtml = '';
11346					var widthSectionIndex = parseInt(data.SectionIndex, 10) + 1;
11347					for (var widthIndex in widths) {
11348						var width = widths[widthIndex];
11349						var cssClass = '';
11350						if (stepData && stepData[widthSectionIndex] && 'indexOf' in stepData[widthSectionIndex] && stepData[widthSectionIndex].indexOf(width) > -1) {
11351							cssClass = 'selected';
11352						}
11353						var widthDescription = '';
11354						if (options.WidthDescriptionConfiguration && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'] && options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][width]) {
11355							widthDescription = ' (' + options.WidthDescriptionConfiguration[dataBuffer.stepData[0][0]]['Boot'][width] + ')';
11356						}
11357						widthsHtml += templates.option({
11358							Image: '',
11359							Text: width + widthDescription,
11360							Value: width,
11361							CssClass: cssClass
11362						});
11363					}
11364					dataBuffer.widths = widths;
11365					$container.find('[section="' + widthSectionIndex + '"] .options').html(widthsHtml);
11366					updateSizes(sectionIndex, options.Configuration.Steps[dataBuffer.step].Sections[sectionIndex]);
11367				} else {
11368					for (var refinementKey in data.Refinements) {
11369						var refinementCount = data.Refinements[refinementKey];
11370						if (refinementCount) {
11371							var refinementText;
11372							if (dataBuffer.DisplayValues[data.Section.Refinement] && dataBuffer.DisplayValues[data.Section.Refinement][refinementKey]) {
11373								refinementText = dataBuffer.DisplayValues[data.Section.Refinement][refinementKey];
11374							} else {
11375								refinementText = refinementKey;
11376							}
11377
11378							var cssClass = '';
11379							if (stepData && stepData[data.SectionIndex] && 'indexOf' in stepData[data.SectionIndex] && stepData[data.SectionIndex].indexOf(refinementKey) > -1) {
11380								cssClass = 'selected';
11381							}
11382
11383							var sectionImages;
11384							if (typeof section.Images == 'object' && section.ImagesRelatedSelectionStep != undefined && section.ImagesRelatedSelectionSection != undefined && dataBuffer.stepData && dataBuffer.stepData[section.ImagesRelatedSelectionStep] && dataBuffer.stepData[section.ImagesRelatedSelectionStep][section.ImagesRelatedSelectionSection]) {
11385								var imagesRelatedSelectionValue = dataBuffer.stepData[section.ImagesRelatedSelectionStep][section.ImagesRelatedSelectionSection];
11386								sectionImages = section.Images[imagesRelatedSelectionValue];
11387							} else {
11388								sectionImages = section.Images;
11389							}
11390							var imageHtml = '';
11391							if (sectionImages && sectionImages[refinementKey]) {
11392								imageHtml = templates.optionImage({
11393									ImageUrl: sectionImages[refinementKey]
11394								});
11395							}							
11396							optionsHtmlInner += templates.option({
11397								Image: imageHtml,
11398								Text: refinementText,
11399								Value: refinementKey,
11400								CssClass: cssClass
11401							});
11402						}
11403					}
11404					renderSection(data.SectionIndex, data.Section.Header, data.Section.Text, optionsHtmlInner);
11405				}
11406			}).catch();
11407		} else {
11408			renderSection(sectionIndex, section.Header, section.Text, optionsHtml);
11409		}
11410	}
11411}
11412
11413function renderSection(index, header, textParameter, sectionOptions) {
11414	dataBuffer.sectionRenderCount++;
11415	var text = '';
11416	if (textParameter) {
11417		text = '<p>' + textParameter + '</p>';
11418	}
11419	$container.find('[section="' + index + '"]').html(templates.section({
11420		Header: header,
11421		Text: text,
11422		Options: sectionOptions
11423	}));
11424
11425	if (dataBuffer.sectionRenderCount == options.Configuration.Steps[dataBuffer.step].Sections.length) {
11426		if ($step.attr('loading')) {
11427			$step.css('height', '');
11428			$step.loading('destroy');
11429			$('html').animate({ scrollTop: 0 }, 250);
11430		}
11431		$container.find('[step-delta]').removeClass('disabled');
11432		if (options.Configuration.Steps[dataBuffer.step].Sections[index].SelectRequired && $('.options').length != $('.options').find('.selected').length) {
11433			$container.find('.next').addClass('disabled');
11434		}	
11435		if (dataBuffer.step == 0) {
11436			$container.find('.back').addClass('disabled');
11437		}
11438		if ($container.find('.option').length == 0) {
11439			if (dataBuffer.step < (options.Configuration.Steps.length - 1)) {
11440				dataBuffer.step += dataBuffer.stepDelta;
11441				render();
11442			} else {
11443				$('section header h2').text('No more options to choose from, click the View Results button!')
11444			}
11445		}
11446	}
11447}
11448
11449function renderResults() {
11450	var url = buildUrl();
11451	url = util.appendParamToURL(url, 'bootfinderviewresults');
11452
11453	$container.find('[step]').html(templates.results({
11454		Url: url
11455	}));
11456
11457	dataBuffer.$emailForm = $container.find('form.email');
11458	dataBuffer.emailValidator = dataBuffer.$emailForm.validate(ValidationSettings.Global);
11459	dataBuffer.$emailForm.find('input[name="email"]').rules('add', $.extend(ValidationRules.Email, {
11460		required: true,
11461		messages: {
11462			required: $.validator.format('Please enter an email address.')
11463		}
11464	}));
11465	dataBuffer.$emailForm.find('input[name="useragreement"]').rules('add', {
11466		required: true,
11467		messages: {
11468			required: $.validator.format('Please agree to the terms and conditions.')
11469		}
11470	});
11471
11472	if ($step.attr('loading')) {
11473		$step.css('height', '');
11474		$step.loading('destroy');
11475		$('html').animate({ scrollTop: 0 }, 250);
11476	}
11477	$container.find('[step-delta]').removeClass('disabled');
11478	$container.find('.next').addClass('disabled');
11479}
11480
11481function buildUrl(urlParameter, includeAdditionalData) {
11482	var url;
11483	if (dataBuffer && dataBuffer.stepData[options.Configuration.CategorySelectionStep] && dataBuffer.stepData[options.Configuration.CategorySelectionStep][options.Configuration.CategorySelectionSection]) {
11484		var categorySelectionSection = options.Configuration.Steps[options.Configuration.CategorySelectionStep].Sections[options.Configuration.CategorySelectionSection];
11485		var optionRelatedSelection = dataBuffer.stepData[categorySelectionSection.OptionsRelatedSelectionStep][categorySelectionSection.OptionsRelatedSelectionSection][0];
11486		var categorySelection = dataBuffer.stepData[options.Configuration.CategorySelectionStep][options.Configuration.CategorySelectionSection][0];
11487
11488		var categorySelectionIndex;
11489		var optionRelatedSelectionOptionIndex = 0;
11490		var optionRelatedSelectionOptions = categorySelectionSection.Options[optionRelatedSelection];
11491		while (!categorySelectionIndex && optionRelatedSelectionOptionIndex <= optionRelatedSelectionOptions.length) {
11492			if (optionRelatedSelectionOptions[optionRelatedSelectionOptionIndex] && optionRelatedSelectionOptions[optionRelatedSelectionOptionIndex].Text == categorySelection) {
11493				categorySelectionIndex = optionRelatedSelectionOptionIndex;
11494			}
11495			optionRelatedSelectionOptionIndex++;
11496		}
11497
11498		var categorySelectionData = categorySelectionSection.Options[optionRelatedSelection][categorySelectionIndex];
11499		if (urlParameter) {
11500			url = urlParameter;
11501		} else {
11502			url = categorySelectionData.Url;
11503		}
11504		var parameterIndex = 1;
11505		for (var categorySelectionDataKey in categorySelectionData) {
11506			var categorySelectionDataValue = categorySelectionData[categorySelectionDataKey];
11507			if (urlParameter && categorySelectionDataKey == 'cgid') {
11508				url = util.appendParamToURL(url, 'cgid', categorySelectionDataValue);
11509			} else if (categorySelectionDataKey != 'Text' && categorySelectionDataKey != 'Url' && categorySelectionDataKey != 'cgid') {
11510				url = util.appendParamToURL(url, 'prefn' + parameterIndex, categorySelectionDataKey);
11511				url = util.appendParamToURL(url, 'prefv' + parameterIndex, categorySelectionDataValue);
11512				parameterIndex++;
11513			}
11514		}
11515
11516		for (var stepIndexInner in options.Configuration.Steps) {
11517			if (stepIndexInner <= dataBuffer.step) {
11518				var stepInner = options.Configuration.Steps[stepIndexInner];
11519				for (var sectionIndexInner in stepInner.Sections) {
11520					var sectionInner = stepInner.Sections[sectionIndexInner];
11521					if (sectionInner.Refinement && sectionInner.Refinement != 'width') {
11522						var refinementValues;
11523						if (dataBuffer.stepData && dataBuffer.stepData[stepIndexInner] && dataBuffer.stepData[stepIndexInner][sectionIndexInner]) {
11524							refinementValues = dataBuffer.stepData[stepIndexInner][sectionIndexInner];
11525						} else {
11526							refinementValues = [];
11527						}
11528
11529						if (sectionInner.Refinement == 'size') {
11530							var widthSectionIndex = parseInt(sectionIndexInner, 10) + 1;
11531							var selectedWidths = dataBuffer.stepData[stepIndexInner][widthSectionIndex];
11532							var isDatabufferWidths = false;
11533							if (!selectedWidths || !selectedWidths.length) {
11534								selectedWidths = dataBuffer.widths;
11535								isDatabufferWidths = true;
11536							}
11537							var isDatabufferLengths = false;
11538							if (!refinementValues || !refinementValues.length) {
11539								refinementValues = dataBuffer.lengths;
11540								isDatabufferLengths = true;
11541							}
11542							var selectedSizes = [];
11543							if (!isDatabufferWidths || !isDatabufferLengths) {
11544								for (var refinementIndex in refinementValues) {
11545									var selectedLength = refinementValues[refinementIndex];
11546									for (var widthIndex in selectedWidths) {
11547										var selectedWidth = selectedWidths[widthIndex];
11548										var selectedSize = selectedLength + ' ' + selectedWidth;
11549										if (selectedSizes.indexOf(selectedSize) < 0) {
11550											selectedSizes[selectedSizes.length] = selectedSize;
11551										}
11552									}
11553								}
11554							}
11555							refinementValues = selectedSizes;
11556						}
11557
11558						if (refinementValues && refinementValues.length > 0) {
11559							if (sectionInner.Refinement == 'Price') {
11560								if (dataBuffer.Thresholds && dataBuffer.Thresholds[sectionInner.Refinement] && dataBuffer.Thresholds[sectionInner.Refinement][refinementValues]) {
11561									var threshold = dataBuffer.Thresholds[sectionInner.Refinement][refinementValues[0]];
11562									url = util.appendParamToURL(url, 'pmin', threshold.valueFrom);
11563									url = util.appendParamToURL(url, 'pmax', threshold.valueTo);	
11564								}
11565							} else {
11566								url = util.appendParamToURL(url, 'prefn' + parameterIndex, sectionInner.Refinement);
11567								url = util.appendParamToURL(url, 'prefv' + parameterIndex, refinementValues.join('|'));
11568								parameterIndex++;
11569							}
11570						}
11571					}
11572				}
11573			}
11574		}
11575		if (includeAdditionalData) {
11576			url = util.appendParamToURL(url, 'displayvalues', 'Price');
11577			url = util.appendParamToURL(url, 'thresholds', 'Price');
11578		}
11579	}
11580
11581	return url;
11582}
11583
11584function getRefinementCountsForSection(sectionIndex, section) {
11585	var promise = new Promise(function(resolve, reject) {
11586		$.ajax({
11587			type: 'get',
11588			url: buildUrl(Urls.getRefinementCounts, true),
11589			success: function(data) {
11590				dataBuffer = $.extend(dataBuffer, data);
11591
11592				var payload = {
11593					SectionIndex: sectionIndex,
11594					Section: section
11595				};
11596				if (section && section.Refinement) {
11597					payload.Refinements = data.Refinements[section.Refinement];
11598				}
11599				resolve(payload);
11600			}
11601		});
11602	});
11603
11604	return promise;
11605}
11606
11607function sendDataLayer() {
11608	GoogleTagManagerClient.PushDataLayerForEvent('bootfinderquiz', {
11609		step: dataBuffer.step,
11610		stepData: JSON.parse(JSON.stringify(dataBuffer.stepData))
11611	}, null, 'GoogleAnalytics4');
11612}
11613},{"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../components/LoadingIndicator":25,"../util":102,"lodash":133}],50:[function(require,module,exports){
11614'use strict';
11615
11616var dialog = require('../dialog');
11617
11618var quantityStepper = require('../quantityStepper');
11619var account = require('./account');
11620var ApplePay = require('./checkout/ApplePay');
11621var bonusProductsView = require('../bonus-products-view');
11622var quickview = require('../quickview');
11623var cartStoreInventory = require('../storeinventory/cart');
11624var ajax = require('../ajax');
11625var util = require('../util');
11626var formPrepare = require('./checkout/formPrepare');
11627var page = require('../page');
11628var tooltip = require('../tooltip');
11629var TPromise = require('promise');
11630var CheckoutConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants');
11631var DataLayerConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/DataLayerConstants');
11632var GoogleTagManagerClient = require('../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
11633var PaymentMethodConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
11634var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
11635
11636var isCartPage = false;
11637var dataBuffer = {};
11638var options;
11639
11640/**
11641 * @private
11642 * @function
11643 * @description Binds events to the cart page (edit item's details, bonus item's actions, coupon code entry)
11644 */ 
11645function initializeEvents() {	
11646    $(document).off('click', '#cart-table .item-edit-details a').on('click', '#cart-table .item-edit-details a', function (e) {
11647        e.preventDefault();
11648        
11649        quickview.show({
11650            url: e.target.href,
11651            source: 'cart'
11652        });
11653    });
11654    
11655    $(document).off('click', '#cart-table .bonus-item-actions a, #cart-table .item-details .bonusproducts a').on('click', '#cart-table .bonus-item-actions a, #cart-table .item-details .bonusproducts a', function (e) {
11656        e.preventDefault();
11657        
11658        bonusProductsView.show(this.href);
11659    });
11660    
11661    $(document).off('keydown', 'form input[name$="_couponCode"]').on('keydown', 'form input[name$="_couponCode"]', function (e) {   	
11662    	if (e.which === SiteConstants.KeyCodes.Enter) {
11663    		e.preventDefault();
11664    		
11665    		$('.error.coupon-error').remove();
11666    		if ($(document.activeElement).val().length === 0 || e.which === SiteConstants.KeyCodes.Enter && $(this).val().length === 0) {
11667				$('<span/>', {"class":"error coupon-error", text : Resources.COUPON_ADD_ERROR}).insertAfter('.cart-coupon-code .pcode-coupon');
11668				
11669				return false;
11670			} else {
11671				 $('button[name$="addCoupon"]').trigger('click');
11672			}
11673    	}   	 
11674    });
11675
11676    $(document).off('click', 'button[name$="deleteProduct"]').on('click', 'button[name$="deleteProduct"]', function (e) {
11677    	e.preventDefault();
11678    	
11679    	updateCart($(this).attr('name'));
11680    });
11681     
11682    $(document).off('click', 'button[name$="addCoupon"]').on('click', 'button[name$="addCoupon"]', function (e) {
11683	   	e.preventDefault();
11684	   	
11685	   	$('.error.coupon-error').text('');
11686	   	var $form = $('form#cart-items-form');
11687		var name = $(this).attr('name');
11688		
11689		(function() {
11690			return TPromise.resolve($.ajax({
11691	            type: 'POST',
11692	            url: util.ajaxUrl(Urls.onePageSubmit),
11693	            data: $form.serialize() + '&' + name + '=' + name + '&respType=json'
11694	        }))
11695		}()).then(function (response) {
11696			if (response.success && response.success !== CheckoutConstants.ResponseCodes.AlreadyInBasket) {				
11697				var couponCode = $('.pcode-coupon input[name*="_couponCode"]').val();
11698
11699				var dataObject = {
11700					creative_name: couponCode,
11701					creative_slot: 'couponcode',
11702					promotion_id: response.Promotion && response.Promotion.ID ? response.Promotion.ID : '',
11703					promotion_name: response.Promotion && response.Promotion.Name ? response.Promotion.Name : ''
11704				}
11705
11706				GoogleTagManagerClient.PushDataLayerForDataLayerType('SelectPromotion', dataObject);
11707				$(document).trigger('Cart.Update');
11708			} else {1
11709				var message = response.success == CheckoutConstants.ResponseCodes.AlreadyInBasket ? Resources.COUPON_ADD_ERROR_DUP : Resources.COUPON_ADD_ERROR;
11710				$('<span/>', {'class': 'error coupon-error', 'text': message}).insertAfter('.cart-coupon-code .pcode-coupon');
11711			}
11712	    }.bind(this));
11713	});
11714
11715    $(document).off('click', 'button[name$="deleteCoupon"]').on('click', 'button[name$="deleteCoupon"]', function (e) {
11716    	e.preventDefault();
11717    	
11718		var name = $(this).attr('name');
11719		(function(){
11720			return TPromise.resolve($.ajax({
11721	            type: 'POST',
11722	            url: util.ajaxUrl(Urls.onePageSubmit),
11723	            data: $('form#cart-items-form').serialize() + '&' + name + '=' + name + '&respType=json'
11724	        }))
11725		}()).then(function (response) {
11726			$(document).trigger('Cart.Update');
11727	    }.bind(this));
11728    });
11729    
11730    $(document).off('refresh-required').on('refresh-required', function(){
11731    	if ($('form#cart-items-form input[name$="_quantity"]').length > 0) {
11732            setTimeout( function(){ 
11733                $('form#cart-items-form input[name$="_quantity"]:first').trigger('focus').trigger('blur');
11734            }, 300);
11735    	}
11736    });
11737
11738    $(document).on('focus', 'input[name^="dwfrm_guestcheckout_username_"]', function () {
11739		$(this).removeClass('error');
11740		$('#dwfrm_guestcheckout span.error').remove();
11741    });
11742    
11743    $(document).off('keydown', 'form input[name^="dwfrm_guestcheckout_username_"]').on('keydown', 'input[name^="dwfrm_guestcheckout_username_"]', function (e) {   	
11744    	if (e.which === SiteConstants.KeyCodes.Enter) {		
11745    		e.preventDefault();
11746    		$('#dwfrm_guestcheckout span.error').remove();
11747    		
11748    		var $form = $('form.guest-checkout');
11749    		$form.validate().form();
11750    		
11751    		var email = $(document.activeElement).val();
11752    		var isEmailValid = validateEmail(email);
11753    		
11754    		if ($form.valid() && isEmailValid && $('#dwfrm_guestcheckout span.error').length < 1 ) {
11755    			renderGuestCheckout(email)
11756    		} else {
11757    			if ($('input[name^="dwfrm_guestcheckout_username_"].error').length == 0) {
11758    				invalidateGuestCheckout();
11759    			}
11760    		}
11761    	}   	 
11762    });
11763    
11764    $(document).off('click', 'button#guestCheckout').on('click', 'button#guestCheckout', function (e) {
11765    	e.preventDefault();
11766		$('#dwfrm_guestcheckout span.error').remove();
11767		
11768		var $form = $('form.guest-checkout');
11769		$form.validate().form();
11770		
11771		var email = $('input[name^="dwfrm_guestcheckout_username_"]').val();
11772		if ($('input[name^="dwfrm_guestcheckout_username_"].valid').length > 0) {
11773			var email = $('input[name^="dwfrm_guestcheckout_username_"].valid').val();
11774		}
11775		
11776		var isEmailValid = validateEmail(email);
11777    	
11778		if ($form.valid() && isEmailValid && $('#dwfrm_guestcheckout span.error').length < 1 ) {			
11779			renderGuestCheckout(email);
11780		} else {
11781			if ($('input[name^="dwfrm_guestcheckout_username_"].error').length == 0) {
11782				invalidateGuestCheckout();
11783			}
11784		}
11785    });
11786    
11787    $(document).off('click', '#editCustomerEmail').on('click', '#editCustomerEmail', function (e) {
11788		$('.auto-checkout-form').show();
11789    	$('.login-form').addClass('login-hidden');
11790    	clearFormErrors();
11791    });
11792    
11793    $(document).on('focus', 'input[name^="dwfrm_autocheckout_username_"], input[name^="dwfrm_login_"]', function () {
11794    	clearFormErrors();
11795    });
11796
11797    $(document).off('keydown', 'form input[name^="dwfrm_autocheckout_username_"]').on('keydown', 'input[name^="dwfrm_autocheckout_username_"]', function (e) {   	
11798    	if (e.which === SiteConstants.KeyCodes.Enter) {		
11799    		e.preventDefault();
11800    		$('#dwfrm_autocheckout span.error').remove();
11801    		
11802    		var $form = $('form.auto-checkout');
11803    		$form.validate().form();
11804    		
11805    		var email = $(document.activeElement).val();
11806    		var isEmailValid = validateEmail(email);
11807    		
11808    		if ($form.valid() && isEmailValid && $('#dwfrm_autocheckout span.error').length < 1 ) {
11809    			$('.displayAutoEmail').text(email);
11810    			$('input[name^="dwfrm_login_username_"]').val(email);
11811    			renderAutoCheckout(email)
11812    		} else {
11813    			if ($('input[name^="dwfrm_autocheckout_username_"].error').length == 0) {
11814    				invalidateAutoCheckout();
11815    			}
11816    		}
11817    	}   	 
11818    });
11819
11820    $(document).off('click', 'button#autoCheckout').on('click', 'button#autoCheckout', function (e) {
11821    	e.preventDefault();
11822    	$('#dwfrm_autocheckout span.error').remove();
11823    	
11824    	var $form = $('form.auto-checkout');
11825    	$form.validate().form();
11826    	
11827    	var email = $('input[name^="dwfrm_autocheckout_username_"]').val();
11828    	if ($('input[name^="dwfrm_autocheckout_username_"].valid').length > 0) {
11829    		var email = $('input[name^="dwfrm_autocheckout_username_"].valid').val();
11830    	}
11831    	
11832    	var isEmailValid = validateEmail(email);
11833    	
11834    	if ($form.valid() && isEmailValid && $('#dwfrm_autocheckout span.error').length < 1 ) {
11835    		$('.displayAutoEmail').text(email);
11836    		$('input[name^="dwfrm_login_username_"]').val(email);
11837    		renderAutoCheckout(email);
11838    	} else {
11839    		if ($('input[name^="dwfrm_autocheckout_username_"].error').length == 0) {
11840				invalidateAutoCheckout();
11841			}
11842    	}
11843    });
11844    
11845    $('.app-checkout #submitBilling').on('click', function(e){
11846    	e.preventDefault();
11847    	
11848    	var email = $('input[name^="dwfrm_billingaddress_email"]').val();
11849    	if (email.length > 0) {
11850    		var isEmailValid = validateEmail(email);
11851        	if (isEmailValid) {
11852        		$.ajax({
11853        			url: Urls.checkCustomerExists,
11854        			type: "get",
11855        			data: {email: email},
11856        			success: function (response) {
11857        				var data = JSON.parse(response);
11858        				$('.app-checkout').attr('customer-exists', data.customerExists);
11859        				$('.app-checkout').attr('has-rewards', data.hasRewards);
11860        			},
11861        			error: function(jqXHR, textStatus, errorThrown) {
11862        				console.log(textStatus, errorThrown);
11863        			}
11864        		});
11865        	}
11866    	}
11867    });
11868   
11869	$(document).off('click', '.order-total-payments [payment-method] .remove').on('click', '.order-total-payments [payment-method] .remove', function (e) {
11870		var paymentInstrumentUUID = $(e.currentTarget).closest('[payment-method').attr('uuid');
11871	
11872		if (paymentInstrumentUUID) {
11873			var requestPayload = {
11874				UUID: paymentInstrumentUUID
11875			}
11876		
11877			$.ajax({
11878				url: Urls.removePaymentInstrument,
11879				type: 'POST',
11880				contentType: 'application/json',
11881				data: JSON.stringify(requestPayload),
11882				dataType: 'json',
11883				success: function(data) {
11884					updateCart('dwfrm_cart_updateCart');
11885				}
11886			});
11887		}
11888	});
11889    
11890    $(document).on('Cart.Update', function (event, data) {
11891    	updateCart('dwfrm_cart_updateCart', data);
11892    });
11893}
11894
11895function validateEmail(email) {
11896	var isEmailValid = true;
11897	
11898	if (email.length == 0){
11899		isEmailValid = true;
11900	}
11901	
11902	var regexEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
11903	if (email.length > 0 && email.match(regexEmail) == null) {
11904		isEmailValid = false;
11905		invalidateGuestCheckout();
11906	}
11907	
11908	return isEmailValid;
11909}
11910
11911function clearFormErrors() {
11912	$('.form-checkout input[name^="dwfrm_"]').removeClass('error');
11913	$('.form-checkout span.error').remove();
11914	$('.form-checkout .error-box').remove();
11915}
11916
11917function invalidateGuestCheckout() {
11918	$('input[name^="dwfrm_guestcheckout_username_"]').removeClass('valid').addClass('error');
11919	$('.guest-checkout .field-wrapper').append('<span class="error custom-guestcheckout-error" style="display:block !important;">' + Resources.VALIDATE_EMAIL + '</span>');
11920}
11921
11922function renderGuestCheckout(email) {
11923	
11924	if(email.length > 0){
11925		var optIn = false;
11926		
11927		if(SitePreferences.CORDIAL_ENABLED){
11928			var signupEvent = '';
11929			
11930			var auth_data = {
11931				email: email
11932			}
11933			var contact_data = {
11934				'channels': {
11935					'email': {
11936						'address': email,
11937						'subscribeStatus': 'none'
11938					}
11939				},				    
11940				'workflow_checkout_abandon': true
11941			};
11942			
11943			var guestProperties = {
11944				'email': email,
11945				'workflow_checkout_abandon': true
11946			};
11947			
11948			if ($('#guest_checkout_optin').prop("checked")) {
11949				var auth_data = {
11950					email: email
11951				}
11952				var contact_data = {
11953					'channels': {
11954						'email': {
11955							'address': email,
11956							'subscribeStatus': 'subscribed'				            
11957						}
11958					},				    
11959					'signup_source': 'checkout',
11960					'welcome_signup': true,
11961					'workflow_checkout_abandon': true
11962				};
11963				
11964				var properties = {
11965					'signup_source': 'checkout',
11966					'welcome_signup': true,
11967					'workflow_checkout_abandon': true
11968				};
11969				
11970				var signupEvent = ['event', 'email_signup', properties];
11971			}
11972			
11973			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
11974				crdl([
11975					['contact', auth_data, contact_data],
11976					['event', 'guest_checkout', guestProperties],
11977					signupEvent
11978				]);
11979			}
11980		}
11981	} else {
11982		var optIn = $('#guest_checkout_optin').prop("checked");
11983	}
11984	
11985	$.ajax({
11986		url: Urls.setEmailOnCart,
11987		type: "get",
11988		data: {email: email},
11989		success: function (response) {
11990			var data = JSON.parse(response);
11991				
11992			if (data.status == 'success') {
11993				window.location.href = Urls.onePage + ((window.innerWidth < SiteConstants.Responsive.Widths.Mobile) ? '#addressinfo' : '');
11994			}
11995		},
11996		error: function(jqXHR, textStatus, errorThrown) {
11997			console.log(textStatus, errorThrown);
11998		}
11999	});
12000	
12001	$.ajax({
12002		url: Urls.setEmailOptin,
12003		type: "get",
12004		data: {optIn: optIn},
12005		success: function (response) {
12006			var data = JSON.parse(response);
12007				
12008			if (data.status == 'success') {
12009				window.location.href = Urls.onePage + ((window.innerWidth < SiteConstants.Responsive.Widths.Mobile) ? '#addressinfo' : '');
12010			}
12011		},
12012		error: function(jqXHR, textStatus, errorThrown) {
12013			console.log(textStatus, errorThrown);
12014		}
12015	});
12016}
12017
12018
12019function validateAutoEmail(email) {
12020	var isEmailValid = true;
12021	
12022	var regexEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
12023	if (email.length > 0 && email.match(regexEmail)){
12024		isEmailValid = true;
12025	} else {
12026		isEmailValid = false;
12027		invalidateGuestCheckout();
12028	}
12029	
12030	return isEmailValid;
12031}
12032
12033function invalidateAutoCheckout() {
12034	$('input[name^="dwfrm_autocheckout_username_"]').removeClass('valid').addClass('error');
12035	$('.auto-checkout .field-wrapper').append('<span class="error custom-autocheckout-error" style="display:block !important;">' + Resources.VALIDATE_EMAIL + '</span>');
12036}
12037
12038function renderAutoCheckout(email) {
12039	var gotocheckout = true;
12040	
12041	if(email.length > 0){
12042		var optIn = false;
12043		
12044		if(SitePreferences.CORDIAL_ENABLED){
12045			var signupEvent = '';
12046			
12047			var auth_data = {
12048				email: email
12049			}
12050			var contact_data = {
12051				'channels': {
12052					'email': {
12053						'address': email,
12054						'subscribeStatus': 'none'
12055					}
12056				},				    
12057				'workflow_checkout_abandon': true,
12058				'workflow_loyalty_transactional': true
12059			};
12060			
12061			var autoProperties = {
12062				'email': email,
12063				'workflow_checkout_abandon': true,
12064				'workflow_loyalty_transactional': true
12065			};
12066			
12067			if ($('#auto_checkout_optin').prop("checked")) {
12068				var auth_data = {
12069					email: email
12070				}
12071				var contact_data = {
12072					'channels': {
12073						'email': {
12074							'address': email,
12075							'subscribeStatus': 'subscribed'				            
12076						}
12077					},				    
12078					'signup_source': 'checkout',
12079					'welcome_signup': true,
12080					'workflow_checkout_abandon': true,
12081					'workflow_loyalty_transactional': false
12082				};
12083				
12084				var properties = {
12085					'signup_source': 'checkout',
12086					'welcome_signup': true,
12087					'workflow_checkout_abandon': true,
12088					'workflow_loyalty_transactional': false
12089				};
12090				
12091				var signupEvent = ['event', 'email_signup', properties];
12092			}
12093			
12094			if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
12095				crdl([
12096					['contact', auth_data, contact_data],
12097					['event', 'guest_checkout', autoProperties],
12098					signupEvent
12099				]);
12100			}
12101		}
12102	} else {
12103		var optIn = $('#auto_checkout_optin').prop("checked");
12104	}
12105	
12106	$.ajax({
12107		url: Urls.checkCustomerExists,
12108		type: "get",
12109		data: {email: email},
12110		success: function (response) {
12111			var data = JSON.parse(response);
12112				
12113			if (data.status == 'success') {
12114				gotocheckout = false;
12115				$('.auto-checkout-form').hide();
12116				$('.login-form').removeClass('login-hidden');
12117			} else {
12118				window.location.href = Urls.onePage + ((window.innerWidth < SiteConstants.Responsive.Widths.Mobile) ? '#addressinfo' : '');
12119			}
12120		},
12121		error: function(jqXHR, textStatus, errorThrown) {
12122			console.log(textStatus, errorThrown);
12123		}
12124	});
12125	
12126	$.ajax({
12127		url: Urls.setEmailOnCart,
12128		type: "get",
12129		data: {email: email},
12130		success: function (response) {
12131			var data = JSON.parse(response);
12132				
12133			if (data.status == 'success') {
12134				
12135			}
12136		},
12137		error: function(jqXHR, textStatus, errorThrown) {
12138			console.log(textStatus, errorThrown);
12139		}
12140	});
12141	
12142	$.ajax({
12143		url: Urls.setEmailOptin,
12144		type: "get",
12145		data: {optIn: optIn},
12146		success: function (response) {
12147			var data = JSON.parse(response);
12148				
12149			if (data.status == 'success') {
12150				
12151			}
12152		},
12153		error: function(jqXHR, textStatus, errorThrown) {
12154			console.log(textStatus, errorThrown);
12155		}
12156	});
12157}
12158
12159function triggerReactWebviewEvent() {
12160	if ('ReactNativeWebView' in window) {
12161		window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'cart-refresh' }));
12162	}
12163}
12164
12165var updateCartBuffer;
12166var updateCartXmlHttpRequestBuffer;
12167function updateCart(triggerElementName, updateCartOptions, isCartPage) {
12168	if (updateCartXmlHttpRequestBuffer) {
12169		for (var updateCartXmlHttpRequestKey in updateCartXmlHttpRequestBuffer) {
12170			var updateCartXmlHttpRequest = updateCartXmlHttpRequestBuffer[updateCartXmlHttpRequestKey];
12171			
12172			if (updateCartXmlHttpRequest.readyState !== 4) { 
12173				updateCartXmlHttpRequest.abort();
12174			}
12175		}
12176	}
12177	updateCartXmlHttpRequestBuffer = [];
12178	updateCartBuffer = [];
12179
12180	getCartHtml(triggerElementName, updateCartOptions, isCartPage);
12181	if (!isCartPage) {
12182		getOrderSummaryCalloutHtml(triggerElementName, updateCartOptions, isCartPage);
12183	}
12184}
12185
12186function getCartHtml(triggerElementName, updateCartOptions, isCartPage) {
12187    var form = $(".update-cart-button").parents('form');
12188    updateCartBuffer['Cart'] = null;
12189
12190	if (form.attr('action')) {
12191		if (isCartPage == undefined) {
12192			isCartPage = $('.pt_cart').length > 0;
12193		}
12194
12195		updateCartXmlHttpRequestBuffer['getCartHtml'] = $.ajax({
12196	        url : form.attr('action'),
12197		    type : "POST",
12198	        data : triggerElementName + '=' + triggerElementName + '&selectedPaymentMethod=' + $('input[name*="_selectedPaymentMethodID"]').val() + '&format=ajax',
12199		    success : function (data) {
12200		    	updateCartBuffer['Cart'] = data;
12201				if (pageContext.site == SiteConstants.SiteIds.BootBarnWorkforceUS) {
12202					getWorkforcePaymentPanel(triggerElementName, updateCartOptions, isCartPage);
12203				} else {
12204					updateCartBuffer['WorkforceVouchersPaymentPanel'] = true;
12205				}
12206				getCartData(triggerElementName, updateCartOptions, isCartPage);
12207				updateCartRender(triggerElementName, updateCartOptions, isCartPage);
12208		    }
12209		});
12210	}
12211}
12212
12213function getWorkforcePaymentPanel(triggerElementName, updateCartOptions, isCartPage) {
12214	updateCartBuffer['WorkforceVouchersPaymentPanel'] = null;
12215	updateCartBuffer['WorkforceVouchersPaymentPanelHtml'] = null;
12216
12217	updateCartXmlHttpRequestBuffer['getWorkforceVouchersPaymentPanelHtml'] = $.ajax({
12218		url: Urls.Workforce.Vouchers.PaymentPanel.Get,
12219		dataType: 'html',
12220		success: function (data) {
12221			updateCartBuffer['WorkforceVouchersPaymentPanel'] = true;
12222			updateCartBuffer['WorkforceVouchersPaymentPanelHtml'] = $(data).html();
12223			updateCartRender(triggerElementName, updateCartOptions, isCartPage);
12224		}
12225	});
12226}
12227
12228function getOrderSummaryCalloutHtml(triggerElementName, updateCartOptions, isCartPage) {
12229    updateCartBuffer['OrderSummaryCallout'] = null;
12230
12231	updateCartXmlHttpRequestBuffer['getOrderSummaryCalloutHtml'] = $.ajax({
12232		url: Urls.getOrderSummaryCalloutContentSlot,
12233		dataType: 'html',
12234		success: function (data) {
12235			updateCartBuffer['OrderSummaryCallout'] = data;
12236			updateCartRender(triggerElementName, updateCartOptions, isCartPage);
12237		}
12238	});
12239}
12240
12241function getCartData(triggerElementName, updateCartOptions, isCartPage) {
12242	updateCartBuffer['CartData'] = null;
12243
12244	updateCartXmlHttpRequestBuffer['getCartData'] = $.ajax({
12245	    url : Urls.getCartData,
12246		type : "POST",
12247		data: {
12248			isCart: isCartPage
12249		},
12250	    success : function (data) {
12251	    	$('[has-local-delivery-shipment]').attr('has-local-delivery-shipment', data.HasLocalDeliveryShipment || false);
12252	    	$('[has-physical-shipment]').attr('has-physical-shipment', data.HasPhysicalShipment || false);
12253			$('[has-warehouse-shipment]').attr('has-warehouse-shipment', data.HasWarehouseShipment || false);
12254			$('[has-warehouse-shipment]').attr('has-shipping-methods', data.ShippingMethods ? data.ShippingMethods.join(' ') : false);
12255			$('[has-subscription-products]').attr('has-subscription-products', data.HasSubscriptionProducts || false);
12256	    	$('[has-gift-cards]').attr('has-gift-cards', data.HasGiftCards || false);
12257	    	$('[has-gift-cards-only]').attr('has-gift-cards-only', data.HasGiftCardsOnly || false);
12258			$('[has-store-pickup-shipment]').attr('has-store-pickup-shipment', (data.Bopis && data.Bopis.HasStorePickupShipment) || false);
12259			$('[has-store-pickup-shipment-only]').attr('has-store-pickup-shipment-only', (data.Bopis && data.Bopis.HasStorePickupShipmentOnly) || false);
12260
12261			if (data.Bopis && data.Bopis.HasStorePickupShipmentOnly) {
12262				$('#isShippingAddressSameAsBillingAddress').prop('checked', true);
12263				$('input[name$="_useAsShippingAddress"]').prop('checked', true);
12264			}
12265
12266	    	$(document).trigger('CartData.Update', data);
12267
12268	    	updateCartBuffer['CartData'] = data;
12269	    	if (data.Bopis) {
12270	    		updateCartBuffer['Bopis'] = data.Bopis;
12271	    	}
12272	    	
12273	    	if (!data.IsGlobaleValidOrInactive) {
12274	    		$('.section-checkout').addClass('global-e-validation-error');
12275	    		$('.section-checkout').find('.cart-errors').addClass('active');
12276	    	} else {
12277	    		$('.section-checkout').removeClass('global-e-validation-error');
12278	    		$('.section-checkout').find('.cart-errors').removeClass('active');
12279	    	}
12280
12281	    	if ((!data.OrderTotal || !data.OrderTotal.balanceAvailable) || (data.ShippingRestrictions && data.ShippingRestrictions.Status == 'shippingRestrictionsError')) {
12282				if (!isCartPage) {
12283					$('.payment-methods-section').hide();
12284				}
12285			} else {
12286				if (isCartPage) {
12287					$('.guest-checkout .error-form').remove();
12288				} else {
12289					$('.payment-methods-section').show();
12290				}
12291			}
12292	    	
12293	    	handleOrderTotal(triggerElementName, updateCartOptions, isCartPage);
12294	    }
12295	});
12296}
12297
12298function handleOrderTotal(triggerElementName, updateCartOptions, isCartPage) {
12299	updateCartBuffer['PaymentInstrumentHtml'] = null;
12300	var removeAction = '<a class="remove">Remove</a>';
12301	var rewardsProgram = '';
12302	if (pageContext.site == SiteConstants.SiteIds.CountryOutfittersUS) {
12303		rewardsProgram = 'Club CO';
12304	}
12305	if (pageContext.site == SiteConstants.SiteIds.SheplersUS) {
12306		rewardsProgram = 'Sheplers';
12307	}
12308	
12309	if (updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length > 0) {
12310		updateCartBuffer['PaymentInstrumentHtml'] = '';
12311	
12312		for (var paymentInstrumentKey in updateCartBuffer['CartData'].OrderTotal.paymentInstruments) {
12313			var paymentInstrument = updateCartBuffer['CartData'].OrderTotal.paymentInstruments[paymentInstrumentKey];
12314			var paymentInstrumentUUIDAttribute = 'uuid="' + paymentInstrument.UUID + '"';
12315			var showYouPayLabel = false;
12316
12317			switch (paymentInstrument.paymentMethod.Id) {
12318				case PaymentMethodConstants.WorkforceVoucher:
12319					showYouPayLabel = true;
12320		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
12321						'<i class="fa-solid fa-ticket fa-animated"></i>' +
12322		    			'<span class="label"><span class="name">' + 'Company Pays' + '</span>&nbsp;<span class="number">' + paymentInstrument.number + '</span>' + removeAction + '</span>' +
12323		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
12324		    		'</div>';
12325		    	break;
12326
12327				case PaymentMethodConstants.RewardsCredit:
12328		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
12329		    			'<span class="label"><span class="name">B Rewarded</span>&nbsp;<span class="number">Reward</span></span>' +
12330		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
12331		    		'</div>';
12332		    	break;
12333		    	
12334				case PaymentMethodConstants.LoyaltyCredit:
12335		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
12336		    			'<span class="label"><span class="name">'+rewardsProgram+'</span>&nbsp;<span class="number">Reward</span></span>' +
12337		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
12338		    		'</div>';
12339		    	break;
12340			
12341				default:
12342		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
12343		    			'<span class="label"><span class="name">' + paymentInstrument.paymentMethod.Name + '</span>&nbsp;<span class="number">' + paymentInstrument.number + '</span>' + removeAction + '</span>' +
12344		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
12345		    		'</div>';
12346		    	break;
12347		    }
12348		}
12349	} else {
12350		$(document).trigger('GiftCard.Clear');
12351		$(document).trigger('RewardsCredit.Clear');
12352		$(document).trigger('LoyaltyCredit.Clear');
12353	}
12354
12355	var balanceDueLabel = showYouPayLabel ? 'You Pay' : 'Balance Due';
12356	if (updateCartBuffer['CartData'].OrderTotal.balance < updateCartBuffer['CartData'].OrderTotal.total) {
12357		updateCartBuffer['BalanceDueHtml'] = '<span class="label">' + balanceDueLabel + '</span>' +
12358			'<span class="value">' + updateCartBuffer['CartData'].OrderTotal.balanceDisplay + '</span>';
12359	}
12360	
12361	updateCartRender(triggerElementName, updateCartOptions, isCartPage);
12362}
12363
12364function updateCartRender(triggerElementName, updateCartOptions, isCartPage) {
12365	if (updateCartBuffer['Cart'] && (isCartPage || (updateCartBuffer['OrderSummaryCallout'] && updateCartBuffer['CartData'])) && updateCartBuffer['WorkforceVouchersPaymentPanel']) {
12366		var $basketContainerHtml = $(updateCartBuffer['Cart']);
12367	
12368		if (updateCartBuffer['CartData'] && updateCartBuffer['CartData'].OrderTotal) {
12369			$basketContainerHtml.find('.order-total-price .value').html(updateCartBuffer['CartData'].OrderTotal.totalDisplay);
12370	    	$('.payment-section .order-total-price .value').html(updateCartBuffer['CartData'].OrderTotal.totalDisplay);
12371		
12372			if (updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length > 0) {
12373				$basketContainerHtml.find('.order-totals-table').attr('payments-applied', updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length);
12374	    		$('.payment-section').attr('payments-applied', updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length);
12375	    	} else {
12376	    		$basketContainerHtml.find('.order-totals-table').removeAttr('payments-applied');
12377	    		$('.payment-section').removeAttr('payments-applied');
12378	    	}
12379		
12380			$basketContainerHtml.find('.order-totals-table').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
12381	    	$('body').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
12382			$('.payment-section').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
12383		
12384			if (updateCartBuffer['CartData'].OrderTotal.balance > 0) {
12385				$('.payment-method-options').show();
12386				$('.payment-section-footer[data-method="PayPal"] button').hide();
12387			} else {
12388				$('.payment-method-options').hide();
12389				$('.payment-section-footer[data-method="PayPal"] button').show();
12390			}
12391		
12392	    	if (updateCartBuffer['BalanceDueHtml']) {	
12393	    		$basketContainerHtml.find('.order-balance-due').html(updateCartBuffer['BalanceDueHtml']);
12394	    		$('.payment-section .order-balance-due').html(updateCartBuffer['BalanceDueHtml']);    		
12395	    	} else {
12396	    		$('.payment-section .order-balance-due').html('');
12397	    	}
12398	    	
12399	    	if (updateCartBuffer['PaymentInstrumentHtml']) {
12400	    		$basketContainerHtml.find('.order-total-payments').html(updateCartBuffer['PaymentInstrumentHtml']);
12401	    		$('.payment-section .order-total-payments').html(updateCartBuffer['PaymentInstrumentHtml']);
12402	    	} else {
12403	    		$('.payment-section .order-total-payments').html('');
12404	    	}
12405
12406			if (updateCartBuffer['WorkforceVouchersPaymentPanelHtml']) {
12407				$('.payment-methods-section .opc-summary [payment-method="Workforce_Voucher"]').html(updateCartBuffer['WorkforceVouchersPaymentPanelHtml']);
12408			}
12409		}
12410
12411		$('.basket-container').html($basketContainerHtml);
12412		if ($('.approaching-discount-progress-bar-container.hidden').length > 0) {
12413			$('.approaching-discount-progress-bar-container:not(.hidden)').html($('.approaching-discount-progress-bar-container.hidden').html());
12414		} else {
12415			$('.approaching-discount-progress-bar-container:not(.hidden)').html('');
12416		}
12417		if (updateCartBuffer['CartData']) {
12418			var orderTotalForAffirm = updateCartBuffer['CartData'].OrderTotal.balance;
12419			if (!orderTotalForAffirm && updateCartBuffer['CartData'].OrderTotal.adjustedMerchandizeTotalPrice) {
12420				orderTotalForAffirm = updateCartBuffer['CartData'].OrderTotal.adjustedMerchandizeTotalPrice;
12421			}
12422			if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui && orderTotalForAffirm >= updateCartBuffer['CartData'].AffirmMinimumOrderTotal) {
12423				$('.affirm-product-modal').attr('data-amount', orderTotalForAffirm * 100);
12424				affirm.ui.refresh();
12425			} else {
12426				$('.affirm-product-modal').attr('data-amount', 5000);
12427			}
12428		}
12429
12430		if ($('.basket-container').find('.product-availability-list .not-available').length > 0) {
12431			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').attr('disabled', 'disabled');
12432			$('#errorBoxOPC').remove();
12433			$('#submitOPCPayment').after('<div id="errorBoxOPC" class="p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>' + Resources.Validation.Messages.Products.Availability + ':</b> ' + Resources.Validation.Messages.Products.AvailabilityExceeded + '</div>');
12434		} else if (updateCartBuffer['Bopis'] && updateCartBuffer['Bopis'].NumberOfBopisStoresInCart > updateCartBuffer['Bopis'].MaximumNumberOfBopisStoresInCart) {
12435			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').attr('disabled', 'disabled');
12436			$('#errorBoxOPC').remove();
12437			$('#submitOPCPayment').after('<div id="errorBoxOPC" class="p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>' + Resources.Validation.Messages.General.CartLimit + ':</b> ' + Resources.Validation.Messages.Bopis.MaximumStoresExceeded + '</div>');
12438		} else {
12439			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').removeAttr('disabled');
12440			$('#errorBoxOPC').remove();
12441		}
12442		
12443		if (updateCartBuffer['OrderSummaryCallout']) {	
12444			$('.callout-ordersummary').html(updateCartBuffer['OrderSummaryCallout']);
12445		}
12446		if (SitePreferences.ID_ME_DEFAULT_BUTTON) {
12447			document.dispatchEvent(new CustomEvent('idme:bind'));
12448		}
12449	    if (SitePreferences.STORE_PICKUP) {
12450	        cartStoreInventory.init();
12451	    }
12452	    
12453	    account.initCartLogin();
12454	    checkCheckoutAccessibility();
12455
12456		var $cartMiniCartButton = $('.banner-cart-holder .btn-minicart');
12457	    if ($cartMiniCartButton.length > 0) {
12458			$.ajax({url : Urls.cartMiniCart, data : {desktop: '1'}, dataType: 'html', success : function(data) {
12459	            $('.banner-cart-holder').html(data);
12460	        }});
12461			
12462	    	$.ajax({url : Urls.cartMiniCart, dataType: 'html', success : function(data) {
12463	    		var parentMiniCart = $(".top-banner-inner-links #mini-cart");
12464	    		
12465	    		if (parentMiniCart.length > 0) {
12466	    			parentMiniCart.html(data);
12467	    		}
12468	        }});
12469		}
12470	    
12471        $.ajax({url: Urls.cartItemQuantity, data: {desktop: '1'}, dataType: 'html', success: function (data) {
12472        	$('.confirmation-message .item-count').html(data);
12473	        
12474	        var quantity = parseInt($('.confirmation-message .item-count').find('span').text(), 10);
12475	        if (!quantity) {
12476	            $('.checkout-buttons.mobile-only').hide();	            
12477			}
12478		}});
12479		
12480	    
12481	    if (triggerElementName.indexOf('deleteProduct') > -1) {
12482	    	$(document).trigger('refresh-required');
12483	    }
12484
12485		if (!options || !options.EventTriggers || options.EventTriggers['update-summary']) {
12486			$(document).trigger('update-summary');
12487		}
12488		
12489		if (!options || !options.EventTriggers || options.EventTriggers['update-shipping']) {
12490			$(document).trigger('update-shipping');
12491		}
12492	    
12493	    $('[minimum-cart-total], [maximum-cart-total]').each(function(index, element) {
12494	    	var $this = $(this);
12495	    	var paymentMethodCartTotalMinimum = parseFloat($this.attr('minimum-cart-total'));
12496	    	var paymentMethodCartTotalMaximum = parseFloat($this.attr('maximum-cart-total'));
12497	    
12498	    	var isCartTotalMinimumValid = false;
12499	    	if (isNaN(paymentMethodCartTotalMinimum) || updateCartBuffer['CartData'].OrderTotal.balance >= paymentMethodCartTotalMinimum) {
12500	    		isCartTotalMinimumValid = true;
12501	    	}
12502	    	
12503	    	var isCartTotalMaximumValid = false;
12504	    	if (isNaN(paymentMethodCartTotalMaximum) || updateCartBuffer['CartData'].OrderTotal.balance <= paymentMethodCartTotalMaximum) {
12505	    		isCartTotalMaximumValid = true;
12506	    	}
12507	    	
12508	    	if (isCartTotalMinimumValid && isCartTotalMaximumValid) {
12509	    		$this.closest('.payment-method-option').attr('valid-for-balance-due', 'true');
12510	    	} else {
12511	    		$this.closest('.payment-method-option').attr('valid-for-balance-due', 'false');
12512	    	}
12513	    });
12514		
12515	    if (updateCartBuffer['CartData'] && !updateCartBuffer['CartData'].HasPhysicalShipment && $('.single.shipping-address-type-selector input:checked').val() != SiteConstants.AddressTypes.Customer) {
12516	    	$('.single.shipping-address-type-selector input[value="' + SiteConstants.AddressTypes.Customer + '"]').click();
12517	    }
12518		
12519		tooltip.init({
12520			items: '.shipping-method-tooltip',
12521			tooltipClass: 'shipping-method-tooltip'
12522		}, $('[shipping-method]'));
12523
12524		tooltip.init({
12525			items: '.tooltip'
12526		});
12527
12528		triggerReactWebviewEvent();
12529		triggerSrFocus();
12530		$(document).trigger('QuantityStepper.TargetFocus');
12531	    $(document).trigger('Cart.UpdateSuccess');
12532	}
12533}
12534
12535function triggerSrFocus() {
12536	$('[role="alert"]:visible').last().focus().addClass('focus-visible-indicator');
12537}
12538
12539function checkCheckoutAccessibility() {
12540	if ($('.order-item').length == 0) {
12541		$('.section-checkout > div').removeClass('active');
12542		$('.billing-address-form, .shipping-address-cont, .shipping-payment-cont').hide();
12543		$('.form-checkout.login-element, .section-checkout').addClass('empty-cart');
12544	}
12545}
12546
12547/**
12548 * @description Make the AJAX request to get customer data
12549 * @param {Element} form The form element that contains the item quantity and ID data
12550 * @param {Element} Action name to apply to form
12551 * @returns {Promise}
12552 */
12553var updateCustomerDetails = function (form, name) {
12554    var $form = $(form);
12555
12556    return TPromise.resolve($.ajax({
12557        type: 'POST',
12558        url: util.ajaxUrl(Urls.onePageLogin),
12559        data: $form.serialize() + '&' + name + '=' + name
12560    }));
12561};
12562
12563/**
12564 * @description Make the AJAX request to reset customer password
12565 * @param {String} registered customer email
12566 * @returns {Promise}
12567 */
12568var resetCustomerPassword = function (email) {
12569	return TPromise.resolve($.ajax({
12570        type: 'POST',
12571        url: util.ajaxUrl(Urls.resetOPCPass),
12572        data: 'email='+ email
12573    }));
12574};
12575
12576/**
12577 * @description Make the AJAX request to reset customer password
12578 * @param {String} error message
12579 * @param {Element} element to add error after
12580 * @returns {Void}
12581 */
12582var showError = function(message, selector){
12583	var $errorContainer = selector.closest('field-wrapper').next('.error');
12584
12585	if (!$errorContainer.prop('defaultText') || $errorContainer.text() != message) {
12586		selector.prop('defaultText', $errorContainer.text());
12587	}
12588	
12589	$errorContainer.text(message).show();
12590};
12591
12592/**
12593 * @description Make the AJAX request to reset customer password
12594 * @param {String} notification text
12595 * @param {Element} selector to apply message
12596 * @param {String} notification type: error or success
12597 * @returns {Void}
12598 */
12599function showNotification(text, selector, msgType) {
12600	var $selector = $(selector);
12601	if ($selector.length == 0) {
12602		var $form = $('form.opc-login');
12603		$selector = $('<span/>', {'class': 'login-notification'}).wrap('<div class="formfield clearfix error-box"><div class="form-row required"><div class="field-wrapper"></div></div></div>');
12604
12605		$selector.parent().parent().parent().insertBefore('form.opc-login .form-row-button');
12606		$selector = $(selector);
12607	}
12608
12609	$selector.removeClass('success error').addClass(msgType).text(text);
12610};
12611
12612/**
12613 * @description Make the AJAX request to reset customer password
12614 * @param {Element} selector to clear notification type: error or success
12615 * @returns {Void}
12616 */
12617function clearNotification(selector){
12618	if (selector.length) {
12619		$('.formfield.error-box').remove();
12620		selector.removeClass('success error');
12621	}
12622};
12623
12624exports.updateCart = updateCart;
12625exports.init = function (optionsParameter) {
12626	options = optionsParameter;
12627	if (options && options.isCartPage) {
12628    	isCartPage = true;
12629    }
12630    initializeEvents();
12631    if (SitePreferences.STORE_PICKUP) {
12632        cartStoreInventory.init();
12633    }
12634	ApplePay.initialize(options);
12635    account.initCartLogin();
12636	quantityStepper.init(updateCart);
12637    updateCart('dwfrm_cart_updateCart', null, options.isCartPage);
12638
12639
12640
12641	$(document).off('click', '.discount-custom-button').on('click', '.discount-custom-button', function(event) {
12642        event.preventDefault();
12643        
12644        dialog.open({
12645            html: $('.discount-dialog-content').html(),
12646            options: {
12647                title: 'Thorogood Boot Barn Discount',
12648                dialogClass: 'fixed',
12649                draggable: false,
12650                open: function(event, ui) {
12651	            	$('body').addClass('dialog-open');
12652	            },
12653	            close: function(event, ui) {
12654	            	$('body').removeClass('dialog-open');
12655	            }
12656            }
12657        });
12658	});	
12659};
12660
12661},{"../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants":113,"../../../../bc_sheplersbootbarn_core/cartridge/js/DataLayerConstants":114,"../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../ajax":3,"../bonus-products-view":6,"../dialog":36,"../page":46,"../quantityStepper":87,"../quickview":88,"../storeinventory/cart":96,"../tooltip":101,"../util":102,"./account":48,"./checkout/ApplePay":53,"./checkout/formPrepare":61,"promise":134}],51:[function(require,module,exports){
12662'use strict';
12663
12664var addToCart = require('./product/addToCart');
12665var quantityStepper = require('../quantityStepper');
12666var loadingIndicator = require('../components/LoadingIndicator');
12667var _ = require('lodash');
12668var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
12669
12670var html = {};
12671var options = {};
12672var data = {};
12673var templates = {};
12674
12675var video;
12676var stream;
12677var barcodeDetector;
12678var barcodeDetectorInterval;
12679var barcodeScanInputAddProductTimer;
12680var barcodeScanInputFocusInterval;
12681var productSearchInputFocusInterval;
12682
12683var $productFinder;
12684var $barcodeScanner;
12685var $barcodeScannerInputContainer;
12686var $barcodeScannerInput;
12687var $barcodeScannerErrorContainer;
12688var $barcodeScannerAddButton;
12689var $barcodeScannerAddButtonText;
12690var $barcodeScannerStartButton;
12691var $barcodeScannerStartButtonText;
12692var $barcodeScannerModal;
12693var $productSearch;
12694var $productSearchInputContainer;
12695var $productSearchInput;
12696var $productSearchErrorContainer;
12697var $productSearchSearchButton;
12698var $productSearchSearchButtonText;
12699var $productSearchResultsContainer;
12700var $productSearchResults;
12701var $productSearchResultsFooter;
12702var $productSearchResultsLoadMore;
12703
12704function initialize(options) {
12705	$productFinder = $('.product-finder');
12706	$barcodeScanner = $productFinder.find('.section.scan');
12707	$barcodeScannerInputContainer = $barcodeScanner.find('.input');
12708	$barcodeScannerInput = $barcodeScannerInputContainer.find('input');
12709	$barcodeScannerErrorContainer = $barcodeScanner.find('.error');
12710	$barcodeScannerAddButton = $barcodeScanner.find('.action.add');
12711	$barcodeScannerAddButtonText = $barcodeScannerAddButton.find('span');
12712	$barcodeScannerStartButton = $barcodeScanner.find('.action.start');
12713	$barcodeScannerStartButtonText = $barcodeScannerStartButton.find('span');
12714	$barcodeScannerModal = $barcodeScanner.find('.modal');
12715	$productSearch = $productFinder.find('.section.search');
12716	$productSearchInputContainer = $productSearch.find('.input');
12717	$productSearchInput = $productSearchInputContainer.find('input');
12718	$productSearchErrorContainer = $productSearch.find('.error');
12719	$productSearchSearchButton = $productSearch.find('.action.search');
12720	$productSearchSearchButtonText = $productSearchSearchButton.find('span');
12721	$productSearchResultsContainer = $productSearch.find('.results-container');
12722	$productSearchResults = $productSearchResultsContainer.find('.results');
12723	$productSearchResultsFooter = $productSearchResultsContainer.find('footer');
12724	$productSearchResultsLoadMore = $productSearchResultsContainer.find('.load-more');
12725
12726	if (('BarcodeDetector' in window)) {
12727		$barcodeScanner.addClass('barcode-detector');
12728	}
12729
12730	data['ProductSearchPageSize'] = 5;
12731
12732    templates['variations'] = _.template($('#template-variations').html(), TemplateConstants.Lodash.Settings);
12733    templates['variationSelector'] = _.template($('#template-variations-selector').html(), TemplateConstants.Lodash.Settings);
12734	templates['variationSelectorAttributeGuide'] = _.template($('#template-variations-attribute-guide').html(), TemplateConstants.Lodash.Settings);
12735    templates['variationSelectorDropdown'] = _.template($('#template-variations-selector-dropdown').html(), TemplateConstants.Lodash.Settings);
12736    templates['variationSelectorDropdownOption'] = _.template($('#template-variations-selector-dropdown-option').html(), TemplateConstants.Lodash.Settings);
12737    templates['variationSelectorDropdownOptionSelected'] = _.template($('#template-variations-selector-dropdown-option-selected').html(), TemplateConstants.Lodash.Settings);
12738    templates['variationSelectorSwatch'] = _.template($('#template-variations-selector-swatch').html(), TemplateConstants.Lodash.Settings);
12739    templates['variationSelectorSwatchOption'] = _.template($('#template-variations-selector-swatch-option').html(), TemplateConstants.Lodash.Settings);
12740    templates['variationSelectorSwatchOptionUnselectable'] = _.template($('#template-variations-selector-swatch-option-unselectable').html(), TemplateConstants.Lodash.Settings);
12741
12742	// $(document).off('keyup.BarcodeScannerInput').on('keyup.BarcodeScannerInput', '.input', function(event) {
12743	// 	var $this = $(this);
12744
12745	// 	$barcodeScannerErrorContainer.html('');
12746	// 	if (barcodeScanInputAddProductTimer) {
12747	// 		clearTimeout(barcodeScanInputAddProductTimer);
12748	// 	}
12749
12750	// 	barcodeScanInputAddProductTimer = setTimeout(function() {
12751	// 		addProductWithUpc($this.val());
12752	// 	}, 250);
12753	// });
12754
12755	$productFinder.off('click.Add').on('click.Add', '.action.add', function(event) {
12756		event.preventDefault();
12757		addProductWithUpc($barcodeScannerInput.val());
12758	});
12759
12760	$productFinder.off('click.OptionsToggle').on('click.OptionsToggle', '.action.options', function(event) {
12761		$(this).addClass('active');
12762	});
12763
12764	$productFinder.off('click.ModeSet').on('click.ModeSet', '.options [set-mode]', function(event) {
12765		$barcodeScannerErrorContainer.html('');
12766		$productSearchErrorContainer.html('');
12767		$('.product-finder').attr('mode', $(this).attr('set-mode'));
12768		$('.action.options').removeClass('active');
12769	});
12770
12771	$(document).off('click.OptionsClose').on('click.OptionsClose', function(event) {
12772		var $target = $(event.target);
12773		if (!$target.hasClass('options') && $target.closest('.options').length == 0) {
12774			$('.action.options').removeClass('active');
12775		}
12776	});
12777
12778	$barcodeScanner.off('click.KeyboardToggle').on('click.KeyboardToggle', '.options .keyboard', function(event) {
12779		if ($(this).hasClass('active')) {
12780			$(this).removeClass('active');
12781			$barcodeScannerInput.attr('inputmode', 'none');
12782		} else {
12783			$(this).addClass('active');
12784			$barcodeScannerInput.attr('inputmode', $barcodeScannerInput.attr('type'));
12785		}
12786		$('.action.options').removeClass('active');
12787	});
12788
12789	$barcodeScanner.off('click.BarcodeScannerStart').on('click.BarcodeScannerStart', '.action.start', function(event) {
12790		if (!('BarcodeDetector' in window)) {
12791			alert('Barcode Detector is not supported by this browser.');
12792  		} else {
12793			$('.section.scan').addClass('active');
12794
12795			if (!barcodeDetector) {
12796				barcodeDetector = new BarcodeDetector();
12797			}
12798
12799			video = $barcodeScannerModal.find('video')[0];
12800			video.setAttribute('playsinline', '');
12801			video.setAttribute('autoplay', '');
12802			video.setAttribute('muted', '');
12803			video.style.width = '100%';
12804			video.style.height = '100%';
12805
12806			var constraints = {
12807				audio: false,
12808				video: {
12809					facingMode: 'environment'
12810				}
12811			};
12812			
12813			navigator.mediaDevices.getUserMedia(constraints).then(function success(mediaStream) {
12814				stream = mediaStream;
12815				video.srcObject = stream;
12816			});
12817
12818			video.onloadedmetadata = function(event) {
12819				barcodeDetectorInterval = setInterval(detectBarcode, 500);
12820			};
12821		}
12822	});
12823
12824	$(document).off('click.BarcodeScannerStop').on('click.BarcodeScannerStop', '.modal .close', function(event) {
12825		closeBarcodeScanner();
12826	});
12827
12828	$(document).off('paste.UPC').on('paste.UPC', function(event) {
12829		if (!$barcodeScannerInput.is(":focus") && $(event.target).closest('.product-finder').length > 0) {
12830			getClipboardValue(event, this, function(upc) {
12831				$barcodeScannerErrorContainer.html('');
12832				addProductWithUpc(upc);
12833			});
12834		}
12835	});
12836
12837	$productSearchInput.off('keyup.ProductSearchChange').on('keyup.ProductSearchChange', function(event) {
12838		updateProductSearchAction();
12839	});
12840
12841	$productSearch.off('click.ProductSearchStart').on('click.ProductSearchStart', '.action.search', function(event) {
12842		event.preventDefault();
12843		var query = $productSearchInput.val();
12844		if (query) {
12845			if ($productSearchInputContainer.attr('action') == 'search') {
12846				$productSearchResults.html('');
12847				$productSearch.removeClass('active');
12848				data['ProductSearchPage'] = 1;
12849				searchProductsWithQuery(query);
12850			} else {
12851				resetProductSearch();
12852			}
12853		}
12854	});
12855
12856	$productSearch.off('click.ProductSearchLoadMore').on('click.ProductSearchLoadMore', '.load-more', function(event) {
12857		var query = $productSearchInput.val();
12858		if (query) {
12859			data['ProductSearchPage'] += 1;
12860			searchProductsWithQuery(query);
12861		}
12862	});
12863
12864	$productSearch.off('click.ProductSearchProductSelect').on('click.ProductSearchProductSelect', '.grid-tile .product-tile a', function(event) {
12865		event.preventDefault();
12866
12867		var $this = $(this);
12868		var $gridTile = $this.closest('.grid-tile');
12869		var $productTile = $this.closest('.product-tile');
12870		if ($gridTile.find('.product-variations').length > 0) {
12871			$gridTile.find('.product-variations').remove();
12872		} else {
12873			getStylesForProduct($productTile.data('itemid'));
12874		}
12875	});
12876
12877	$productSearch.off('change.ProductSearchAttributeSelect').on('change.ProductSearchAttributeSelect', '.grid-tile .product-variations select', function(event) {
12878		var $productTile = $(this).closest('.grid-tile').find('.product-tile');
12879		getStylesForProduct($productTile.data('itemid'));
12880	});
12881
12882	$productSearch.off('click.ProductSearchProductAdd').on('click.ProductSearchProductAdd', '.grid-tile .product-variations .btn.add', function(event) {
12883		var $this = $(this);
12884		if ($this.attr('pid')) {
12885			addProductWithProductId($this.attr('pid'), $this);
12886		}
12887	});
12888
12889	$productSearch.off('click.ProductSearchClose').on('click.ProductSearchClose', '.results-container .close', function(event) {
12890		$('body').removeClass('product-search');
12891	});
12892
12893	if (!options || options.StoreUIVersion != '2024Redesign') {
12894		barcodeScanInputFocusInterval = setInterval(function() {
12895			if (!$barcodeScannerInput.is(':visible:focus') && $barcodeScanner.find(':focus').length == 0) {
12896				$barcodeScannerInput.focus();
12897			}
12898		}, 500);
12899	
12900		productSearchInputFocusInterval = setInterval(function() {
12901			if (!$productSearchInput.is(':visible:focus') && !$productSearch.hasClass('active') && $productSearch.find(':focus').length == 0) {
12902				$productSearchInput.focus();
12903			}
12904		}, 500);
12905
12906		$(document).off('click.Checkout').on('click.Checkout', '.checkout .btn.btn-checkout', function (event) {
12907			$(this).loading({
12908				IsLabelShown: false,
12909				BackgroundColor: 'transparent'
12910			});
12911		});
12912
12913		quantityStepper.init(null, renderCart);
12914		addToCart();
12915	}
12916}
12917
12918function updateProductSearchAction() {
12919	var productSearchInputValue = $productSearchInput.val();
12920	if (productSearchInputValue) {
12921		productSearchInputValue = productSearchInputValue.toLowerCase();
12922	}
12923	var productSearchQueryValue = $productSearchInputContainer.attr('query');
12924	if (productSearchQueryValue) {
12925		productSearchQueryValue = productSearchQueryValue.toLowerCase();
12926	}
12927
12928	if (productSearchInputValue && productSearchQueryValue && productSearchInputValue == productSearchQueryValue) {
12929		$productSearchInputContainer.attr('action', 'reset');
12930	} else {
12931		$productSearchInputContainer.attr('action', 'search');
12932	}
12933}
12934
12935function searchProductsWithQuery(query) {
12936	$productSearchErrorContainer.html('');
12937	$productSearchInputContainer.attr('query', query);
12938	$productSearchInputContainer.loading({
12939		IsImageShown: false,
12940		IsLabelShown: false
12941	});
12942	$productSearchSearchButton.loading({
12943		IsLabelShown: false,
12944		BackgroundColor: 'transparent'
12945	});
12946	if ($productSearchResultsLoadMore.is(':visible')) {
12947		$productSearchResultsLoadMore.loading({
12948			IsLabelShown: false,
12949			BackgroundColor: 'transparent'
12950		});
12951	}
12952	$productSearchInput.attr('disabled', 'disabled');
12953
12954	$.ajax({
12955		type: 'GET',
12956		url: Urls.Search.Show,
12957		data: {
12958			format: 'ajax',
12959			q: query,
12960			// prefn1: 'availableStores',
12961			// prefv1: options.AgentStoreID,
12962			start: parseInt((data['ProductSearchPage'] - 1) * data['ProductSearchPageSize'], 10),
12963			sz: data['ProductSearchPageSize']
12964		},
12965		success: function(response) {
12966			var $html = $(response);
12967			var $productGrid = $html.find('#search-result-items');
12968			var $pagination = $html.find('.search-result-options .pagination[count]').first();
12969			var productTileCount = $productGrid.find('.grid-tile').length + $('.section.search .results-container').find('.grid-tile').length;
12970			var productTotalCount = 0;
12971			if ($pagination.length > 0) {
12972				productTotalCount = parseInt($html.find('.search-result-options .pagination[count]').first().attr('count').replace(/\D/g, ''), 10);
12973			}
12974
12975			if (productTileCount > 0) {
12976				$productSearchResults.append($productGrid.html());
12977				if (productTileCount < productTotalCount) {
12978					$productSearchResultsFooter.show();
12979				} else {
12980					$productSearchResultsFooter.hide();
12981				}
12982			} else {
12983				$productSearchResults.append($('<li class="no-results">No products found for your search.</li>'));
12984				$productSearchResultsFooter.hide();
12985			}
12986			$productSearch.addClass('active');
12987		},
12988		complete: function(xhr) {
12989			$productSearchInputContainer.attr('upc', '');
12990			$productSearchInputContainer.loading('destroy');
12991			$productSearchSearchButton.loading('destroy');
12992			if ($productSearchResultsLoadMore.attr('loading')) {
12993				$productSearchResultsLoadMore.loading('destroy');
12994			}
12995			$productSearchInput.removeAttr('disabled');
12996			updateProductSearchAction();
12997		}
12998	});
12999}
13000
13001function getStylesForProduct(productId) {
13002	var $productTile = $productSearchResults.find('.product-tile[data-itemid="' + productId + '"]');
13003
13004	if ($productTile.length > 0) {
13005		var $gridTile = $productTile.closest('.grid-tile');
13006
13007		if ($gridTile.length > 0) {
13008			var isFirstLoad = false;
13009			var $productVariations;
13010			if ($gridTile.find('.product-variations').length > 0) {
13011				$productVariations = $gridTile.find('.product-variations');
13012			} else {
13013				isFirstLoad = true;
13014				$productVariations = $gridTile.append('<div class="product-variations"></div>').find('.product-variations');
13015			}
13016			$productVariations.loading({
13017				IsLabelShown: false,
13018				BackgroundColor: 'transparent'
13019			});
13020			if ($gridTile.find('.btn.add').is(':visible')) {
13021				$gridTile.find('.btn.add').loading({
13022					IsLabelShown: false,
13023					BackgroundColor: 'transparent'
13024				});
13025			}
13026			data['attributeGuides'] = {};
13027
13028			var payload = {
13029				pid: $productTile.data('itemid')
13030			}
13031
13032			if (!isFirstLoad) {
13033				var $productVariationAttributesSelected = $productVariations.find('[attribute] option.selectable:checked');
13034				$productVariationAttributesSelected.each(function() {
13035					var $this = $(this);
13036					var productVariationAttributeId = $this.closest('[attribute]').attr('attribute');
13037					var productVariationAttributeValue = $this.val();
13038					payload['dwvar_' + payload.pid + '_' + productVariationAttributeId] = productVariationAttributeValue;
13039				});
13040			}
13041
13042			$.ajax({
13043				method: 'post',
13044				url: Urls.getVariationAttributes,
13045				data: payload,
13046				dataType: 'json',
13047				success: function(response) {
13048					html['variationSelectors'] = '';
13049					for (var variation in response.variations) {
13050						var variationData = response.variations[variation];
13051						var variationDataValueSelected = null;
13052						
13053						html['variationSelectorWidgetOptions'] = '';
13054										
13055						for (var variationValue in variationData.values) {
13056							var variationDataValue = variationData.values[variationValue];
13057							var variationDataValueTemplate;
13058							
13059							var variationDataValuePayload = {
13060								attribute: variationDataValue.attribute,
13061								valueId: variationDataValue.valueId,
13062								value: variationDataValue.value,
13063								title: variationDataValue.title,
13064								text: variationDataValue.text,
13065								stock: variationDataValue.stock,
13066								shipping: variationDataValue.shipping,
13067								isSameDayShippingWindow: variationDataValue.isSameDayShippingWindow,
13068								quantitySourceType: variationDataValue.quantitySourceType,
13069								href: variationDataValue.href,
13070								selectable: variationDataValue.selectable ? 'selectable' : 'unselectable',
13071								selected: !isFirstLoad && variationDataValue.selected ? 'selected' : '',
13072								swatchImageUrl: variationDataValue.images.swatch.url,
13073								largeSwatchImageUrl: variationDataValue.images.largeSwatch.url,
13074								hrefAttribute: variationDataValue.selectable ? 'href' : 'xhref'
13075							};
13076							
13077							if (variationData.type == 'swatch') {
13078								if (variationDataValue.selectable) {
13079									variationDataValueTemplate = templates['variationSelectorSwatchOption'];
13080								
13081									variationDataValuePayload.imageLarge = JSON.stringify({
13082										url: variationDataValue.images.large.url,
13083										title: variationDataValue.images.large.title,
13084										alt: variationDataValue.images.large.alt,
13085										hires: variationDataValue.images.hires.url
13086									});
13087								} else {
13088									variationDataValueTemplate = templates['variationSelectorSwatchOptionUnselectable'];
13089								}
13090							} else {
13091								variationDataValueTemplate = templates['variationSelectorDropdownOption'];
13092								
13093								if (variationDataValue.selected) {
13094									variationDataValueSelected = variationDataValue;
13095								}
13096							}
13097						
13098							html['variationSelectorWidgetOptions'] += variationDataValueTemplate(variationDataValuePayload);
13099						}	
13100						
13101						if (variationData.type == 'swatch') {
13102							html['variationSelectorWidget'] = templates['variationSelectorSwatch']({
13103								attribute: variationData.productVariationAttribute,
13104								options: html['variationSelectorWidgetOptions']
13105							});
13106						} else {
13107							var placeholder = variationData.label.placeholder;
13108							if (variationDataValueSelected) {
13109								placeholder = templates['variationSelectorDropdownOptionSelected']({
13110									text: variationDataValueSelected.text,
13111									stock: variationDataValueSelected.stock,
13112									shipping: variationDataValueSelected.shipping,
13113									isSameDayShippingWindow: variationDataValueSelected.isSameDayShippingWindow,
13114									quantitySourceType: variationDataValueSelected.quantitySourceType
13115								});
13116							}
13117							
13118							html['variationSelectorWidget'] = templates['variationSelectorDropdown']({
13119								attribute: variationData.productVariationAttribute,
13120								text: placeholder,
13121								options: html['variationSelectorWidgetOptions']
13122							});
13123						}
13124
13125						html['attributeGuide'] = '';
13126						if (variationData.attributeGuide) {
13127							data['attributeGuides'][variationData.productVariationAttribute] = variationData.attributeGuide;
13128							html['attributeGuide'] = templates['variationSelectorAttributeGuide'](_.extend(variationData.attributeGuide, { Attribute: variationData.productVariationAttribute }));
13129						}
13130
13131						html['variationSelector'] = templates['variationSelector']({
13132							attribute: variationData.productVariationAttribute,
13133							label: {
13134								text: variationData.label.text,
13135								message: variationData.label.message
13136							},
13137							selector: html['variationSelectorWidget'],
13138							guide: html['attributeGuide']
13139						});
13140
13141						html['variationSelectors'] += html['variationSelector'];
13142					}
13143					
13144					html['selectors'] = templates['variations']({
13145						selectors: html['variationSelectors']
13146					});
13147
13148					$productVariations.html(html['selectors']).addClass('loaded');
13149					if (response.pid) {
13150						$gridTile.find('.btn.add').attr('pid', response.pid);
13151					} else {
13152						$gridTile.find('.btn.add').removeAttr('pid');
13153					}
13154				},
13155				complete: function(xhr) {
13156					$productVariations.loading('destroy');
13157				}
13158			});
13159		}
13160	}
13161}
13162
13163function resetProductSearch() {
13164	$productSearch.removeClass('active');
13165	$productSearchInputContainer.attr('query', '');
13166	$productSearchInput.val('');
13167	$productSearchInput.focus();
13168	updateProductSearchAction();
13169}
13170
13171function addProductWithProductId(productId, $gridTileAddButton) {
13172	if ($gridTileAddButton) {
13173		$gridTileAddButton.loading({
13174			IsLabelShown: false,
13175			BackgroundColor: 'transparent'
13176		});
13177	}
13178	$productSearchErrorContainer.html('');
13179
13180	addProduct({
13181		payload: {
13182			pid: productId
13183		},
13184		complete: function(xhr) {
13185			if ($gridTileAddButton && $gridTileAddButton.attr('loading')) {
13186				$gridTileAddButton.loading('destroy');
13187			}
13188			resetProductSearch();
13189			renderCart();
13190		}
13191	});
13192}
13193
13194function detectBarcode() {
13195	var canvas = $barcodeScannerModal.find('canvas')[0];
13196	canvas.width = video.videoWidth;
13197	canvas.height = video.videoHeight;
13198
13199	var canvasContext = canvas.getContext('2d');
13200	canvasContext.drawImage(video, 0, 0, canvas.width, canvas.height);
13201
13202	var barcodeDetector = new BarcodeDetector();
13203	barcodeDetector.detect(canvasContext.getImageData(0, 0, canvas.width, canvas.height)).then(barcodes => {
13204		$barcodeScannerModal.addClass('scanned');
13205		
13206		barcodes.forEach(function(barcode) {
13207			var barcodeValue;
13208			if ('rawValue' in barcode) {
13209				barcodeValue = barcode.rawValue;
13210			} else if ('rawData' in barcode) {
13211				barcodeValue = barcode.rawData;
13212			}
13213			
13214			closeBarcodeScanner();
13215			addProductWithUpc(barcodeValue);
13216		});
13217	}).catch(error => {
13218		// alert(error);
13219	});
13220}
13221
13222function closeBarcodeScanner() {
13223	$('.section.scan').removeClass('active');
13224	if (barcodeDetectorInterval) {
13225		barcodeDetectorInterval = clearInterval(barcodeDetectorInterval);
13226	}
13227	stream.getTracks().forEach(function(track) {
13228		track.stop();
13229	});
13230	video.srcObject = null;
13231}
13232
13233function getClipboardValue(event, element, callback) {
13234	if (navigator.clipboard && navigator.clipboard.readText) {
13235	  	// modern approach with Clipboard API
13236	  	navigator.clipboard.readText().then(callback);
13237	} else if (event.originalEvent && event.originalEvent.clipboardData) {
13238	  	// OriginalEvent is a property from jQuery, normalizing the event object
13239	  	callback(event.originalEvent.clipboardData.getData('text'));
13240	} else if (event.clipboardData) {
13241	  	// used in some browsers for clipboardData
13242	  	callback(event.clipboardData.getData('text/plain'));
13243	} else if (window.clipboardData) {
13244	  	// Older clipboardData version for Internet Explorer only
13245	  	callback(window.clipboardData.getData('Text'));
13246	} else {
13247	  	// Last resort fallback, using a timer
13248	  	setTimeout(function() {
13249			callback(element.value)
13250	  	}, 100);
13251	}
13252}
13253
13254function addProductWithUpc(upc) {
13255	$barcodeScannerInputContainer.attr('upc', upc);
13256	$barcodeScannerInputContainer.loading({
13257		IsImageShown: false,
13258		IsLabelShown: false
13259	});
13260	$barcodeScannerAddButton.loading({
13261		IsLabelShown: false,
13262		BackgroundColor: 'transparent'
13263	});
13264	$barcodeScannerInput.val('');
13265	$barcodeScannerErrorContainer.html('');
13266	$barcodeScannerInput.attr('disabled', 'disabled');
13267
13268	addProduct({
13269		payload: {
13270			upc: upc
13271		},
13272		success: function(response) {
13273			if (response && !response.IsProductInProductService) {
13274				$barcodeScannerErrorContainer.append('<p>Product not found in service!</p>');	
13275			} else if (response && !response.IsProductInCatalog) {
13276				$barcodeScannerErrorContainer.append('<p>Product not found in catalog!</p>');	
13277			}
13278		},
13279		complete: function(xhr) {
13280			$barcodeScannerInputContainer.attr('upc', '');
13281			$barcodeScannerInputContainer.loading('destroy');
13282			$barcodeScannerAddButton.loading('destroy');
13283			$barcodeScannerInput.removeAttr('disabled');
13284			renderCart();
13285		}
13286	});
13287}
13288
13289function addProduct(parameters) {
13290	var payload = $.extend(parameters.payload || {}, {
13291		format: 'ajax',
13292		Quantity: 1,
13293		cartAction: 'add',
13294		csrf_token: options.Token,
13295		channel: 'Echo',
13296		bopis_store: '',
13297		bopis_quantity_available: '',
13298		bopis_shipping_option: ''
13299	});
13300
13301	if (payload.pid || payload.upc) {
13302		$.ajax({
13303			type: 'POST',
13304			url: Urls.addProduct,
13305			data: payload,
13306			success: function(response) {
13307				if (parameters.success && typeof parameters.success == 'function') {
13308					parameters.success(response);
13309				}
13310			},
13311			complete: function(xhr) {
13312				if (parameters.complete && typeof parameters.complete == 'function') {
13313					parameters.complete(xhr);
13314				}
13315			}
13316		});
13317	}
13318}
13319
13320function renderCart() {
13321	if (options && options.StoreUIVersion == '2024Redesign') {
13322		location.reload();
13323	} else {
13324		$.ajax({
13325			type: 'GET',
13326			url: Urls.Cart.GetCartBodyProductLineItems,
13327			success: function(response) {
13328				$('#wrapper > .cart > form').html(response);
13329			}
13330		});
13331
13332		$.ajax({
13333			type: 'POST',
13334			url: Urls.getCartData,
13335			data: {
13336				isCart: true
13337			},
13338			success: function(response) {
13339				if (response && response.OrderTotal && response.OrderTotal.adjustedMerchandizeTotalPrice) {
13340					$('.order-subtotal .order-value').text(response.OrderTotal.adjustedMerchandizeTotalPriceDisplay);
13341					$('.order-totals-table').removeClass('hidden');
13342				} else {
13343					$('.order-totals-table').addClass('hidden');
13344				}
13345			}
13346		});
13347	}
13348
13349	updateCSRFToken();
13350}
13351
13352function updateCSRFToken() {
13353    $.ajax({
13354    	method: 'get',
13355    	url: Urls.CSRFToken.Get,
13356		dataType: 'json',
13357    	success: function(response) {
13358    		options.Token = response.token;
13359    	}
13360    });
13361}
13362
13363exports.renderCart = renderCart;
13364exports.init = function (optionsParameter) {
13365	options = optionsParameter;
13366	initialize(options);
13367};
13368},{"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../components/LoadingIndicator":25,"../quantityStepper":87,"./product/addToCart":72,"lodash":133}],52:[function(require,module,exports){
13369'use strict';
13370
13371var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
13372var sizeRefinements = require('../size-refinements');
13373var sliders = require('../sliders');
13374var styleCounts = require('../style-counts');
13375var util = require('../util');
13376
13377exports.init = function(options) {
13378	styleCounts.render(options.StyleCountCache);
13379	sizeRefinements.init();
13380	sliders.initializeCategorySliders('.main-content .category-landing-slider:not(.slick-initialized)');
13381};
13382},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../size-refinements":93,"../sliders":95,"../style-counts":99,"../util":102}],53:[function(require,module,exports){
13383'use strict';
13384
13385var dialog = require('../../dialog');
13386var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
13387var OrderConfirmation = require('../orderconfirmation');
13388
13389exports.initialize = initialize;
13390
13391var applePaySession;
13392var options = {};
13393var buffer = {};
13394
13395function initialize(optionsParameter) {
13396	options = optionsParameter || {};
13397
13398	$('[payment-method-container="DW_APPLE_PAY"]').attr('can-make-payments', 'ApplePaySession' in window && 'canMakePayments' in ApplePaySession && ApplePaySession.canMakePayments());
13399	$('[payment-method-group-container="DW_APPLE_PAY"]').attr('can-make-payments', 'ApplePaySession' in window && 'canMakePayments' in ApplePaySession && ApplePaySession.canMakePayments());
13400	if (options.isCartPage || options.isCheckoutPage) {
13401		getApplePayPaymentRequest();
13402	}
13403	$(document).off('ApplePay.GetPaymentRequest').on('ApplePay.GetPaymentRequest Cart.UpdateSuccess ProductDetailPage.Variant.Updated ProductDetailPage.AddToCartOptionsSelect', function(event, data) {
13404		$('[payment-method-container="DW_APPLE_PAY"]').attr('can-make-payments', 'ApplePaySession' in window && 'canMakePayments' in ApplePaySession && ApplePaySession.canMakePayments());
13405		$('[payment-method-group-container="DW_APPLE_PAY"]').attr('can-make-payments', 'ApplePaySession' in window && 'canMakePayments' in ApplePaySession && ApplePaySession.canMakePayments());
13406		getApplePayPaymentRequest(data);
13407	});
13408
13409	$(document).off('click.ApplePay').on('click.ApplePay', '.submit-applepay-button', function(event) {
13410		var isVariantSelected = true;
13411		if (options.ProductId) {
13412			var sizeSelected = $('div.product-variations .size.size-options li.selected');
13413			var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
13414			var widthSelected = $('div.product-variations .width.width-options li.selected').length;
13415			var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
13416
13417			if (!isSizeSelected) {
13418				dialog.open({
13419					html: 'Please select a size',
13420					options: {
13421						dialogClass: 'dialog-alert',
13422						draggable: false
13423					}
13424				});
13425				isVariantSelected = false;
13426			}
13427
13428			if (!$('div.product-variations .color.color-options li.selected').length) {
13429				dialog.open({
13430					html: 'Please select a color',
13431					options: {
13432						dialogClass: 'dialog-alert',
13433						draggable: false
13434					}
13435				});
13436				isVariantSelected = false;
13437			}
13438		}
13439
13440		if (options.isCartPage || options.isCheckoutPage || isVariantSelected) {
13441			if (pageContext && pageContext.options && pageContext.options.GoogleTagManager && pageContext.options.GoogleTagManager.EnableGoogleAnalytics4) {
13442				var applePayEventData;
13443				var applePayEventOptions;
13444				if (pageContext.type == 'product') {
13445					if (pageContext.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4 && pageContext.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4.items && pageContext.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4.items.length > 0) {
13446						applePayEventData = {
13447							action_name: 'applepay',
13448							action_page_type: 'PDP',
13449							action_value: pageContext.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4.items[0].price,
13450							action_items: [pageContext.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4.items[0].item_id]
13451						}
13452					}
13453				} else if (pageContext.type == 'cart') {
13454					applePayEventData = {
13455						action_name: 'applepay',
13456						action_page_type: 'Cart'
13457					}
13458					applePayEventOptions = {
13459						DecorateDataLayerWithCartTotal: true,
13460						DecorateDataLayerWithCartTotalKey: 'action_value',
13461						DecorateDataLayerWithProductData: true,
13462						DecorateDataLayerWithProductDataKey: 'action_items'
13463					}
13464				} else if (pageContext.type == 'onepcheckout_groups') {
13465					applePayEventData = {
13466						action_name: 'applepay',
13467						action_page_type: 'Checkout'
13468					}
13469					applePayEventOptions = {
13470						DecorateDataLayerWithCartTotal: true,
13471						DecorateDataLayerWithCartTotalKey: 'action_value',
13472						DecorateDataLayerWithProductData: true,
13473						DecorateDataLayerWithProductDataKey: 'action_items'
13474					}
13475				}
13476				if (applePayEventData) {
13477					GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', applePayEventData, applePayEventOptions);
13478				}
13479			}
13480
13481			applePaySession = new ApplePaySession(14, buffer['applePayRequest']);
13482			applePaySession.onvalidatemerchant = validateMerchant;
13483			applePaySession.onshippingcontactselected = shippingContactSelected;
13484			applePaySession.onshippingmethodselected = shippingMethodSelected;
13485			applePaySession.onpaymentmethodselected = paymentMethodSelected;
13486			applePaySession.oncouponcodechanged = couponCodeChanged;
13487			applePaySession.onpaymentauthorized = paymentAuthorized;
13488			applePaySession.oncancel = cancel;
13489			applePaySession.begin();
13490		}
13491	});
13492}
13493
13494function validateMerchant(event) {
13495	fetch(Urls.ApplePay.ValidateMerchant, getFetchOptions('POST', event))
13496	.then(function(response) { 
13497		return response.json();
13498	})
13499	.then(function(response) {
13500		applePaySession.completeMerchantValidation(response);
13501	})
13502	.catch(function(error) {
13503		console.error('ApplePay-ValidateMerchant', error);
13504	});
13505}
13506
13507function shippingContactSelected(event) {
13508	fetch(Urls.ApplePay.ShippingContactSelected, getFetchOptions('POST', event))
13509	.then(function(response) { 
13510		return response.json();
13511	})
13512	.then(function(response) {
13513		applePaySession.completeShippingContactSelection(response);
13514		if (!options || !options.ProductId) {
13515			$(document).trigger('Cart.Update');
13516		}
13517	})
13518	.catch(function(error) {
13519		console.error('ApplePay-ShippingContactSelected', error);
13520	});
13521}
13522
13523function shippingMethodSelected(event) {
13524	fetch(Urls.ApplePay.ShippingMethodSelected, getFetchOptions('POST', event))
13525	.then(function(response) { 
13526		return response.json();
13527	})
13528	.then(function(response) {
13529		applePaySession.completeShippingMethodSelection(response);
13530		if (!options || !options.ProductId) {
13531			$(document).trigger('Cart.Update');
13532		}
13533	})
13534	.catch(function(error) {
13535		console.error('ApplePay-ShippingMethodSelected', error);
13536	});
13537}
13538
13539function paymentMethodSelected(event) {
13540	fetch(Urls.ApplePay.PaymentMethodSelected, getFetchOptions('POST', event))
13541	.then(function(response) { 
13542		return response.json();
13543	})
13544	.then(function(response) {
13545		applePaySession.completePaymentMethodSelection(response);
13546	})
13547	.catch(function(error) {
13548		console.error('ApplePay-PaymentMethodSelected', error);
13549	});
13550}
13551
13552function couponCodeChanged(event) {
13553	fetch(Urls.ApplePay.CouponCodeChanged)
13554	.then(function(response) { 
13555		return response.json();
13556	})
13557	.then(function(response) {
13558		applePaySession.completeCouponCodeChange(response);
13559	})
13560	.catch(function(error) {
13561		console.error('ApplePay-CouponCodeChanged', error);
13562	});
13563}
13564
13565function paymentAuthorized(event) {
13566	fetch(Urls.ApplePay.PaymentAuthorized, getFetchOptions('POST', event))
13567	.then(function(response) { 
13568		return response.json();
13569	})
13570	.then(function(response) {
13571		if (response.status == 'success') {
13572			applePaySession.completePayment(ApplePaySession.STATUS_SUCCESS);
13573		} else {
13574			applePaySession.completePayment(ApplePaySession.STATUS_FAILURE);
13575		}
13576	})
13577	.then(function(response) {
13578		if (event.payment) {
13579			if (event.payment.billingContact) {
13580				var billingAddressObject = {};
13581				billingAddressObject.FirstName = event.payment.billingContact.givenName;
13582				billingAddressObject.LastName = event.payment.billingContact.familyName;
13583				billingAddressObject.PrimaryLine = event.payment.billingContact.addressLines[0] || '';
13584				billingAddressObject.SecondaryLine = event.payment.billingContact.addressLines[1] || '';
13585				billingAddressObject.StateCode = event.payment.billingContact.administrativeArea;
13586				billingAddressObject.PostalCode = event.payment.billingContact.postalCode;
13587				billingAddressObject.City = event.payment.billingContact.locality;
13588
13589				if (event.payment.shippingContact && event.payment.shippingContact.phone) {
13590					billingAddressObject.Phone = event.payment.shippingContact.phone.replace('+','');
13591				}
13592
13593				if (options.isCheckoutPage) {
13594					$(document).trigger('SetAddressFromAddressVerification', $.extend(billingAddressObject, { AddressType: 'billing', DisableSubmitPayment: true, SkipUseBillingAsShipping: true }));
13595				}
13596			}
13597
13598			if (event.payment.shippingContact) {
13599				var shippingAddressObject = {};
13600				shippingAddressObject.FirstName = event.payment.shippingContact.givenName;
13601				shippingAddressObject.LastName = event.payment.shippingContact.familyName;
13602				if (event.payment.shippingContact.addressLines) {
13603					shippingAddressObject.PrimaryLine = event.payment.shippingContact.addressLines[0] || '';
13604					shippingAddressObject.SecondaryLine = event.payment.shippingContact.addressLines[1] || '';
13605					shippingAddressObject.StateCode = event.payment.shippingContact.administrativeArea;
13606					shippingAddressObject.PostalCode = event.payment.shippingContact.postalCode;
13607					shippingAddressObject.City = event.payment.shippingContact.locality;
13608				}
13609
13610				if (event.payment.shippingContact.phoneNumber) {
13611					shippingAddressObject.Phone = event.payment.shippingContact.phoneNumber.replace('+','');
13612				}
13613
13614				if (event.payment.shippingContact.emailAddress) {
13615					shippingAddressObject.Email = event.payment.shippingContact.emailAddress;
13616				}
13617
13618				if (options.isCheckoutPage) {
13619					$(document).trigger('SetAddressFromAddressVerification', $.extend(shippingAddressObject, { AddressType: '
13619shipping', DisableSubmitPayment: true }));
13620				}
13621			}
13622		}
13623
13624		if (options.ProductId) {
13625			fetch(Urls.ApplePay.Confirmation, getFetchOptions('GET'))
13626			.then(function(response) {
13627				response.text().then(function(text) {
13628					dialog.open({
13629						html: text,
13630						options: {
13631							dialogClass: 'pt_order-confirmation',
13632							open: function() {
13633								$('body').addClass('dialog-open');
13634								OrderConfirmation.init();
13635								var dataLayerOptions = {
13636									PaymentMethod:  'Apple Pay'
13637								};
13638								$(document).off('click.OrderConfirmationModalClose').on('click.OrderConfirmationModalClose', '.ui-dialog.pt_order-confirmation .btn.continue', function(event) {
13639									event.preventDefault();
13640									dialog.close();
13641								});
13642								GoogleTagManagerClient.PushDataLayerForDataLayerType('ViewOrderConfirmation', null, dataLayerOptions).then(function() {
13643									$(document).trigger('ProductDetailPage.AddToCartOptionsSelect', { ProductId: $('#pid').val(), StoreId: $('.bopis-add-to-cart-options-container .selected-store').attr('store-id') });
13644								});
13645							},
13646							close: function(event, ui) {
13647								$('body').removeClass('dialog-open');
13648							}
13649						}
13650					});
13651				});
13652			})
13653			.catch(function(error) {
13654				console.error('ApplePay-Confirmation', error);
13655			});
13656		} else if (options.isCheckoutPage) {
13657			$(document).trigger('Cart.Update');
13658		} else {
13659			window.location = Urls.onePage;
13660		}
13661
13662	})
13663	.catch(function(error) {
13664		console.error('ApplePay-PaymentAuthorized', error);
13665	});
13666}
13667
13668function cancel(event) {
13669	fetch(Urls.ApplePay.Cancel, getFetchOptions('POST'))
13670	.then(function() {
13671		// applePaySession.abort();
13672		if (options.isCheckoutPage) {
13673			$(document).trigger('Cart.Update');
13674		}
13675	})
13676	.catch(function(error) {
13677		console.error('ApplePay-Cancel', error);
13678	});
13679}
13680
13681function getApplePayPaymentRequest(data) {
13682	var urlSearchParameters;
13683	if (data && data.ProductId) {
13684		urlSearchParameters = new URLSearchParams();
13685		urlSearchParameters.append('pid', data.ProductId);
13686		if (data.StoreId) {
13687			urlSearchParameters.append('storeid', data.StoreId);
13688		}
13689		var bopisAddToCartOption = $('.bopis-add-to-cart-options-container').find('input[name="AddToCartOption"]:checked').val();
13690		if (bopisAddToCartOption) {
13691			urlSearchParameters.append('addtocartoption', bopisAddToCartOption);
13692		}
13693	}
13694
13695	fetch(Urls.ApplePay.GetRequest + (urlSearchParameters ? '?' + urlSearchParameters : ''))
13696	.then(function(response) { 
13697		return response.json();
13698	})
13699	.then(function(applePayRequest) {
13700		buffer['applePayRequest'] = applePayRequest;
13701	})
13702	.catch(function(error) {
13703		console.error('ApplePay-GetRequest', error);
13704	});
13705}
13706
13707function getFetchOptions(method, body) {
13708	var fetchOptions = {
13709		credentials: 'include'
13710	};
13711	if (method) {
13712		fetchOptions.method = method;
13713	}
13714	if (body) {
13715		fetchOptions.headers = {
13716			'Content-Type': 'application/json',
13717			'Accept': 'application/json'
13718		}
13719		fetchOptions.body = JSON.stringify(filterEvent(body));
13720	}
13721
13722	return fetchOptions;
13723}
13724
13725function filterEvent(event) {
13726	var eventFiltered = {};
13727	for (var property in event) {
13728		if (!Event.prototype.hasOwnProperty(property)) {
13729			eventFiltered[property] = event[property];
13730		}
13731	}
13732	return eventFiltered;
13733}
13734},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../dialog":36,"../orderconfirmation":71}],54:[function(require,module,exports){
13735'use strict';
13736
13737var _ = require('lodash');
13738var loadingIndicator = require('../../components/LoadingIndicator');
13739var giftCardBalanceInquiryDialog = require('../../components/GiftCardBalanceInquiryDialog');
13740var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
13741var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
13742var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
13743var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
13744var opcpayment = require('./opcpayment');
13745
13746exports.initialize = initialize;
13747
13748var $giftCardPaymentContainer = $('[payment-method="SVS_Gift_Card"]');
13749var $giftCardPaymentForm = $giftCardPaymentContainer.find('form');
13750var $giftCardPaymentCardNumber = $giftCardPaymentContainer.find('input[name$="giftCard_number"]');
13751var $giftCardPaymentPinNumber = $giftCardPaymentContainer.find('input[name$="giftCard_pin"]');
13752var $giftCardPaymentMessage = $giftCardPaymentContainer.find('.messages');
13753var $giftCardPaymentErrorMessage = $giftCardPaymentContainer.find('span.error');
13754var $giftCardPaymentApplied = $giftCardPaymentContainer.find('.form-giftcards-applied');
13755var $giftCardPaymentApplyButton = $giftCardPaymentContainer.find('.apply');
13756var giftCardValidator;
13757var giftCardRecaptcha;
13758var options = {};
13759var loaded = {};
13760
13761function renderRecaptcha() {
13762	if (loaded.Recaptcha && $giftCardPaymentApplyButton.length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForGiftCards && options.Recaptcha.GoogleSiteKey) {
13763		giftCardRecaptcha = grecaptcha.render('svs-gift-card-apply-button', {
13764	        sitekey: options.Recaptcha.GoogleSiteKey,
13765	        callback: submitGiftCard,
13766	        badge: 'bottomleft'
13767	    });
13768	}
13769}
13770
13771function initialize(optionsParameter) {
13772	options = optionsParameter || {};
13773	
13774	renderRecaptcha();
13775	$(document).on('Recaptcha.Loaded', function(event) {
13776		if (!loaded.Recaptcha) {
13777			loaded.Recaptcha = true;
13778	        renderRecaptcha();
13779	    }
13780	});
13781	
13782    $giftCardPaymentContainer.on('click', '.apply:not([loading])', function(event) {
13783		event.preventDefault();
13784		
13785        if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForGiftCards || !options.Recaptcha.GoogleSiteKey) {
13786            $(document).trigger('GiftCard.Apply');
13787        }
13788	});
13789	
13790	$(document).on('GiftCard.Apply', function(event) {		
13791        event.preventDefault();
13792
13793        if (parseFloat($('[balance-due]').first().attr('balance-due')) <= 0) {
13794            $giftCardPaymentErrorMessage.html(Resources.RewardsCredit.NoBalanceDue).show();
13795            
13796            if (giftCardRecaptcha != undefined) {
13797				grecaptcha.reset(giftCardRecaptcha);
13798			}
13799        } else {
13800            $giftCardPaymentErrorMessage.html('').hide();
13801            giftCardValidator = $giftCardPaymentForm.validate();
13802			$.validator.addMethod('giftCardNumberLength', ValidationHelper.ValidateGiftCardNumberLength, Resources.Validation.Messages.GiftCard.Number.Length);
13803			$.validator.addMethod('giftCardNumberValues', ValidationHelper.ValidateGiftCardNumberValues, Resources.Validation.Messages.GiftCard.Number.Values);
13804            $giftCardPaymentCardNumber.rules('add', ValidationRules.GiftCard.Number);
13805            $giftCardPaymentPinNumber.rules('add', ValidationRules.GiftCard.Pin);
13806            giftCardValidator.form();
13807
13808            // Adding ADA compliance error messages for giftCardPayment section
13809            if ($giftCardPaymentForm.find('.field.error').length > 0) {
13810                $('.payment-declined').remove();
13811                var giftCardPaymentFormErrorMessages = opcpayment.getErrorMessages($giftCardPaymentForm);
13812                var giftCardPaymentFormErrorBox = opcpayment.errorBox('
13812Cannot Proceed', 'Please fix the following errors:', giftCardPaymentFormErrorMessages);
13813                if (giftCardPaymentFormErrorBox) {
13814                    $giftCardPaymentApplied.after(giftCardPaymentFormErrorBox);
13815                }
13816            }
13817
13818            if (Urls && Urls.GiftCard.Apply && giftCardValidator.valid()) {
13819                var giftCardRowContainerHtml = _.template($('#template-gift-card-row-container').html(), TemplateConstants.Lodash.Settings)();
13820                var $giftCardPaymentAppliedGiftCard = $(giftCardRowContainerHtml).appendTo($giftCardPaymentApplied);
13821
13822                $giftCardPaymentApplyButton.loading(options.LoadingIndicator || {
13823                    IsImageShown: false,
13824                    Label: Resources.Loading.Processing
13825                });
13826				if (!options.LoadingIndicator) {
13827					$giftCardPaymentAppliedGiftCard.loading(options.LoadingIndicator || {
13828						Label: Resources.Loading.GiftCard.Applying
13829					});
13830				}
13831
13832				var giftCardPaymentCardNumber = $giftCardPaymentCardNumber.val().match(/(\d)/g);
13833				if (giftCardPaymentCardNumber && giftCardPaymentCardNumber.length) {
13834					giftCardPaymentCardNumber = giftCardPaymentCardNumber.join('');
13835				}
13836
13837                $.ajax({
13838                    method: 'POST',
13839                    url: Urls.GiftCard.Apply,
13840                    contentType: 'application/json',
13841                    data: JSON.stringify({
13842                        number: giftCardPaymentCardNumber,
13843                        pin: $giftCardPaymentPinNumber.val(),
13844                        amount: null
13845                    }),
13846                    dataType: 'json',
13847                    success: function (data) {
13848						if ($giftCardPaymentApplyButton.attr('loading')) {
13849                        	$giftCardPaymentApplyButton.loading('destroy');
13850						}
13851						if ($giftCardPaymentAppliedGiftCard.attr('loading')) {
13852                        	$giftCardPaymentAppliedGiftCard.loading('destroy');
13853						}
13854                        
13855                        if (data.success) {
13856                            $giftCardPaymentCardNumber.val('');
13857                            $giftCardPaymentPinNumber.val('');
13858                            
13859                            if (data.amountApplied && data.amountApplied.value > 0) {
13860                            	$giftCardPaymentApplied.find('[data-id="' + data.id + '"]').remove();                            	
13861                                $giftCardPaymentAppliedGiftCard.attr('data-id', data.id);
13862
13863                                var giftCardRowContentHtml = _.template($('#template-gift-card-row-content').html(), TemplateConstants.Lodash.Settings)({
13864                                    cardNumber: data.number,
13865                                    amountApplied: data.amountApplied.displayValue
13866                                });
13867
13868                                $giftCardPaymentAppliedGiftCard.html(giftCardRowContentHtml);
13869                            }
13870                            
13871                            if (data.message) {
13872                            	$giftCardPaymentErrorMessage.html(data.message).show();
13873                            }
13874
13875							if (options.StoreUIVersion == '2024Redesign') {
13876								location.reload();
13877							} else {
13878                            	$(document).trigger('Cart.Update');
13879							}
13880                        } else {
13881                        	if (data.message == 'Invalid Message') {
13882                        		data.message = Resources.Error.Generic;	
13883                        	}
13884                        	
13885                            $giftCardPaymentAppliedGiftCard.remove();
13886                            $giftCardPaymentErrorMessage.html(data.message || Resources.Error.Generic).show();
13887                        }
13888                        
13889						if (giftCardRecaptcha != undefined) {
13890							grecaptcha.reset(giftCardRecaptcha);
13891						}
13892
13893						var dataObject = {
13894							value: data.amountApplied.value,
13895							payment_type: 'Gift Card SVS'
13896						}
13897						GoogleTagManagerClient.PushDataLayerForDataLayerType('AddPaymentInfo', dataObject);
13898                    },
13899                    error: function() {
13900						$giftCardPaymentApplyButton.loading('destroy');
13901						$giftCardPaymentAppliedGiftCard.remove();
13902						$giftCardPaymentErrorMessage.html(Resources.Error.Generic).show();
13903						
13904						if (giftCardRecaptcha != undefined) {
13905							grecaptcha.reset(giftCardRecaptcha);
13906						}
13907					},
13908					complete: function(data, status, xhr) {
13909						if (data.status == 302 && data.responseText) {
13910							location.href = data.responseText;
13911						}
13912					}
13913                });
13914            } else {
13915            	if (giftCardRecaptcha != undefined) {
13916					grecaptcha.reset(giftCardRecaptcha);
13917				}	
13918            }
13919        }
13920    });
13921
13922    $(document).off('mousedown.GiftCardPaymentPanel').on('mousedown.GiftCardPaymentPanel', function (event) {
13923        if (giftCardValidator && $(event.target).closest($giftCardPaymentContainer).length == 0) {
13924            giftCardValidator.resetForm();
13925            $giftCardPaymentErrorMessage.html('').hide();
13926        }
13927    });
13928
13929    $(document).on('CartData.Update', function (event, data) {
13930    	$('[has-gift-cards]').attr('has-gift-cards', data.HasGiftCards || false);
13931	    $('[has-gift-cards-only]').attr('has-gift-cards-only', data.HasGiftCardsOnly || false);
13932	    
13933	    if (data.HasGiftCards && !data.AllowGiftCardPurchaseWithGiftCard) {
13934	    	$giftCardPaymentMessage.html(Resources.GiftCard.GiftCardPurchaseWithGiftCardNotAllowed);
13935	    	$giftCardPaymentContainer.attr('status', 'locked');
13936	    } else {
13937	    	$giftCardPaymentMessage.html('');
13938	    	$giftCardPaymentContainer.attr('status', 'active');
13939	    }
13940    });
13941    
13942    $(document).on('GiftCard.Clear', function () {
13943        $giftCardPaymentApplied.html('');
13944        $('.order-balance-due').html('');
13945    });
13946
13947    $giftCardPaymentContainer.on('click', '.remove:not([loading])', function (event) {
13948        event.preventDefault();
13949
13950        var $giftCardRow = $(this).closest('.form-giftcard-applied');
13951        $giftCardRow.loading(options.LoadingIndicator || {
13952            Label: Resources.Loading.GiftCard.Removing
13953        });
13954        $giftCardPaymentErrorMessage.html('').hide();
13955
13956        $.ajax({
13957            url : Urls.GiftCard.Remove,
13958            type : 'POST',
13959            contentType: 'application/json',
13960            data: JSON.stringify({
13961                id: $giftCardRow.data('id')
13962            }),
13963            dataType : 'json',
13964            success : function(data) {
13965            	$giftCardRow.loading('destroy');
13966            	
13967                if (data.success) {
13968                    $giftCardRow.remove();
13969
13970                    if (options.StoreUIVersion == '2024Redesign') {
13971						location.reload();
13972					} else {
13973						$(document).trigger('Cart.Update');
13974					}
13975                } else {
13976                	$giftCardPaymentErrorMessage.html(data.error || Resources.Error.Generic).show();
13977                }
13978            },
13979            error: function() {
13980				$giftCardRow.loading('destroy');
13981				$giftCardPaymentErrorMessage.html(Resources.Error.Generic).show();
13982			}
13983        });
13984    });
13985
13986    $(document).on('click.BalanceInquiry', '[payment-method="SVS_Gift_Card"] .balance-inquiry a', function (event) {
13987        event.preventDefault();
13988        giftCardBalanceInquiryDialog.initialize();
13989    });
13990    
13991    $(document).on('click', '.form-label.expander', function(){
13992    	$('form.svs-gift-card-information').slideToggle();
13993    	$giftCardPaymentContainer.toggleClass('svs-collapsed');
13994    });
13995}
13996
13997},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/GiftCardBalanceInquiryDialog":21,"../../components/LoadingIndicator":25,"./opcpayment":65,"lodash":133}],55:[function(require,module,exports){
13998'use strict';
13999/* global Resources, Urls */
14000
14001var _ = require('lodash');
14002var loadingIndicator = require('../../components/LoadingIndicator');
14003var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
14004var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
14005var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
14006var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
14007var opcpayment = require('./opcpayment');
14008var util = require('../../util');
14009var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
14010
14011exports.initialize = initialize;
14012
14013var $loyaltyPaymentContainer = $('[payment-method="LOYALTY_CREDIT"]');
14014var $loyaltyPaymentsForm = $loyaltyPaymentContainer.find('form');
14015var $loyaltyPaymentsApplied = $loyaltyPaymentContainer.find('.loyalty-applied');
14016var $loyaltyPaymentInput = $loyaltyPaymentContainer.find('#dwfrm_billing_loyalty_loyaltyNumber');
14017var $loyaltyPhoneNumberInput = $loyaltyPaymentContainer.find('#dwfrm_billing_loyalty_loyaltyPhoneNumber');
14018var $loyaltyZipCodeInput = $loyaltyPaymentContainer.find('#dwfrm_billing_loyalty_loyaltyZipCode');
14019var $loyaltyPaymentSecondaryContainer = $loyaltyPaymentContainer.find('.loyalty-secondary');
14020var $loyaltyPaymentSecondarySelect = $loyaltyPaymentContainer.find('#loyalty-secondary-select');
14021var $loyaltyPaymentErrorMessage = $loyaltyPaymentContainer.find('span.error');
14022var loyaltyCreditValidator;
14023var loyaltyCreditRowTemplate = _.template($('#template-loyalty-credit-row').html(), TemplateConstants.Lodash.Settings);
14024var loyaltyCreditRecaptcha;
14025var options = {};
14026var loaded = {};
14027
14028function renderRecaptcha() {
14029	if (loaded.Recaptcha && $('#loyalty-credit-add-button').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForloyaltyCredit && options.Recaptcha.GoogleSiteKey) {
14030		loyaltyCreditRecaptcha = grecaptcha.render('loyalty-credit-add-button', {
14031	        sitekey: options.Recaptcha.GoogleSiteKey,
14032	        callback: submitLoyaltyCredit,
14033	        badge: 'bottomleft'
14034	    });
14035	}
14036}
14037
14038function initialize(optionsParameter) {
14039	options = optionsParameter || {};
14040	var currentSite = '';
14041	if ($(".logo.shep").length > 0){
14042    	currentSite = Resources.LoyaltyCredit.NotOptedInSHEP;
14043    }
14044	if ($(".logo.co").length > 0){
14045    	currentSite = Resources.LoyaltyCredit.NotOptedInCO;
14046    }
14047	
14048	renderRecaptcha();
14049	$(document).on('Recaptcha.Loaded', function(event) {
14050		if (!loaded.Recaptcha) {
14051			loaded.Recaptcha = true;
14052	        renderRecaptcha();
14053	    }
14054	});
14055	
14056	if ($(".loyalty-row[status='applied']").length > 0){
14057		$loyaltyPaymentsForm.hide();
14058	}
14059
14060	if ($loyaltyPaymentSecondarySelect.closest('.opc-summary').length > 0) {
14061		$loyaltyPaymentSecondarySelect.selectmenu({
14062			width: '100%',
14063			appendTo: '[payment-method="LOYALTY_CREDIT"] .loyalty-secondary',
14064			change: function(event, ui) {
14065				$loyaltyPaymentSecondaryContainer.attr('secondary', ui && ui.item && ui.item.value ? ui.item.value : '');
14066			}
14067		});
14068	}
14069	
14070	$loyaltyPaymentContainer.on('click', '.reset', function(event) {
14071		event.preventDefault();
14072		resetSecondarySelect();
14073	});
14074
14075	$loyaltyPaymentContainer.on('click', '.add', function(event) {
14076		event.preventDefault();
14077		
14078        if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForRe
14078wardsCredit || !options.Recaptcha.GoogleSiteKey) {
14079            $(document).trigger('LoyaltyCredit.Add'); 
14080        }
14081	});
14082	
14083	$(document).on('LoyaltyCredit.Add', function(event, data) {
14084		event.preventDefault();
14085
14086		if (parseFloat($('[balance-due]').first().attr('balance-due')) <= 0) {
14087			$loyaltyPaymentErrorMessage.html(Resources.LoyaltyCredit.NoBalanceDue).show();
14088		} else {
14089			var loyaltySecondaryFactor = $loyaltyPaymentSecondaryContainer.attr('secondary');
14090			loyaltyCreditValidator = $loyaltyPaymentsForm.validate();
14091            $loyaltyPaymentInput.rules('add', ValidationRules.LoyaltyCredit.Number);
14092
14093			switch (loyaltySecondaryFactor) {
14094				case "zipcode":
14095					$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
14096					$loyaltyZipCodeInput.rules('add', ValidationRules.ZipCode);
14097					break;
14098
14099				default:
14100					break;
14101			}
14102
14103            loyaltyCreditValidator.form();
14104
14105			// Adding ADA compliance error messages for giftCardPayment section
14106            if ($loyaltyPaymentsForm.find('.field.error').length > 0) {
14107				$('.payment-declined').remove();
14108                var loyaltyPaymentFormErrorMessages = opcpayment.getErrorMessages($loyaltyPaymentsForm);
14109                var loyaltyPaymentFormErrorBox = opcpayment.errorBox('
14109Cannot Proceed', 'Please fix the following errors:', loyaltyPaymentFormErrorMessages);
14110                if (loyaltyPaymentFormErrorBox) {
14111                    $loyaltyPaymentsApplied.after(loyaltyPaymentFormErrorBox);
14112                }
14113            }
14114			
14115            if (Urls && Urls.checkLoyalty && loyaltyCreditValidator.valid()) {
14116            	$loyaltyPaymentsForm.hide();
14117            	
14118				var $loyaltyCreditRow = $(loyaltyCreditRowTemplate());
14119				$loyaltyCreditRow = $loyaltyCreditRow.appendTo($loyaltyPaymentsApplied);
14120				$loyaltyCreditRow.attr('status', 'pending');
14121				$loyaltyCreditRow.find('.basketLoyaltyNumber').html(util.getMaskedNumber($loyaltyPaymentInput.val(), 4, 'xxxx... '));
14122	
14123				$loyaltyCreditRow.find('.btn').loading({IsLabelShown: false});
14124				$loyaltyCreditRow.find('.form-controls').loading({Label: Resources.Loading.LoyaltyCredit.Loading});
14125	
14126				$.ajax({
14127					url: Urls.checkLoyalty,
14128					type: 'POST',
14129					data: {
14130						csrf_token:  $loyaltyPaymentContainer.find('input[name="csrf_token"]').val(),
14131						loyaltyNumber: $loyaltyPaymentInput.val(),
14132						loyaltyPhoneNumber: $loyaltyPhoneNumberInput.val(),
14133						loyaltyZipCode: $loyaltyZipCodeInput.val()
14134					},
14135					dataType: 'json',
14136					success: function(data) {
14137						$loyaltyPaymentErrorMessage.html('').hide();
14138	
14139						$loyaltyCreditRow.find('.btn').loading('destroy');
14140						$loyaltyCreditRow.find('.form-controls').loading('destroy');
14141	
14142						if (data.error !== 'undefined' && data.error === true) {
14143							$loyaltyCreditRow.remove();
14144							$loyaltyPaymentsForm.show();
14145							$loyaltyPaymentErrorMessage.html(data.errorMsg).show();
14146						} else if (!data.optinStatus) {
14147							$loyaltyPaymentContainer.attr('has-loyaltynumber', 'false');
14148							$loyaltyCreditRow.attr('status', 'message');
14149							$loyaltyCreditRow.find('.message').html(currentSite);
14150						} else if (parseFloat(data.creditAmount) <= 0) {
14151							$loyaltyPaymentContainer.attr('has-loyaltynumber', 'true');
14152							$loyaltyCreditRow.attr('status', 'message');
14153							$loyaltyCreditRow.find('.message').html(Resources.LoyaltyCredit.NoBalanceAvailable);
14154						} else {
14155							$loyaltyPaymentContainer.attr('has-loyaltynumber', 'true');
14156							$loyaltyPaymentContainer.attr('data-loyalty', data.optIn);
14157							$loyaltyCreditRow.find('.basketLoyaltyAmount').html(data.creditAmountFormatted);
14158													
14159							var details = '';					
14160							var vouchers = data.creditVouchers;
14161							
14162							vouchers.reverse().forEach(function(voucher){
14163								details += '<div class="voucher"><span class="reward-amount"><b>$'+voucher.rewardAmount+'</b></span><span class="expiration"><i>Expires '+voucher.expirationDate+'</i></span></div>';
14164							});
14165							
14166							$loyaltyCreditRow.find('.vouchers').html(details);
14167						}
14168						
14169						if (loyaltyCreditRecaptcha != undefined) {
14170							grecaptcha.reset(loyaltyCreditRecaptcha);
14171						}
14172					},
14173					error: function() {
14174						$loyaltyCreditRow.remove();
14175						$loyaltyPaymentsForm.show();
14176						$loyaltyPaymentErrorMessage.html(Resources.Error.Generic).show();
14177						
14178						if (loyaltyCreditRecaptcha != undefined) {
14179							grecaptcha.reset(loyaltyCreditRecaptcha);
14180						}
14181					},
14182					complete: function(data, status, xhr) {
14183						if (data.status == 302 && data.responseText) {
14184							location.href = data.responseText;
14185						}
14186					}
14187				});
14188            }
14189		}
14190	});
14191
14192	$loyaltyPaymentContainer.on('click', '.apply', function(event) {
14193		event.preventDefault();
14194
14195		var $loyaltyCreditRow = $(this).closest('.loyalty-row');
14196		
14197		$loyaltyCreditRow.find('.basketLoyaltyAmount').addClass('hide');
14198		$loyaltyCreditRow.find('.voucher').addClass('hide');
14199		$loyaltyCreditRow.find('.btn').loading({IsLabelShown: false});
14200		$loyaltyCreditRow.find('.form-controls').loading({Label: Resources.Loading.LoyaltyCredit.Applying});
14201
14202		$.ajax({
14203			url: Urls.applyLoyalty,
14204			type: 'POST',
14205			data: {
14206				csrf_token:  $loyaltyPaymentContainer.find('input[name="csrf_token"]').val(),
14207				loyaltyNumber: $loyaltyPaymentInput.val(),
14208				loyaltyPhoneNumber: $loyaltyPhoneNumberInput.val(),
14209				loyaltyZipCode: $loyaltyZipCodeInput.val()
14210			},
14211			dataType: 'json',
14212			success: function(data) {
14213				$loyaltyPaymentErrorMessage.html('').hide();
14214
14215				$loyaltyCreditRow.find('.btn').loading('destroy');
14216				$loyaltyCreditRow.find('.form-controls').loading('destroy');
14217
14218				if (data.error !== 'undefined' && data.error === true) {
14219					$loyaltyCreditRow.attr('status', 'message');
14220					$loyaltyPaymentInput.val('');
14221					$loyaltyCreditRow.remove();
14222					$loyaltyPaymentsForm.show();
14223					$loyaltyPaymentErrorMessage.html(data.errorMsg).show();
14224				} else {
14225					$loyaltyCreditRow.attr('status', 'applied');
14226					$loyaltyPaymentsForm.hide();
14227					$loyaltyCreditRow.find('.voucher').removeClass('hide');
14228					$loyaltyCreditRow.find('.basketLoyaltyNumber').html(util.getMaskedNumber(data.loyaltyNumber, 4, 'xxxx... '));
14229					$loyaltyCreditRow.find('.basketLoyaltyAmount').removeClass('hide').html(data.loyaltyCreditUsedFormatted).attr('label', Resources.LoyaltyCredit.RewardApplied);
14230
14231					var dataObject = {
14232						value: data.rewardsCreditUsed,
14233						payment_type: 'Loyalty'
14234					}
14235					GoogleTagManagerClient.PushDataLayerForDataLayerType('AddPaymentInfo', dataObject);
14236
14237					$(document).trigger('Cart.Update');
14238				}
14239			},
14240			error: function() {
14241				$loyaltyCreditRow.attr('status', 'message');
14242				$loyaltyPaymentErrorMessage.html(Resources.Error.Generic).show();
14243				resetForm(true);
14244			},
14245			complete: function(data, status, xhr) {
14246				if (data.status == 302 && data.responseText) {
14247					location.href = data.responseText;
14248				}
14249			}
14250		});
14251	});
14252
14253	$loyaltyPaymentContainer.on('click LoyaltyCredit.Remove', '.remove', function(event, data) {
14254		event.preventDefault();
14255
14256		var $loyaltyCreditRow = $(this).closest('.loyalty-row');
14257		$loyaltyCreditRow.find('.basketLoyaltyAmount').addClass('hide');
14258		$loyaltyCreditRow.find('.voucher').addClass('hide');
14259		$loyaltyCreditRow.find('.btn').loading({IsLabelShown: false});
14260		$loyaltyCreditRow.find('.form-controls').loading({Label: Resources.Loading.LoyaltyCredit.Removing});
14261
14262		var payload = {
14263			loyaltyNumber: $('input#dwfrm_billing_loyalty_loyaltyNumber').val()
14264		}
14265
14266		if (data && data.ResetSessionData) {
14267			payload.resetSessionData = true
14268		}
14269
14270		$.ajax({
14271			url: Urls.removeLoyalty,
14272			type: 'POST',
14273			dataType: 'json',
14274			data: payload,
14275			success: function() {
14276				$loyaltyPaymentErrorMessage.html('').hide();
14277
14278				if ($loyaltyCreditRow.find('.btn[loading]').length > 0) {
14279					$loyaltyCreditRow.find('.btn').loading('destroy');
14280				}
14281				if ($loyaltyCreditRow.find('.form-controls[loading]').length > 0) {
14282					$loyaltyCreditRow.find('.form-controls').loading('destroy');
14283				}
14284
14285				if ($loyaltyPaymentContainer.attr('has-loyaltynumber') !== 'true') {
14286					$loyaltyPaymentInput.val('');
14287					$loyaltyPaymentsForm.show();
14288					$loyaltyCreditRow.attr('status', 'form');
14289					$loyaltyPaymentsApplied.html('');
14290				} else {
14291					$loyaltyCreditRow.attr('status', 'pending');
14292					$loyaltyCreditRow.find('.voucher').removeClass('hide');
14293					$loyaltyCreditRow.find('.basketLoyaltyAmount').removeClass('hide').attr('label', Resources.LoyaltyCredit.Reward);
14294				}
14295
14296				$(document).trigger('Cart.Update');
14297			},
14298            error: function() {
14299				$loyaltyCreditRow.find('.btn').loading('destroy');
14300				$loyaltyCreditRow.find('.form-controls').loading('destroy');
14301
14302				$loyaltyPaymentErrorMessage.html(Resources.Error.Generic).show();
14303			}
14304		});
14305	});
14306	
14307	$loyaltyPaymentContainer.on('click', '.change', function(event) {
14308		event.preventDefault();
14309		
14310		$loyaltyPaymentContainer.attr('has-loyaltynumber', 'false');
14311		if ($loyaltyPaymentsApplied.find('.loyalty-row').attr('status') === 'applied') {
14312			$loyaltyPaymentContainer.find('.remove').trigger('LoyaltyCredit.Remove');
14313		}
14314		resetForm();
14315	});
14316
14317	$(document).off('mousedown.LoyaltyCreditPaymentPanel').on('mousedown.LoyaltyCreditPaymentPanel', function (event) {
14318        if (loyaltyCreditValidator && $(event.target).closest($loyaltyPaymentContainer).length == 0) {
14319            loyaltyCreditValidator.resetForm();
14320            $loyaltyPaymentErrorMessage.html('').hide();
14321        }
14322    });
14323	
14324	$(document).on('LoyaltyCredit.Clear', function(event) {
14325		event.preventDefault();
14326		resetForm();
14327	});
14328
14329	$loyaltyPaymentContainer.on('click', '.reset', function(event) {
14330		event.preventDefault();
14331		$loyaltyPaymentsForm.show();
14332		$loyaltyPhoneNumberInput.val('');
14333		$loyaltyZipCodeInput.val('');
14334	});
14335	
14336	if ($loyaltyPaymentContainer.attr('has-loyaltynumber') === 'true' && $loyaltyPaymentsApplied.find('.loyalty-row').length == 0 && $('[checkout-mode]').attr('checkout-mode') != 'echo') {
14337		$(document).trigger('LoyaltyCredit.Add', { Registered: true });
14338	}
14339}
14340
14341function resetForm(showErrorMessage) {
14342	if (loyaltyCreditValidator) {
14343		loyaltyCreditValidator.resetForm();
14344	}
14345	
14346	if (!showErrorMessage) {
14347		$loyaltyPaymentErrorMessage.html('').hide();
14348	}
14349	
14350	var $loyaltyCreditRow = $loyaltyPaymentsApplied.find('.loyalty-row');
14351	if ($loyaltyPaymentContainer.attr('has-loyaltynumber') !== 'true') {
14352		$loyaltyPaymentInput.val('');
14353		$loyaltyPhoneNumberInput.val('');
14354		$loyaltyZipCodeInput.val('');
14355		
14356		$loyaltyPaymentsForm.show();
14357		$loyaltyPaymentsApplied.html('');
14358	} else {
14359		$loyaltyCreditRow.find('.basketLoyaltyAmount').attr('label', Resources.LoyaltyCredit.Reward);
14360	}
14361
14362	resetSecondarySelect();
14363}
14364
14365function resetSecondarySelect() {
14366	$loyaltyPaymentSecondarySelect.prop('selectedIndex', 0);
14367	if ($loyaltyPaymentSecondarySelect.closest('.opc-summary').length > 0) {
14368		$loyaltyPaymentSecondarySelect.selectmenu('refresh');
14369	}
14370	$loyaltyPaymentSecondaryContainer.attr('secondary', '');
14371}
14372},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"../../util":102,"./opcpayment":65,"lodash":133}],56:[function(require,module,exports){
14373'use strict';
14374
14375var _ = require('lodash');
14376var loadingIndicator = require('../../components/LoadingIndicator');
14377var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
14378var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
14379
14380exports.initialize = initialize;
14381
14382var $paymentContainer = $('[payment-method="B2B_Payment_Voucher"]');
14383var $paymentForm = $paymentContainer.find('form');
14384var $paymentVoucherNumber = $paymentContainer.find('input[name="paymentvouchercode"]');
14385var $paymentMessage = $paymentContainer.find('.messages');
14386var $paymentErrorMessage = $paymentContainer.find('span.error');
14387var $paymentsApplied = $paymentContainer.find('.form-payments-applied');
14388var $paymentApplyButton = $paymentContainer.find('.apply');
14389var validator;
14390var recaptcha;
14391var options = {};
14392var loaded = {};
14393
14394function renderRecaptcha() {
14395	if (loaded.Recaptcha && $paymentApplyButton.length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForPaymentVouchers && options.Recaptcha.GoogleSiteKey) {
14396		recaptcha = grecaptcha.render('payment-voucher-apply-button', {
14397	        sitekey: options.Recaptcha.GoogleSiteKey,
14398	        callback: submitPaymentVoucher,
14399	        badge: 'bottomleft'
14400	    });
14401	}
14402}
14403
14404function initialize(optionsParameter) {
14405	options = optionsParameter || {};
14406
14407	renderRecaptcha();
14408	$(document).on('Recaptcha.Loaded', function(event) {
14409		if (!loaded.Recaptcha) {
14410			loaded.Recaptcha = true;
14411	        renderRecaptcha();
14412	    }
14413	});
14414	
14415    $paymentContainer.on('click', '.apply:not([loading])', function (event) {
14416		event.preventDefault();
14417		
14418        if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForPaymentVouchers || !options.Recaptcha.GoogleSiteKey) {
14419            $(document).trigger('PaymentVoucher.Apply');
14420        }
14421	});
14422	
14423	$(document).on('PaymentVoucher.Apply', function(event) {		
14424        event.preventDefault();
14425
14426        if (parseFloat($('[balance-due]').first().attr('balance-due')) <= 0) {
14427            $paymentErrorMessage.html(Resources.PaymentVoucher.NoBalanceDue).show();
14428            
14429            if (recaptcha != undefined) {
14430				grecaptcha.reset(recaptcha);
14431			}
14432        } else {
14433            $paymentErrorMessage.html('').hide();
14434            validator = $paymentForm.validate();
14435            $paymentVoucherNumber.rules('add', ValidationRules.PaymentVoucher.Number);
14436            validator.checkForm();
14437
14438            if (Urls && Urls.PaymentVoucher.Apply && validator.valid()) {
14439                var rowContainerHtml = _.template($('#template-paymentvoucher-row-container').html(), TemplateConstants.Lodash.Settings)();
14440                var $paymentApplied = $(rowContainerHtml).appendTo($paymentsApplied);
14441
14442                $paymentApplyButton.loading({
14443                    IsImageShown: true,
14444					IsLabelShown: false
14445                });
14446                $paymentApplied.loading({
14447                    Label: Resources.Loading.PaymentVoucher.Applying
14448                });
14449
14450                $.ajax({
14451                    method: 'POST',
14452                    url: Urls.PaymentVoucher.Apply,
14453                    contentType: 'application/json',
14454                    data: JSON.stringify({
14455                        number: $paymentVoucherNumber.val(),
14456                        amount: null
14457                    }),
14458                    dataType: 'json',
14459                    success: function (data) {
14460                        $paymentApplyButton.loading('destroy');
14461                        $paymentApplied.loading('destroy');
14462                        
14463                        if (data.success) {
14464                            $paymentVoucherNumber.val('');
14465                            
14466                            if (data.amountApplied && data.amountApplied.value > 0) {
14467                            	$paymentsApplied.find('[data-id="' + data.id + '"]').remove();                            	
14468                                $paymentApplied.attr('data-id', data.id);
14469
14470                                var rowContentHtml = _.template($('#template-paymentvoucher-row-content').html(), TemplateConstants.Lodash.Settings)({
14471                                    paymentNumber: data.number,
14472                                    amountApplied: data.amountApplied.displayValue
14473                                });
14474
14475                                $paymentApplied.html(rowContentHtml);
14476                            }
14477                            
14478                            if (data.message) {
14479                            	$paymentErrorMessage.html(data.message).show();
14480                            }
14481
14482                            $(document).trigger('Cart.Update');
14483                        } else {
14484                        	if (data.message == 'Invalid Message') {
14485                        		data.message = Resources.Error.Generic;	
14486                        	}
14487                        	
14488                            $paymentApplied.remove();
14489                            $paymentErrorMessage.html(data.message || Resources.Error.Generic).show();
14490                        }
14491                        
14492						if (recaptcha != undefined) {
14493							grecaptcha.reset(recaptcha);
14494						}
14495                    },
14496                    error: function() {
14497						$paymentApplyButton.loading('destroy');
14498						$paymentApplied.remove();
14499						$paymentErrorMessage.html(Resources.Error.Generic).show();
14500						
14501						if (recaptcha != undefined) {
14502							grecaptcha.reset(recaptcha);
14503						}
14504					}
14505                });
14506            } else {
14507            	if (recaptcha != undefined) {
14508					grecaptcha.reset(recaptcha);
14509				}	
14510            }
14511        }
14512    });
14513
14514    $(document).off('mousedown.paymentPanel').on('mousedown.paymentPanel', function (event) {
14515        if (validator && $(event.target).closest($paymentContainer).length == 0) {
14516            validator.resetForm();
14517            $paymentErrorMessage.html('').hide();
14518        }
14519    });
14520
14521    $(document).on('CartData.Update', function (event, data) {
14522    	$('[has-gift-cards]').attr('has-gift-cards', data.HasGiftCards || false);
14523	    $('[has-gift-cards-only]').attr('has-gift-cards-only', data.HasGiftCardsOnly || false);
14524	    
14525	    if (data.HasGiftCards && !data.AllowpurchaseWithGiftCard) {
14526	    	$paymentMessage.html(Resources.GiftCard.purchaseWithGiftCardNotAllowed);
14527	    	$paymentContainer.attr('status', 'locked');
14528	    } else {
14529	    	$paymentMessage.html('');
14530	    	$paymentContainer.attr('status', 'active');
14531	    }
14532    });
14533
14534    $paymentContainer.on('click', '.remove:not([loading])', function (event) {
14535        event.preventDefault();
14536
14537        var $row = $(this).closest('.form-payment-applied');
14538        $row.loading({
14539            Label: Resources.Loading.GiftCard.Removing
14540        });
14541        $paymentErrorMessage.html('').hide();
14542
14543        $.ajax({
14544            url : Urls.PaymentVoucher.Remove,
14545            type : 'POST',
14546            contentType: 'application/json',
14547            data: JSON.stringify({
14548                id: $row.data('id')
14549            }),
14550            dataType : 'json',
14551            success : function(data) {
14552            	$row.loading('destroy');
14553            	
14554                if (data.success) {
14555                    $row.remove();
14556
14557                    $(document).trigger('Cart.Update');
14558                } else {
14559                	$paymentErrorMessage.html(data.error || Resources.Error.Generic).show();
14560                }
14561            },
14562            error: function() {
14563				$row.loading('destroy');
14564				$paymentErrorMessage.html(Resources.Error.Generic).show();
14565			}
14566        });
14567    });
14568}
14569
14570},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../components/LoadingIndicator":25,"lodash":133}],57:[function(require,module,exports){
14571'use strict';
14572/* global Resources, Urls */
14573
14574var _ = require('lodash');
14575var loadingIndicator = require('../../components/LoadingIndicator');
14576var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
14577var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
14578var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
14579var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
14580var opcpayment = require('./opcpayment');
14581var util = require('../../util');
14582
14583exports.initialize = initialize;
14584
14585var $rewardsPaymentContainer = $('[payment-method="REWARDS_CREDIT"][has-rewardsnumber]');
14586var $rewardsPaymentsForm = $rewardsPaymentContainer.find('form');
14587var $rewardsPaymentsApplied = $rewardsPaymentContainer.find('.rewards-applied');
14588var $rewardsPaymentInput = $rewardsPaymentContainer.find('#dwfrm_billing_rewards_rewardsNumber');
14589var $rewardsEmailInput = $rewardsPaymentContainer.find('#dwfrm_billing_rewards_rewardsEmail');
14590var $rewardsPhoneNumberInput = $rewardsPaymentContainer.find('#dwfrm_billing_rewards_rewardsPhoneNumber');
14591var $rewardsZipCodeInput = $rewardsPaymentContainer.find('#dwfrm_billing_rewards_rewardsZipCode');
14592var $rewardsPaymentSecondaryContainer = $rewardsPaymentContainer.find('.rewards-secondary');
14593var $rewardsPaymentSecondarySelect = $rewardsPaymentContainer.find('#rewards-secondary-select');
14594var $rewardsPaymentErrorMessage = $rewardsPaymentContainer.find('span.error');
14595var rewardsCreditValidator;
14596var rewardsCreditRowTemplate = _.template($('#template-rewards-credit-row').html(), TemplateConstants.Lodash.Settings);
14597var rewardsCreditRecaptcha;
14598var options = {};
14599var loaded = {};
14600
14601function renderRecaptcha() {
14602	if (loaded.Recaptcha && $('#rewards-credit-add-button').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForRe
14602wardsCredit && options.Recaptcha.GoogleSiteKey) {
14603		rewardsCreditRecaptcha = grecaptcha.render('rewards-credit-add-button', {
14604	        sitekey: options.Recaptcha.GoogleSiteKey,
14605	        callback: submitRewardsCredit,
14606	        badge: 'bottomleft'
14607	    });
14608	}
14609}
14610
14611function initialize(optionsParameter) {
14612	options = optionsParameter || {};
14613	
14614	renderRecaptcha();
14615	$(document).on('Recaptcha.Loaded', function(event) {
14616		if (!loaded.Recaptcha) {
14617			loaded.Recaptcha = true;
14618	        renderRecaptcha();
14619	    }
14620	});
14621
14622	if ($rewardsPaymentSecondarySelect.closest('.opc-summary').length > 0) {
14623		$rewardsPaymentSecondarySelect.selectmenu({
14624			width: '100%',
14625			appendTo: '[payment-method="REWARDS_CREDIT"] .rewards-secondary',
14626			change: function(event, ui) {
14627				$rewardsPaymentSecondaryContainer.attr('secondary', ui && ui.item && ui.item.value ? ui.item.value : '');
14628			}
14629		});
14630	}
14631	
14632	$rewardsPaymentContainer.on('click', '.reset', function(event) {
14633		event.preventDefault();
14634		resetSecondarySelect();
14635	});
14636
14637	$rewardsPaymentContainer.on('click', '.add', function(event) {
14638		event.preventDefault();
14639		
14640        if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForRewardsCredit || !options.Recaptcha.GoogleSiteKey) {
14641            $(document).trigger('RewardsCredit.Add'); 
14642        }
14643	});
14644	
14645	$(document).on('RewardsCredit.Add', function(event, data) {
14646		event.preventDefault();
14647
14648		if (parseFloat($('[balance-due]').first().attr('balance-due')) <= 0) {
14649			$rewardsPaymentErrorMessage.html(Resources.RewardsCredit.NoBalanceDue).show();
14650		} else {
14651			var rewardsSecondaryFactor = $rewardsPaymentSecondaryContainer.attr('secondary');
14652			rewardsCreditValidator = $rewardsPaymentsForm.validate();
14653            $rewardsPaymentInput.rules('add', ValidationRules.RewardsCredit.Number);
14654
14655			switch (rewardsSecondaryFactor) {
14656				case "zipcode":
14657					$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_POSTAL_CODE);
14658					$rewardsZipCodeInput.rules('add', ValidationRules.ZipCode);
14659					break;
14660
14661				default:
14662					break;
14663			}
14664
14665            rewardsCreditValidator.form();
14666
14667			// Adding ADA compliance error messages for giftCardPayment section
14668            if ($rewardsPaymentsForm.find('.field.error').length > 0) {
14669				$('.payment-declined').remove();
14670                var rewardsPaymentFormErrorMessages = opcpayment.getErrorMessages($rewardsPaymentsForm);
14671                var rewardsPaymentFormErrorBox = opcpayment.errorBox('
14671Cannot Proceed', 'Please fix the following errors:', rewardsPaymentFormErrorMessages);
14672                if (rewardsPaymentFormErrorBox) {
14673                    $rewardsPaymentsApplied.after(rewardsPaymentFormErrorBox);
14674                }
14675            }
14676			
14677            if (Urls && Urls.checkRewards && rewardsCreditValidator.valid()) {
14678            	$rewardsPaymentsForm.hide();
14679            	
14680				var $rewardsCreditRow = $(rewardsCreditRowTemplate());
14681				$rewardsCreditRow = $rewardsCreditRow.appendTo($rewardsPaymentsApplied);
14682				$rewardsCreditRow.attr('status', 'pending');
14683				$rewardsCreditRow.find('.basketRewardNumber').html(util.getMaskedNumber($rewardsPaymentInput.val(), 4, 'xxxx... '));
14684	
14685				$rewardsCreditRow.find('.btn').loading({IsLabelShown: false});
14686				$rewardsCreditRow.find('.form-controls').loading({Label: Resources.Loading.RewardsCredit.Loading});
14687	
14688				$.ajax({
14689					url: data && data.Registered ? Urls.checkRewardsForRegistered : Urls.checkRewards,
14690					type: 'POST',
14691					data: {
14692						csrf_token:  $rewardsPaymentContainer.find('input[name="csrf_token"]').val(),
14693						rewardsNumber: $rewardsPaymentInput.val(),
14694						rewardsEmail: $rewardsEmailInput.val(),
14695						rewardsPhoneNumber: $rewardsPhoneNumberInput.val(),
14696						rewardsZipCode: $rewardsZipCodeInput.val()
14697					},
14698					dataType: 'json',
14699					success: function(data) {
14700						$rewardsPaymentErrorMessage.html('').hide();
14701	
14702						if ($rewardsCreditRow.find('.btn[loading]').length > 0) {
14703							$rewardsCreditRow.find('.btn').loading('destroy');
14704						}
14705						if ($rewardsCreditRow.find('.form-controls[loading]').length > 0) {
14706							$rewardsCreditRow.find('.form-controls').loading('destroy');
14707						}
14708	
14709						if (data && data.error !== 'undefined' && data.error === true) {
14710							$rewardsCreditRow.remove();
14711							$rewardsPaymentsForm.show();
14712							$rewardsPaymentErrorMessage.html(data.errorMsg).show();
14713						} else if (data && parseFloat(data.creditAmount) <= 0) {
14714							$rewardsPaymentContainer.attr('has-rewardsnumber', 'true');
14715							$rewardsCreditRow.attr('status', 'message');
14716							$rewardsCreditRow.find('.message').html(Resources.RewardsCredit.NoBalanceAvailable);
14717						} else if (data && data.creditAmountFormatted) {
14718							$rewardsPaymentContainer.attr('has-rewardsnumber', 'true');
14719							$rewardsCreditRow.find('.basketRewardAmount').html(data.creditAmountFormatted.replace('.00', ''));
14720							$rewardsCreditRow.find('.expiration-date').html(data.creditExpirationDate);
14721						}
14722						
14723						if (rewardsCreditRecaptcha != undefined) {
14724							grecaptcha.reset(rewardsCreditRecaptcha);
14725						}
14726					},
14727					error: function() {
14728						$rewardsCreditRow.remove();
14729						$rewardsPaymentsForm.show();
14730						$rewardsPaymentErrorMessage.html(Resources.Error.Generic).show();
14731						
14732						if (rewardsCreditRecaptcha != undefined) {
14733							grecaptcha.reset(rewardsCreditRecaptcha);
14734						}
14735					},
14736					complete: function(data, status, xhr) {
14737						if (data.status == 302 && data.responseText) {
14738							location.href = data.responseText;
14739						}
14740					}
14741				});
14742            }
14743		}
14744	});
14745
14746	$rewardsPaymentContainer.on('click', '.apply', function(event) {
14747		event.preventDefault();
14748
14749		var $rewardsCreditRow = $(this).closest('.reward-row');
14750
14751		$rewardsCreditRow.find('.btn').loading({IsLabelShown: false});
14752		$rewardsCreditRow.find('.form-controls').loading({Label: Resources.Loading.RewardsCredit.Applying});
14753
14754		$.ajax({
14755			url: Urls.applyRewards,
14756			type: 'POST',
14757			data: {
14758				csrf_token:  $rewardsPaymentContainer.find('input[name="csrf_token"]').val(),
14759				rewardsNumber: $rewardsPaymentInput.val(),
14760				rewardsEmail: $rewardsEmailInput.val(),
14761				rewardsPhoneNumber: $rewardsPhoneNumberInput.val(),
14762				rewardsZipCode: $rewardsZipCodeInput.val()
14763			},
14764			dataType: 'json',
14765			success: function(data) {
14766				$rewardsPaymentErrorMessage.html('').hide();
14767				if ($rewardsCreditRow.find('.btn[loading]').length > 0) {
14768					$rewardsCreditRow.find('.btn').loading('destroy');
14769				}
14770				if ($rewardsCreditRow.find('.form-controls[loading]').length > 0) {
14771					$rewardsCreditRow.find('.form-controls').loading('destroy');
14772				}
14773
14774				if (data.error !== 'undefined' && data.error === true) {
14775					$rewardsCreditRow.attr('status', 'message');
14776					$rewardsPaymentInput.val('');
14777					$rewardsCreditRow.remove();
14778					$rewardsPaymentsForm.show();
14779					$rewardsPaymentErrorMessage.html(data.errorMsg).show();
14780				} else {
14781					$rewardsCreditRow.attr('status', 'applied');
14782					$rewardsCreditRow.find('.basketRewardNumber').html(util.getMaskedNumber(data.rewardsNumber, 4, 'xxxx... '));
14783					$rewardsCreditRow.find('.basketRewardAmount').html(data.rewardsCreditUsedFormatted).attr('label', Resources.RewardsCredit.RewardApplied);
14784					$rewardsCreditRow.find('.expiration-date').html(data.creditExpirationDate);
14785
14786					var dataObject = {
14787						value: data.rewardsCreditUsed,
14788						payment_type: 'B Rewarded'
14789					}
14790					GoogleTagManagerClient.PushDataLayerForDataLayerType('AddPaymentInfo', dataObject);
14791					$(document).trigger('Cart.Update');
14792				}
14793			},
14794			error: function() {
14795				$rewardsCreditRow.attr('status', 'message');
14796				$rewardsPaymentErrorMessage.html(Resources.Error.Generic).show();
14797				resetForm(true);
14798			},
14799			complete: function(data, status, xhr) {
14800				if (data.status == 302 && data.responseText) {
14801					location.href = data.responseText;
14802				}
14803			}
14804		});
14805	});
14806
14807	$rewardsPaymentContainer.on('click RewardsCredit.Remove', '.remove', function(event, data) {
14808		event.preventDefault();
14809
14810		var $rewardsCreditRow = $(this).closest('.reward-row');
14811		$rewardsCreditRow.find('.btn').loading({IsLabelShown: false});
14812		$rewardsCreditRow.find('.form-controls').loading({Label: Resources.Loading.RewardsCredit.Removing});
14813
14814		var payload = {
14815			rewardsNumber: $('input#dwfrm_billing_rewards_rewardsNumber').val()
14816		}
14817
14818		if (data && data.ResetSessionData) {
14819			payload.resetSessionData = true
14820		}
14821
14822		$.ajax({
14823			url: Urls.removeRewards,
14824			type: 'POST',
14825			dataType: 'json',
14826			data: payload,
14827			success: function() {
14828				$rewardsPaymentErrorMessage.html('').hide();
14829
14830				if ($rewardsCreditRow.find('.btn[loading]').length > 0) {
14831					$rewardsCreditRow.find('.btn').loading('destroy');
14832				}
14833				if ($rewardsCreditRow.find('.form-controls[loading]').length > 0) {
14834					$rewardsCreditRow.find('.form-controls').loading('destroy');
14835				}
14836
14837				if ($rewardsPaymentContainer.attr('has-rewardsnumber') !== 'true') {
14838					$rewardsPaymentInput.val('');
14839					$rewardsPaymentsForm.show();
14840					$rewardsCreditRow.attr('status', 'form');
14841					$rewardsPaymentsApplied.html('');
14842				} else {
14843					$rewardsCreditRow.attr('status', 'pending');
14844					$rewardsCreditRow.find('.basketRewardAmount').attr('label', Resources.RewardsCredit.Reward);
14845				}
14846				
14847				$(document).trigger('Cart.Update');
14848			},
14849            error: function() {
14850				if ($rewardsCreditRow.find('.btn[loading]').length > 0) {
14851					$rewardsCreditRow.find('.btn').loading('destroy');
14852				}
14853				if ($rewardsCreditRow.find('.form-controls[loading]').length > 0) {
14854					$rewardsCreditRow.find('.form-controls').loading('destroy');
14855				}
14856
14857				$rewardsPaymentErrorMessage.html(Resources.Error.Generic).show();
14858			}
14859		});
14860	});
14861	
14862	$rewardsPaymentContainer.on('click', '.change', function(event) {
14863		event.preventDefault();
14864		
14865		$rewardsPaymentContainer.attr('has-rewardsnumber', 'false');
14866		if ($rewardsPaymentsApplied.find('.reward-row').attr('status') === 'applied') {
14867			$rewardsPaymentContainer.find('.remove').trigger('RewardsCredit.Remove');
14868		}
14869		resetForm();
14870	});
14871
14872	$(document).off('mousedown.RewardsCreditPaymentPanel').on('mousedown.RewardsCreditPaymentPanel', function (event) {
14873        if (rewardsCreditValidator && $(event.target).closest($rewardsPaymentContainer).length == 0) {
14874            rewardsCreditValidator.resetForm();
14875            $rewardsPaymentErrorMessage.html('').hide();
14876        }
14877    });
14878	
14879	$(document).on('RewardsCredit.Clear', function(event) {
14880		event.preventDefault();
14881		resetForm();
14882	});
14883
14884	$rewardsPaymentContainer.on('click', '.reset', function(event) {
14885		event.preventDefault();
14886		$rewardsPaymentsForm.show();
14887		$rewardsPhoneNumberInput.val('');
14888		$rewardsZipCodeInput.val('');
14889	});
14890	
14891	if (($rewardsPaymentContainer.attr('has-rewardsnumber') === 'true' && ($rewardsPhoneNumberInput.val() || $rewardsZipCodeInput.val())) && $rewardsPaymentsApplied.find('.reward-row').length == 0 && $('[checkout-mode]').attr('checkout-mode') != 'echo') {
14892		$(document).trigger('RewardsCredit.Add', { Registered: true });
14893	} 
14894}
14895
14896function resetForm(showErrorMessage) {
14897	if (rewardsCreditValidator) {
14898		rewardsCreditValidator.resetForm();
14899	}
14900	
14901	if (!showErrorMessage) {
14902		$rewardsPaymentErrorMessage.html('').hide();
14903	}
14904	
14905	var $rewardsCreditRow = $rewardsPaymentsApplied.find('.reward-row');
14906	if ($rewardsPaymentContainer.attr('has-rewardsnumber') !== 'true') {
14907		$rewardsPaymentInput.val('');
14908		$rewardsPhoneNumberInput.val('');
14909		$rewardsZipCodeInput.val('');
14910		
14911		$rewardsPaymentsForm.show();
14912		$rewardsPaymentsApplied.html('');
14913	} else {
14914		$rewardsCreditRow.find('.basketRewardAmount').attr('label', Resources.RewardsCredit.Reward);
14915	}
14916
14917	resetSecondarySelect();
14918}
14919
14920function resetSecondarySelect() {
14921	$rewardsPaymentSecondarySelect.prop('selectedIndex', 0);
14922	if ($rewardsPaymentSecondarySelect.closest('.opc-summary').length > 0) {
14923		$rewardsPaymentSecondarySelect.selectmenu('refresh');
14924	}
14925	$rewardsPaymentSecondaryContainer.attr('secondary', '');
14926}
14927},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../components/LoadingIndicator":25,"../../util":102,"./opcpayment":65,"lodash":133}],58:[function(require,module,exports){
14928'use strict';
14929
14930var _ = require('lodash');
14931var loadingIndicator = require('../../components/LoadingIndicator');
14932var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
14933var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
14934
14935exports.initialize = initialize;
14936
14937var $paymentContainer = $('[payment-method="Workforce_Voucher"]');
14938var $paymentForm = $paymentContainer.find('form');
14939var $paymentMessage = $paymentContainer.find('.messages');
14940var $paymentErrorMessage = $paymentContainer.find('span.error');
14941var $paymentsApplied = $paymentContainer.find('.form-payments-applied');
14942var recaptcha;
14943var options = {};
14944var loaded = {};
14945
14946function renderRecaptcha() {
14947	if (loaded.Recaptcha && options && options.Recaptcha && options.Recaptcha.EnabledForPaymentVouchers && options.Recaptcha.GoogleSiteKey) {
14948		recaptcha = grecaptcha.render('payment-voucher-apply-button', {
14949	        sitekey: options.Recaptcha.GoogleSiteKey,
14950	        callback: submitPaymentVoucher,
14951	        badge: 'bottomleft'
14952	    });
14953	}
14954}
14955
14956function initialize(optionsParameter) {
14957	options = optionsParameter || {};
14958
14959	renderRecaptcha();
14960	$(document).on('Recaptcha.Loaded', function(event) {
14961		if (!loaded.Recaptcha) {
14962			loaded.Recaptcha = true;
14963	        renderRecaptcha();
14964	    }
14965	});
14966	
14967    $paymentContainer.on('click', '.apply:not([loading]):not([disabled])', function (event) {
14968		event.preventDefault();
14969
14970        if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForPaymentVouchers || !options.Recaptcha.GoogleSiteKey) {
14971            $(document).trigger('WorkforceVoucher.Apply', event);
14972        }
14973	});
14974	
14975	$(document).on('WorkforceVoucher.Apply', function(event, data) {		
14976        event.preventDefault();
14977        var $this = $(data.target);
14978        var $voucher = $this.closest('[voucher-id');
14979
14980        if (parseFloat($('[balance-due]').first().attr('balance-due')) <= 0) {
14981            $paymentErrorMessage.html(Resources.PaymentVoucher.NoBalanceDue).show();
14982            
14983            if (recaptcha != undefined) {
14984				grecaptcha.reset(recaptcha);
14985			}
14986        } else {
14987            $paymentErrorMessage.html('').hide();
14988
14989            if (Urls.Workforce && Urls.Workforce.Vouchers && Urls.Workforce.Vouchers.PaymentPanel && Urls.Workforce.Vouchers.PaymentPanel.Apply) {
14990                $this.loading({
14991                    IsImageShown: true,
14992					IsLabelShown: false
14993                });
14994
14995                $.ajax({
14996                    method: 'POST',
14997                    url: Urls.Workforce.Vouchers.PaymentPanel.Apply,
14998                    contentType: 'application/json',
14999                    data: JSON.stringify({
15000                        id: $voucher.attr('voucher-id')
15001                    }),
15002                    dataType: 'json',
15003                    success: function (data) {
15004                        $this.loading('destroy');
15005                        
15006                        if (data.success) {
15007                            if (data.amountApplied && data.amountApplied.value > 0) {
15008                            	$voucher.attr('is-applied', 'true');
15009                            }
15010                            
15011                            if (data.message) {
15012                            	$paymentErrorMessage.html(data.message).show();
15013                            }
15014
15015                            $(document).trigger('Cart.Update');
15016                        } else {
15017                        	if (data.message == 'Invalid Message') {
15018                        		data.message = Resources.Error.Generic;	
15019                        	}
15020                        	
15021                            $paymentErrorMessage.html(data.message || Resources.Error.Generic).show();
15022                        }
15023                        
15024						if (recaptcha != undefined) {
15025							grecaptcha.reset(recaptcha);
15026						}
15027                    },
15028                    error: function() {
15029						$this.loading('destroy');
15030						$paymentErrorMessage.html(Resources.Error.Generic).show();
15031						
15032						if (recaptcha != undefined) {
15033							grecaptcha.reset(recaptcha);
15034						}
15035					}
15036                });
15037            } else {
15038            	if (recaptcha != undefined) {
15039					grecaptcha.reset(recaptcha);
15040				}	
15041            }
15042        }
15043    });
15044
15045    $(document).off('mousedown.paymentPanel').on('mousedown.paymentPanel', function (event) {
15046        if ($(event.target).closest($paymentContainer).length == 0) {
15047            $paymentErrorMessage.html('').hide();
15048        }
15049    });
15050
15051    $(document).on('CartData.Update', function (event, data) {
15052    	$('[has-gift-cards]').attr('has-gift-cards', data.HasGiftCards || false);
15053	    $('[has-gift-cards-only]').attr('has-gift-cards-only', data.HasGiftCardsOnly || false);
15054	    
15055	    if (data.HasGiftCards && !data.AllowpurchaseWithGiftCard) {
15056	    	$paymentMessage.html(Resources.GiftCard.purchaseWithGiftCardNotAllowed);
15057	    	$paymentContainer.attr('status', 'locked');
15058	    } else {
15059	    	$paymentMessage.html('');
15060	    	$paymentContainer.attr('status', 'active');
15061	    }
15062    });
15063
15064    $paymentContainer.on('click', '.remove:not([loading])', function (event) {
15065        event.preventDefault();
15066
15067        var $this = $(this);
15068        var $voucher = $this.closest('.workforce-voucher');
15069        $this.loading({
15070            IsImageShown: true,
15071            IsLabelShown: false
15072        });
15073        $paymentErrorMessage.html('').hide();
15074
15075        $.ajax({
15076            url: Urls.Workforce.Vouchers.PaymentPanel.Remove,
15077            type: 'POST',
15078            contentType: 'application/json',
15079            data: JSON.stringify({
15080                id: $voucher.attr('voucher-id')
15081            }),
15082            dataType : 'json',
15083            success : function(data) {
15084            	$this.loading('destroy');
15085            	
15086                if (data.success) {
15087                    $voucher.attr('is-applied', 'false');
15088                    $(document).trigger('Cart.Update');
15089                } else {
15090                	$paymentErrorMessage.html(data.error || Resources.Error.Generic).show();
15091                }
15092            },
15093            error: function() {
15094				$this.loading('destroy');
15095				$paymentErrorMessage.html(Resources.Error.Generic).show();
15096			}
15097        });
15098    });
15099}
15100
15101},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../components/LoadingIndicator":25,"lodash":133}],59:[function(require,module,exports){
15102'use strict';
15103
15104var util = require('../../util');
15105var shipping = require('./shipping');
15106
15107/**
15108 * @function
15109 * @description Selects the first address from the list of addresses
15110 */
15111exports.init = function () {
15112    var $form = $('.address');
15113    // select address from list
15114    $('select[name$="_addressList"]', $form).on('change', function () {
15115        var selected = $(this).children(':selected').first();
15116        var selectedAddress = $(selected).data('address');
15117        if (!selectedAddress) { return; }
15118        util.fillAddressFields(selectedAddress, $form);
15119        shipping.updateShippingMethodList();
15120        // re-validate the form
15121        $form.validate().form();
15122    });
15123};
15124
15125},{"../../util":102,"./shipping":66}],60:[function(require,module,exports){
15126'use strict';
15127
15128var ajax = require('../../ajax');
15129var formPrepare = require('./formPrepare');
15130var giftcard = require('../../giftcard');
15131var util = require('../../util');
15132
15133var PaymentMethodConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
15134
15135/**
15136 * @function
15137 * @description Fills the Credit Card form with the passed data-
15137parameter and clears the former cvn input
15138 * @param {Object} data The Credit Card data (holder, type, masked number, expiration month/year)
15139 */
15140function setCCFields(data) {
15141    var $creditCard = $('[data-method="' + PaymentMethodConstants.CreditCard + '"]');
15142    $creditCard.find('input[name$="creditCard_owner"]').val(data.holder).trigger('change');
15143    $creditCard.find('select[name$="_type"]').val(data.type).trigger('change');
15144    $creditCard.find('input[name*="_creditCard_number"]').val(data.maskedNumber).trigger('change');
15145    $creditCard.find('[name$="_month"]').val(data.expirationMonth).trigger('change');
15146    $creditCard.find('[name$="_year"]').val(data.expirationYear).trigger('change');
15147    $creditCard.find('input[name$="_cvn"]').val('').trigger('change');
15148}
15149
15150/**
15151 * @function
15152 * @description Updates the credit card form with the attributes of a given card
15153 * @param {String} cardID the credit card ID of a given card
15154 */
15155function populateCreditCardForm(cardID) {
15156    // load card details
15157    var url = util.appendParamToURL(Urls.billingSelectCC, 'creditCardUUID', cardID);
15158    ajax.getJson({
15159        url: url,
15160        callback: function (data) {
15161            if (!data) {
15162                window.alert(Resources.CC_LOAD_ERROR);
15163                return false;
15164            }
15165            setCCFields(data);
15166        }
15167    });
15168}
15169
15170
15171function populatePrivateLabelCardForm(cardID) {
15172    // load card details
15173    var url = util.appendParamToURL(Urls.billingSelectPrivateLabel, 'creditCardUUID', cardID);
15174    ajax.getJson({
15175        url: url,
15176        callback: function (data) {
15177            if (!data) {
15178                window.alert(Resources.CC_LOAD_ERROR);
15179                return false;
15180            }
15181            var $privateLabelCard = $('[data-method="' + PaymentMethodConstants.AllianceData + '"]');
15182            $privateLabelCard.find('input[name*="_privateLabelCredit_number"]').val(data.maskedNumber).trigger('change');
15183        }
15184    });
15185}
15186
15187
15188/**
15189 * @function
15190 * @description Changes the payment method form depending on the passed paymentMethodID
15191 * @param {String} paymentMethodID the ID of the payment method, to which the payment method form should be changed to
15192 */
15193function updatePaymentMethod(paymentMethodID) {
15194    var $paymentMethods = $('.payment-method');
15195    $paymentMethods.removeClass('payment-method-expanded');
15196
15197    var $selectedPaymentMethod = $paymentMethods.filter('[data-method="' + paymentMethodID + '"]');
15198    if ($selectedPaymentMethod.length === 0) {
15199        $selectedPaymentMethod = $('[data-method="Custom"]');
15200    }
15201    $selectedPaymentMethod.addClass('payment-method-expanded');
15202
15203    // ensure checkbox of payment method is checked
15204    $('input[name$="_selectedPaymentMethodID"]').removeAttr('checked');
15205    $('input[value=' + paymentMethodID + ']').prop('checked', 'checked');
15206
15207    formPrepare.validateForm();
15208}
15209
15210/**
15211 * @function
15212 * @description loads billing address, Gift Certificates, Coupon and Payment methods
15213 */
15214exports.init = function () {
15215    var $checkoutForm = $('.checkout-billing');
15216    var $addGiftCert = $('#add-giftcert');
15217    var $giftCertCode = $('input[name$="_giftCertCode"]');
15218    var $addCoupon = $('#add-coupon');
15219    var $couponCode = $('input[name$="_couponCode"]');
15220    var $selectPaymentMethod = $('.payment-method-options');
15221    var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();
15222
15223    formPrepare.init({
15224        formSelector: 'form[id$="billing"]',
15225        continueSelector: '[name$="billing_save"]'
15226    });
15227
15228    // default payment method to 'CREDIT_CARD'
15229    updatePaymentMethod((selectedPaymentMethod) ? selectedPaymentMethod : PaymentMethodConstants.CreditCard);
15230    $selectPaymentMethod.on('click', 'input[type="radio"]', function () {
15231        updatePaymentMethod($(this).val());
15232    });
15233
15234    // select credit card from list
15235    var $creditCardList = $('#creditCardList');
15236    $creditCardList.on('change', function () {
15237        var cardUUID = $(this).val();
15238        if (!cardUUID) {return;}
15239        populateCreditCardForm(cardUUID);
15240
15241        // remove server side error
15242        $('.required.error').removeClass('error');
15243        $('.error-message').remove();
15244    });
15245    
15246    //preselect default credit card
15247    if($creditCardList.length > 0){
15248		$creditCardList.find('option').eq(1).prop('selected', true);
15249		$creditCardList.trigger('change');
15250	}
15251
15252    
15253    // select private label card from list
15254    var $privateLabelCardList = $('#privateLabelCardList');
15255    $privateLabelCardList.on('change', function () {
15256        var cardUUID = $(this).val();
15257        if (!cardUUID) {return;}
15258        populatePrivateLabelCardForm(cardUUID);
15259
15260        // remove server side error
15261        $('.required.error').removeClass('error');
15262        $('.error-message').remove();
15263    });
15264
15265    // preselect default private label card
15266    if($privateLabelCardList.length > 0){
15267    	$privateLabelCardList.find('option').eq(1).prop('selected', true);
15268    	$privateLabelCardList.trigger('change');
15269	} 
15270    
15271    $('#check-giftcert').on('click', function (e) {
15272        e.preventDefault();
15273        var $balance = $('.balance');
15274        if ($giftCertCode.length === 0 || $giftCertCode.val().length === 0) {
15275            var error = $balance.find('span.error');
15276            if (error.length === 0) {
15277                error = $('<span>').addClass('error').appendTo($balance);
15278            }
15279            error.html(Resources.GIFT_CERT_MISSING);
15280            return;
15281        }
15282
15283        giftcard.checkBalance($giftCertCode.val(), function (data) {
15284            if (!data || !data.giftCertificate) {
15285                $balance.html(Resources.GIFT_CERT_INVALID).removeClass('success').addClass('error');
15286                return;
15287            }
15288            $balance.html(Resources.GIFT_CERT_BALANCE + ' ' + data.giftCertificate.balance).removeClass('error').addClass('success');
15289        });
15290    });
15291
15292    $addGiftCert.on('click', function (e) {
15293        e.preventDefault();
15294        var code = $giftCertCode.val(),
15295            $error = $checkoutForm.find('.giftcert-error');
15296        if (code.length === 0) {
15297            $error.html(Resources.GIFT_CERT_MISSING);
15298            return;
15299        }
15300
15301        var url = util.appendParamsToUrl(Urls.redeemGiftCert, {giftCertCode: code, format: 'ajax'});
15302        $.getJSON(url, function (data) {
15303            var fail = false;
15304            var msg = '';
15305            if (!data) {
15306                msg = Resources.BAD_RESPONSE;
15307                fail = true;
15308            } else if (!data.success) {
15309                msg = data.message.split('<').join('&lt;').split('>').join('&gt;');
15310                fail = true;
15311            }
15312            if (fail) {
15313                $error.html(msg);
15314                return;
15315            } else {
15316                window.location.assign(Urls.billing);
15317            }
15318        });
15319    });
15320
15321    $addCoupon.on('click', function (e) {
15322        e.preventDefault();
15323        var $error = $checkoutForm.find('.coupon-error'),
15324            code = $couponCode.val();
15325        if (code.length === 0) {
15326            $error.html(Resources.COUPON_CODE_MISSING);
15327            return;
15328        }
15329
15330        var url = util.appendParamsToUrl(Urls.addCoupon, {couponCode: code, format: 'ajax'});
15331        $.getJSON(url, function (data) {
15332            var fail = false;
15333            var msg = '';
15334            if (!data) {
15335                msg = Resources.BAD_RESPONSE;
15336                fail = true;
15337            } else if (!data.success) {
15338                msg = data.message.split('<').join('&lt;').split('>').join('&gt;');
15339                fail = true;
15340            }
15341            if (fail) {
15342                $error.html(msg);
15343                return;
15344            }
15345
15346            //basket check for displaying the payment section, if the adjusted total of the basket is 0 after applying the coupon
15347            //this will force a page refresh to display the coupon message based on a parameter message
15348            if (data.success && data.baskettotal === 0) {
15349                window.location.assign(Urls.billing);
15350            }
15351        });
15352    });
15353
15354    // trigger events on enter
15355    $couponCode.on('keydown', function (e) {
15356        if (e.which === 13) {
15357            e.preventDefault();
15358            $addCoupon.click();
15359        }
15360    });
15361    $giftCertCode.on('keydown', function (e) {
15362        if (e.which === 13) {
15363            e.preventDefault();
15364            $addGiftCert.click();
15365        }
15366    });
15367};
15368
15369},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"../../ajax":3,"../../giftcard":38,"../../util":102,"./formPrepare":61}],61:[function(require,module,exports){
15370'use strict';
15371
15372var _ = require('lodash');
15373
15374var $form, $continue, $requiredInputs, validator;
15375
15376var hasEmptyRequired = function () {
15377    // filter out only the visible fields
15378    var requiredValues = $requiredInputs.filter(':visible').map(function () {
15379        return $(this).val();
15380    });
15381    return _(requiredValues).contains('');
15382};
15383
15384var validateForm = function () {
15385    // only validate form when all required fields are filled to avoid
15386    // throwing errors on empty form
15387    if (!validator) {
15388        return;
15389    }
15390    if (!hasEmptyRequired()) {
15391        if (validator.form()) {
15392            $continue.removeAttr('disabled');
15393        }
15394    } else {
15395        $continue.attr('disabled', 'disabled');
15396    }
15397};
15398
15399var validateEl = function () {
15400    if ($(this).val() === '') {
15401        $continue.attr('disabled', 'disabled');
15402    } else {
15403        // enable continue button on last required field that is valid
15404        // only validate single field
15405        if (validator.element(this) && !hasEmptyRequired()) {
15406            $continue.removeAttr('disabled');
15407        } else {
15408            $continue.attr('disabled', 'disabled');
15409        }
15410    }
15411};
15412
15413var init = function (opts) {
15414    if (!opts.formSelector || !opts.continueSelector) {
15415        throw new Error('Missing form and continue action selectors.');
15416    }
15417    $form = $(opts.formSelector);
15418    $continue = $(opts.continueSelector);
15419    validator = $form.validate();
15420    $requiredInputs = $('.required', $form).find(':input');
15421    validateForm();
15422    // start listening
15423    $requiredInputs.on('change', validateEl);
15424    $requiredInputs.filter('input').on('keyup', _.debounce(validateEl, 200));
15425};
15426
15427exports.init = init;
15428exports.validateForm = validateForm;
15429exports.validateEl = validateEl;
15430
15431},{"lodash":133}],62:[function(require,module,exports){
15432'use strict';
15433
15434var address = require('./address'),
15435    billing = require('./billing'),
15436    multiship = require('./multiship'),
15437    shipping = require('./shipping');
15438
15439/**
15440 * @function Initializes the page events depending on the checkout stage (shipping/billing)
15441 */
15442exports.init = function () {
15443    address.init();
15444    if ($('.checkout-shipping').length > 0) {
15445        shipping.init();
15446    } else if ($('.checkout-multi-shipping').length > 0) {
15447        multiship.init();
15448    } else {
15449        billing.init();
15450    }
15451
15452    //if on the order review page and there are products that are not available diable the submit order button
15453    if ($('.order-summary-footer').length > 0) {
15454        if ($('.notavailable').length > 0) {
15455            $('.order-summary-footer .submit-order .button-fancy-large').attr('disabled', 'disabled');
15456        }
15457    }
15458};
15459
15460},{"./address":59,"./billing":60,"./multiship":63,"./shipping":66}],63:[function(require,module,exports){
15461'use strict';
15462
15463var address = require('./address'),
15464    formPrepare = require('./formPrepare'),
15465    dialog = require('../../dialog'),
15466    util = require('../../util');
15467
15468/**
15469 * @function
15470 * @description Initializes gift message box for multiship shipping, the message box starts off as hidden and this will display it if the radio button is checked to yes, also added event handler to listen for when a radio button is pressed to display the message box
15471 */
15472function initMultiGiftMessageBox() {
15473    $.each($('.item-list'), function () {
15474        var $this = $(this);
15475        var $giftMessage = $this.find('.gift-message-text');
15476
15477        //handle initial load
15478        $giftMessage.toggleClass('hidden', $('input[name$="_isGift"]:checked', this).val() !== 'true');
15479
15480        //set event listeners
15481        $this.on('change', function () {
15482            $giftMessage.toggleClass('hidden', $('input[name$="_isGift"]:checked', this).val() !== 'true');
15483        });
15484    });
15485}
15486
15487
15488/**
15489 * @function
15490 * @description capture add edit adddress form events
15491 */
15492function addEditAddress(target) {
15493    var $addressForm = $('form[name$="multishipping_editAddress"]'),
15494        $addressDropdown = $addressForm.find('select[name$=_addressList]'),
15495        $addressList = $addressForm.find('.address-list'),
15496        add = true,
15497        selectedAddressUUID = $(target).parent().siblings('.select-address').val();
15498
15499    $addressDropdown.on('change', function (e) {
15500        e.preventDefault();
15501        var selectedAddress = $addressList.find('select').val();
15502        if (selectedAddress !== 'newAddress') {
15503            selectedAddress = $.grep($addressList.data('addresses'), function (add) {
15504                return add.UUID === selectedAddress;
15505            })[0];
15506            add = false;
15507            // proceed to fill the form with the selected address
15508            util.fillAddressFields(selectedAddress, $addressForm);
15509        } else {
15510            //reset the form if the value of the option is not a UUID
15511            $addressForm.find('.input-text, .input-select').val('');
15512        }
15513    });
15514
15515    $addressForm.on('click', '.cancel', function (e) {
15516        e.preventDefault();
15517        dialog.close();
15518    });
15519
15520    $addressForm.on('submit', function (e) {
15521        e.preventDefault();
15522        $.getJSON(Urls.addEditAddress, $addressForm.serialize(), function (response) {
15523            if (!response.success) {
15524                // @TODO: figure out a way to handle error on the form
15525                return;
15526            }
15527            var address = response.address,
15528                $shippingAddress = $(target).closest('.shippingaddress'),
15529                $select = $shippingAddress.find('.select-address'),
15530                $selected = $select.find('option:selected'),
15531                newOption = '<option value="' + address.UUID + '">' +
15532                    ((address.ID) ? '(' + address.ID + ')' : address.firstName + ' ' + address.lastName) + ', ' +
15533                    address.address1 + ', ' + address.city + ', ' + address.stateCode + ', ' + address.postalCode +
15534                    '</option>';
15535            dialog.close();
15536            if (add) {
15537                $('.shippingaddress select').removeClass('no-option').append(newOption);
15538                $('.no-address').hide();
15539            } else {
15540                $('.shippingaddress select').find('option[value="' + address.UUID + '"]').html(newOption);
15541            }
15542            // if there's no previously selected option, select it
15543            if ($selected.length === 0 || $selected.val() === '') {
15544                $select.find('option[value="' + address.UUID + '"]').prop('selected', 'selected').trigger('change');
15545            }
15546        });
15547    });
15548
15549    //preserve the uuid of the option for the hop up form
15550    if (selectedAddressUUID) {
15551        //update the form with selected address
15552        $addressList.find('option').each(function () {
15553            //check the values of the options
15554            if ($(this).attr('value') === selectedAddressUUID) {
15555                $(this).prop('selected', 'selected');
15556                $addressDropdown.trigger('change');
15557            }
15558        });
15559    }
15560}
15561
15562/**
15563 * @function
15564 * @description shows gift message box in multiship, and if the page is the multi shipping address page it will call initmultishipshipaddress() to initialize the form
15565 */
15566exports.init = function () {
15567    initMultiGiftMessageBox();
15568    if ($('.cart-row .shippingaddress .select-address').length > 0) {
15569        formPrepare.init({
15570            continueSelector: '[name$="addressSelection_save"]',
15571            formSelector: '[id$="multishipping_addressSelection"]'
15572        });
15573    }
15574    $('.edit-address').on('click', 'a', function (e) {
15575        dialog.open({url: this.href, options: {open: function () {
15576            address.init();
15577            addEditAddress(e.target);
15578        }}});
15579    });
15580};
15581
15582},{"../../dialog":36,"../../util":102,"./address":59,"./formPrepare":61}],64:[function(require,module,exports){
15583/*
15584 * Previously called opcaddress_enhanced.js
15585 * Removed the "_enhanced" from the file name to keep things consistent.
15586 * 
15587 * BOOT-6833 - Eliminate old feature flags & unused JS files
15588 */ 
15589
15590'use strict';
15591
15592var _ = require('lodash');
15593var ajax = require('../../ajax');
15594var dialog = require('../../dialog');
15595var findMoreStores = require('../../findMoreStores');
15596var tooltip = require('../../tooltip');
15597var util = require('../../util');
15598var TPromise = require('promise');
15599var AddressAutocompleteDialog;
15600var AddressVerificationDialog;
15601var AddressVerificationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper');
15602var CheckoutConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants');
15603var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
15604var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
15605var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
15606var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
15607var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
15608
15609var shippingMethods;
15610var isStoreShippingAddressOnly;
15611var options = {};
15612var dataBuffer = {};
15613// Matches cart.js's loadingIndicatorSettings exactly - the same fullscreen loader shown for
15614// Save/setOPCAddress, reused here for "Use My Location" so both dialogs stay consistent.
15615var loadingIndicatorSettings = {
15616	IsLabelShown: false,
15617	CssClass: 'fullscreen',
15618	BackgroundColor: 'transparent'
15619};
15620
15621var billingAddressFormContainerSelector = '.billing-address-form';
15622var $billingAddressFormContainer = $(billingAddressFormContainerSelector);
15623var billingAddressFormSelector = '#billingAddressForm';
15624var $billingAddressForm = $(billingAddressFormSelector);
15625var shippingAddressSectionSelector = '.form-checkout.shipping-address-section';
15626var $shippingAddressSection = $(shippingAddressSectionSelector);
15627var shippingAddressFormContainerSelector = '.shipping-address-form';
15628var $shippingAddressFormContainer = $(shippingAddressFormContainerSelector);
15629var shippingAddressFormSelector = '#shippingAddressForm';
15630var $shippingAddressForm = $(shippingAddressFormSelector);
15631var paymentFormContainerSelector = '.payment-section';
15632var $paymentFormContainer = $(paymentFormContainerSelector);
15633var paymentFormSelector = '#paymentSection';
15634var $paymentForm = $(paymentFormSelector);
15635var $sectionCheckout = $('.section-checkout');
15636var $shippingMethodList = $('.form-checkout.payment-section #shipping-method-list');
15637
15638var shippingAddressFormValidator;
15639var billingAddressFormValidator;
15640
15641function initializeEvents() {
15642	$(document).ready(function() {
15643        if (!SitePreferences.PROMOTIONAL_SMS) {
15644            $('#billingAddressForm').find('[field="promotionalsms"]').hide();
15645        } else {
15646			if ($('.promotionalSMSLabel').length) {
15647				var promoSMSLabelText = $('.promotionalSMSLabel').html();
15648				$('#billingAddressForm [field="promotionalsms"] label span').html(promoSMSLabelText);
15649			}
15650		}
15651    });
15652
15653	$sectionCheckout.on('click', '.find-more-stores a', function () {
15654		findMoreStores.initialize({ view: 'checkout', storeQuery: StoreQueryFilters.ShipToStoreEnabled });
15655	});
15656	
15657	$(document).on('click', '.international-checkout', function() {
15658		location.reload();
15659	});
15660	
15661	$(document).on('change', 'form [id*="_country"]', function () {
15662		var $this = $(this);
15663		var $form = $this.closest('form');
15664		var $stateDropdown = $form.find('[id*="states_state"]');
15665
15666		renderOptions('#' + $stateDropdown.attr('id'), '#' + $this.attr('id'));
15667
15668		var $initialState = $form.find('.initial-state');
15669		var initialStateValue = $initialState.val();
15670		var $selectedStateOption = $stateDropdown.find('option[value="' + initialStateValue + '"]');
15671		if ($selectedStateOption.length > 0) {
15672			$stateDropdown.val(initialStateValue);
15673			$selectedStateOption.prop('selected', true);
15674		}
15675
15676		if ($form.attr('id') == $shippingAddressForm.attr('id') && $this.val()) {
15677			if ($this.val().toUpperCase() == SiteConstants.CountryCodes.UnitedStates) {
15678				$('#shipping-international-info').hide();
15679			} else {
15680				$('#shipping-international-info').show();
15681			}
15682		}
15683	});
15684
15685	$(document).on('change', 'form [id*="_postal"]', function () {
15686		var $this = $(this);
15687		$this.val($this.val().toUpperCase());
15688	});
15689
15690	$('.shipping-same').on('click', function(){
15691		if($('.shipping-same').is(':checked')){
15692			$('input[name$="_useAsShippingAddress"]').prop('checked', true);
15693		} else {
15694			$('input[name$="_useAsShippingAddress"]').prop('checked', false);
15695		}
15696	});
15697
15698	$('.add-diff').on('click', function(){
15699		$('input[name$="_useAsShippingAddress"]').prop('checked', false);
15700	});
15701
15702	$billingAddressFormContainer.on('click', '.edit-address-det', function (e) {
15703		e.preventDefault();
15704		editBillingAddress();
15705	});
15706
15707	$billingAddressFormContainer.on('click', '.cancel-address-det', function (e) {
15708		e.preventDefault();
15709
15710		$('.bill-address-fields').addClass('collapsed').hide();
15711		$('.billing-select-address').show();
15712
15713		$('.billing-address-preview').show();
15714		$('.billing-address-data.form-info').show();
15715
15716		$billingAddressForm.hide();
15717		$shippingAddressFormContainer.show();
15718
15719		$shippingAddressSection.addClass('active').find('.shipping-address-cont').removeClass('hidden');
15720
15721		if ($shippingAddressSection.attr('panel-mode') != 'view') {
15722			$shippingAddressForm.show();
15723		} else {
15724			$shippingAddressForm.hide();
15725			$('.form-checkout.payment-section').addClass('active').find('.shipping-payment-cont').removeClass('hidden');
15726		}
15727		
15728		if ($('.shipping-methods-section .shipping-restrictions').length == 0) {
15729			$('.payment-methods-section').show();
15730		}
15731	});
15732
15733	$('.shipping-address-section').on('click', '.edit-address-det', function (e) {
15734		e.preventDefault();
15735		editShippingAddress();
15736	});
15737
15738	$('.shipping-address-section').on('click', '.add-address-det', function (e) {
15739		e.preventDefault();
15740
15741		writeShippingAddressFormToBuffer();
15742
15743		$('.form-checkout.shipping-address-section').attr('panel-mode', 'add');
15744		$shippingAddressFormContainer.data('hidden-editable', true).hide();
15745		$('.shipping-address-data').hide();
15746		$shippingAddressFormContainer.show();
15747
15748		$shippingAddressForm.removeClass('add edit').addClass('add');
15749		$shippingAddressForm.find('input[name="action"]').val('add');
15750
15751		$billingAddressForm.hide();
15752		$('.billing-address-data.form-info').css('display', 'block');
15753		$('.form-checkout.payment-section').removeClass('active').find('.shipping-payment-cont').addClass('hidden');
15754
15755		var $shippingAddressFormFields = $shippingAddressForm.find('.editaddress-shipping');
15756		$('.editaddress-shipping .initial-state').val('');
15757
15758		$('.shipping-select-address').hide();
15759		$('.editaddress-shipping').removeClass('collapsed');
15760		$('#dwfrm_singleshipping_addressList').val('');
15761
15762		$shippingAddressFormFields.find('input:text, input:password, input:file, textarea').val('');
15763		$shippingAddressFormFields.find('option[default]').prop('selected', true);
15764		$shippingAddressFormFields.find('input:checkbox, input:radio').not('[name*="shippingAddress_addToAddressBook"]').removeAttr('checked');
15765
15766		$('#dwfrm_shippingaddress_country').trigger('change');
15767	});
15768
15769	$('.shipping-address-section').on('click', '.cancel-address-det', function (e) {
15770		e.preventDefault();
15771
15772		writeBufferToShippingAddressForm();
15773   		submitShipping(true);
15774	});
15775
15776    $('.single.shipping-address-type-selector input').on('click', function () {
15777		var shippingAddressType = $(this).val();
15778        shippingAddressFormValidator.form();
15779		$('.form-checkout.shipping-address-section').attr('panel-mode', 'blank');
15780		$('.shipping-address-cont span.error').remove();
15781		$('.shipping-address-cont input, .shipping-address-cont select').removeClass('error');
15782		
15783        if (shippingAddressType == SiteConstants.AddressTypes.Customer) {
15784        	$shippingAddressForm.find('.formfield input[type="text"], .formfield select').not('[disabled]').not('#dwfrm_shippingaddress_myStoreID').val('');
15785        	$('.form-checkout.shipping-address-section').attr('shipping-address-type', 'my-address').attr('value', 'false');
15786        	
15787        	if ($('#dwfrm_multipleshipping_addressList option').length > 0 || dataBuffer.shippingAddressFields && dataBuffer.shippingAddressFields['dwfrm_shippingaddress_firstName']) {
15788				writeBufferToShippingAddressForm();
15789        		
15790				if ($shippingAddressForm.valid()) {
15791					$('.shipping-address-data.form-info').html('').append(buildDetailsBlockForForm($shippingAddressForm));
15792					$('.shipping-address-data').show();
15793				
15794					$shippingAddressForm.hide();
15795					$('.payment-section').addClass('active');
15796					$('.shipping-payment-cont').removeClass('hidden');
15797					$('.shipping-address-cont').removeClass('hidden'); 
15798					$shippingAddressForm.data('shipping-filled', true);
15799				}
15800				
15801				if ($('#dwfrm_multipleshipping_addressList option').length > 0) {
15802					$('#dwfrm_multipleshipping_addressList').trigger('change');
15803				}
15804			} else {
15805				$shippingAddressForm.find('.formfield option[default]').prop('selected', true);
15806				$shippingAddressForm.data('shipping-filled', false);
15807				$('.form-checkout.address-section').attr('panel-mode', 'view');
15808
15809				$shippingAddressFormContainer.data('hidden-editable', true).hide();
15810				$('.shipping-address-data.form-info').hide();
15811				$shippingAddressFormContainer.show();
15812				$shippingAddressForm.removeClass('add edit').addClass('edit');
15813				$shippingAddressForm.find('input[name="action"]').val('edit');
15814
15815				$('.editaddress-shipping').removeClass('collapsed');
15816				setShipToStore(false);
15817			}
15818
15819			setShipToStore(isShipToStoreEnabled());
15820			$('.shipping-address-cont').removeClass('hidden');
15821
15822			if (shippingAddressFormValidator.checkForm()) {
15823				setOPCAddress(shippingAddressFormSelector, 'dwfrm_shippingaddress_saveopcaddress').then(function (data) {
15824    				handleShippingRestrictions(data);	
15825					$('.payment-section').addClass('active');
15826					$('.shipping-payment-cont').removeClass('hidden');
15827
15828					updateShippingMethodList($shippingAddressForm, true, null);
15829				});
15830			} else {
15831				updateShippingMethodList($shippingAddressForm, true, null);
15832				$('.payment-section').removeClass('active');
15833				$('.shipping-payment-cont').addClass('hidden');
15834			}
15835
15836			$('.billing-address-data.form-info').css('display', 'block');
15837
15838            if ($('.multiple.select-address option').length > 0) {
15839            	$('.multiple.select-address').show();
15840            } else {
15841            	$('.multiple.select-address').hide();
15842            }
15843
15844        	$('.single.select-address').hide();
15845        } else if (shippingAddressType == SiteConstants.AddressTypes.Store) {
15846			$shippingAddressForm.hide();
15847            $('.editaddress-shipping').addClass('collapsed');
15848            $('.multiple.select-address').hide();
15849            $('.single.select-address').show();
15850			$('#mystores_addressList').trigger('change');
15851        }
15852    });
15853
15854    $('select[name$="_addressList"]', $billingAddressForm).on('change', function () {
15855        var selected = $(this).children(':selected').first();
15856        var selectedAddress = $(selected).data('address');
15857
15858        if (!selectedAddress) { return; }
15859
15860        util.fillAddressFields(selectedAddress, $billingAddressForm);
15861        billingAddressFormValidator.form();
15862    });
15863
15864    $('select[name$="_addressList"]', $shippingAddressForm).on('change', function () {
15865        var selected = $(this).children(':selected').first();
15866        var selectedAddress = $(selected).data('address');
15867
15868        if (!selectedAddress) { return; }
15869
15870        util.fillAddressFields(selectedAddress, $shippingAddressForm);
15871        shippingAddressFormValidator.form();
15872    });
15873
15874    $('#submitBilling').on('click', function(e){
15875    	e.preventDefault();
15876    	submitBilling();
15877    });
15878
15879    $('#submitShipping').on('click', function(e) {
15880    	e.preventDefault();
15881    	submitShipping();
15882    });
15883
15884	$('.toggle.top, .toggle.bottom').on('click', function(e){
15885    	e.preventDefault();
15886    	$('#internationalinfo').slideToggle();
15887	});
15888
15889	if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
15890		$(document).off('keyup.AddressAutocomplete').on('keyup.AddressAutocomplete', billingAddressFormSelector + ' input[type="text"], ' + shippingAddressFormSelector + ' input[type="text"]', function (event) {
15891			var $this = $(this);
15892			var $form = $this.closest('form');
15893			var formValidator = $form.validate();
15894
15895			if (event.keyCode == 8 || event.keyCode == 46) {
15896				$this.parent().find('.address-autocomplete-dialog').remove();
15897			} else if (!formValidator.checkForm() && $this.attr('id').indexOf('_address1') >= 0) {
15898				var addressAutocompleteHandler = function (addressAutocompleteData) {
15899					AddressAutocompleteDialog.initialize(options, addressAutocompleteData);
15900				}
15901			
15902				AddressVerificationHelper.AutocompleteAddress($form, $this, options).then(addressAutocompleteHandler);
15903			} else {
15904				$this.parent().find('.address-autocomplete-dialog').remove();
15905			}
15906		});
15907		
15908		$(document).off('SetAddressFromAddressAutocomplete').on('SetAddressFromAddressAutocomplete', function (event, data) {
15909			AddressVerificationHelper.SetAddressFromAddressVerificationDialog(data.$form, data.Address);
15910		});
15911	}
15912
15913	if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
15914		$(document).off('SetAddressFromAddressVerification').on('SetAddressFromAddressVerification', function (event, addressData) {
15915			if (addressData.AddressType == 'billing') {
15916				AddressVerificationHelper.SetAddressFromAddressVerificationDialog($billingAddressForm, addressData);
15917				submitBilling(true, addressData.SkipUseBillingAsShipping);
15918			} else if (addressData.AddressType == 'shipping') {
15919				AddressVerificationHelper.SetAddressFromAddressVerificationDialog($shippingAddressForm, addressData);
15920				submitShipping(false, true);
15921			}
15922		});
15923	}
15924
15925	$(document).on('opc.SetShippingAddress', function (e, address) {
15926		var templateSettings = {
15927				interpolate: /{{([\s\S]+?)}}/g
15928	    	};
15929
15930		var template = _.template($('#template-address-selector-option').html(), templateSettings);
15931
15932		address.ID = address.key;
15933		if (address.isMyStore && !address.myStoreID && address.storeId) {
15934			address.myStoreID = address.storeId;
15935		}
15936
15937		var templateData = _.extend(address, {
15938			id: address.UUID,
15939			cssClass: 'find-more-store-option',
15940			selected: 'selected',
15941			text: address.labelText
15942		});
15943
15944		var $addressList = $('#mystores_addressList');
15945
15946		var addressIndexInList = null;
15947		$addressList.find('option').each(function (index, object) {
15948			if (address.ID == $(object).val()) {
15949				addressIndexInList = index;
15950				return false;
15951			}
15952		});
15953
15954	    if (addressIndexInList) {
15955			$addressList.prop('selectedIndex', addressIndexInList);
15956		} else {
15957			dataBuffer['mystores_addressList'] = dataBuffer['mystores_addressList'].filter(function (address) {
15958				return !address.isTemporary;
15959			});
15960
15961			address.isTemporary = true;
15962			dataBuffer['mystores_addressList'].push(address);
15963			dataBuffer['store_name_lookup'][address.firstName] = address.fullName;
15964			
15965			$addressList.find('.find-more-store-option').remove();
15966			$addressList.prepend(template(templateData)).prop('selectedIndex', 0);
15967		}
15968
15969	    $('#mystores_addressList').trigger('change');
15970	});
15971
15972	$(document).on('change', '#dwfrm_multipleshipping_addressList, #mystores_addressList', function (e) {
15973		var $this = $(this);
15974		var actionName = 'dwfrm_shippingaddress_saveopcaddress';
15975		$('.shipping-address-cont span.error').remove();
15976		$('.shipping-address-cont input, .shipping-address-cont select').removeClass('error');
15977
15978		var $target = $(e.currentTarget);
15979        var selectedAddress;
15980		if ($target && $target.attr('id')) {
15981			var dataBufferForAddressList = dataBuffer[$target.attr('id')];
15982			if (dataBufferForAddressList) {
15983				selectedAddress = dataBufferForAddressList.filter(function (address) {
15984					return address.UUID == $target.val();
15985				})[0];
15986			}
15987		}
15988
15989        if (!selectedAddress) {
15990        	return false;
15991        }
15992
15993		var isStoreAddress = false;
15994		var storeId;
15995        if (selectedAddress.hasOwnProperty('isMyStore') && selectedAddress.myStoreID) {
15996			isStoreAddress = selectedAddress.isMyStore;
15997			storeId = selectedAddress.myStoreID;
15998        }
15999
16000		setShipToStore(isStoreAddress);
16001
16002		if (isStoreAddress) {
16003			$shippingAddressForm.find('.formfield.myStoreID input').val(storeId);
16004		} else {
16005			$shippingAddressForm.find('.formfield input[type="text"], .formfield select').not('[disabled]').not('#dwfrm_shippingaddress_myStoreID').val('');
16006			util.fillAddressFields(selectedAddress, $shippingAddressForm);
16007		}
16008		$this.find('option.placeholder').remove();
16009
16010		setOPCAddress(shippingAddressFormSelector, actionName).then(function (data) {
16011			handleShippingRestrictions(data);
16012			$shippingAddressForm.data('shipping-filled', true);
16013			$shippingAddressForm.hide();
16014			$('.shipping-address-data.form-info').html('').append(buildDetailsBlockForAddress(selectedAddress, false, false, 'shippingAddressForm'));
16015			$('.shipping-address-data').show();
16016
16017			if (billingAddressFormValidator.valid()) {
16018				$('.shipping-payment-cont').removeClass('hidden');
16019				$('.payment-section').addClass('active');
16020			} else {
16021				$('.shipping-payment-cont').addClass('hidden');
16022				$('.payment-section').removeClass('active');
16023				$('.billing-address-data.form-info').hide();
16024
16025				$billingAddressFormContainer.show();
16026				$billingAddressFormContainer.find('input.required.field.error').eq(0).focus();
16027			}
16028			
16029			updateShippingMethodList($shippingAddressForm, true, isStoreAddress);
16030		}.bind(this));
16031	});
16032
16033	$('#dwfrm_billing_addressList').on('change', function() {
16034    	$('.custom-email-error').remove();
16035    	var isEmailValid = util.validateEmail($('#dwfrm_billingaddress_email').val());
16036
16037		if ($(this).val() && billingAddressFormValidator.valid() && isEmailValid) {
16038			$billingAddressForm.hide();
16039			$('.billing-address-data').html('').append(buildDetailsBlockForForm($billingAddressForm)).show();
16040			$('.shipping-address-cont').removeClass('hidden');
16041			$('.shipping-address-section').addClass('active');
16042			$('.form-checkout.address-section').attr('panel-mode', 'view').attr('value', 'true');
16043		} else {
16044			editBillingAddress();
16045			$('.shipping-address-cont').addClass('hidden');
16046			$('.shipping-address-section').removeClass('active');
16047		}
16048	});
16049
16050	function renderZipSearchResults($storeSelector, stores) {
16051		var $results = $storeSelector.find('.zip-search-results');
16052		$results.empty();
16053
16054		stores.forEach(function (store) {
16055			var $option = $('<label class="store-option"></label>');
16056			var $radio = $('<input type="radio" name="ZipSearchStoreId" />').val(store.StoreId);
16057			var $radioCircle = $('<span class="radio-circle"></span>');
16058			var $details = $('<span class="store-details-text"></span>');
16059
16060			$details.append($('<span class="store-name"></span>').text(store.Name));
16061			$details.append($('<span class="store-address"></span>').text(
16062				store.Address1 + ', ' + store.City + ', ' + store.StateCode + ' ' + store.PostalCode
16063			));
16064
16065			$option.append($details).append($radio).append($radioCircle);
16066			$results.append($option);
16067		});
16068
16069		$results.off('click.ShipToStore.PickResult').on('click.ShipToStore.PickResult', '.store-option', function () {
16070			$results.find('.store-option').removeClass('selected');
16071			var $checkedRadio = $(this).addClass('selected').find('input[name="ZipSearchStoreId"]').prop('checked', true);
16072			$storeSelector.data('pendingStoreId', $checkedRadio.val());
16073		});
16074
16075		// The results list just grew the dialog's content height - re-center it vertically now
16076		// that it's taller, same trick repSelectionsModal.js uses (reassert the current position
16077		// option to force jQuery UI to recompute it).
16078		if ($storeSelector.dialog('instance')) {
16079			$storeSelector.dialog('option', 'position', $storeSelector.dialog('option', 'position'));
16080		}
16081	}
16082
16083	function openShipToStoreDialog($shippingMethod) {
16084		// Cached on $shippingMethod (never relocated) rather than re-found via .find() every time -
16085		// once .store-selector has been dialog-ified once, it no longer lives inside
16086		// [shipping-method] until destroyed, so a plain .find() here would come up empty on any
16087		// open after the first.
16088		var $storeSelector = $shippingMethod.data('storeSelectorDialog') || $shippingMethod.find('.store-selector');
16089		$shippingMethod.data('storeSelectorDialog', $storeSelector);
16090
16091		// Destroy any stale instance from a previous open here, rather than inside that instance's
16092		// own close callback (kept that way for safety, though the actual "second open does
16093		// nothing" bug turned out to be in dialog.js's close() calling methods on a stale singleton
16094		// reference - see dialog.js).
16095		if ($storeSelector.dialog('instance')) {
16096			$storeSelector.dialog('destroy');
16097			$storeSelector.hide();
16098		}
16099
16100		// Snapshot the currently-committed store so Cancel can revert to it.
16101		$storeSelector.data('originalSelectValue', $storeSelector.find('select').val());
16102
16103		// Deliberately not calling the shared dialog.close() here - this function already destroys
16104		// its own prior instance of THIS element above; the shared singleton's close() would instead
16105		// dismiss whatever unrelated dialog (cookie consent, quickview, etc.) happens to be open
16106		// elsewhere on the page at the moment this one opens.
16107		dialog.create({
16108			target: $storeSelector,
16109			options: {
16110				title: 'Change Ship-to-Store',
16111				dialogClass: 'ship-to-store-dialog',
16112				width: 480,
16113				open: function () {
16114					$('.ui-widget-overlay').addClass('rep-selections-overlay');
16115				},
16116				close: function () {
16117					$('.ui-widget-overlay').removeClass('rep-selections-overlay');
16118					// destroy() deliberately NOT called here - see the guard at the top of this
16119					// function instead, which runs it safely on the NEXT open (a fresh call stack,
16120					// not nested inside this close event).
16121				}
16122			}
16123		});
16124		$storeSelector.dialog('open');
16125	}
16126
16127	$(document).off('click.ShipToStore.Change').on('click.ShipToStore.Change', '[shipping-method] .store-details .change', function (e) {
16128		openShipToStoreDialog($(this).closest('[shipping-method]'));
16129	});
16130
16131	// Scoped to .store-selector itself, NOT [shipping-method] - once the dialog opens, jQuery UI
16132	// relocates .store-selector out from under [shipping-method] (appends it near <body>), which
16133	// breaks any selector requiring that outer ancestor. .store-selector still travels with its
16134	// own descendants regardless of where it currently sits, so scoping to it directly is safe.
16135	$(document).off('change.ShipToStore.SelectStore').on('change.ShipToStore.SelectStore', '.store-selector select', function (e) {
16136		var $storeSelector = $(this).closest('.store-selector');
16137		$storeSelector.find('.zip-search-results .store-option').removeClass('selected');
16138		$storeSelector.find('.zip-search-results input[name="ZipSearchStoreId"]').prop('checked', false);
16139		$storeSelector.data('pendingStoreId', $(this).val());
16140	});
16141
16142	// Mirrors repSelectionsModal.js's showZipError/clearZipError exactly - error text comes from the
16143	// error span's own data-error-message (set in the template), not a hardcoded JS string, and the
16144	// error state clears live as the rep types, not just on the next submit attempt.
16145	function showZipError($zip, $error) {
16146		$zip.addClass('error');
16147		$error.text($error.data('errorMessage')).addClass('visible');
16148	}
16149
16150	function clearZipError($zip, $error) {
16151		$zip.removeClass('error');
16152		$error.removeClass('visible');
16153	}
16154
16155	$(document).off('input.ShipToStore.ZipSearch').on('input.ShipToStore.ZipSearch', '.store-selector .zip-search-input', function (e) {
16156		var $zipSearchForm = $(this).closest('.zip-search-form');
16157		clearZipError($(this), $zipSearchForm.find('.error'));
16158	});
16159
16160	// .zip-search-form is a <div>, not a <form> - this content sits inside checkout's own outer
16161	// form, and a nested <form> start tag is silently dropped by the browser's HTML parser (nested
16162	// forms are invalid HTML), leaving a submit button with nothing to submit. So this triggers on
16163	// the Search button's click and the zip field's Enter key directly, rather than a form submit.
16164	function runZipSearch($zipSearchForm) {
16165		var $storeSelector = $zipSearchForm.closest('.store-selector');
16166		var $zip = $zipSearchForm.find('.zip-search-input');
16167		var $error = $zipSearchForm.find('.error');
16168		var $searchButtonContainer = $zipSearchForm.find('.btn-store-search-container');
16169
16170		clearZipError($zip, $error);
16171
16172		if ($.trim($zip.val()).length !== 5) {
16173			showZipError($zip, $error);
16174			return;
16175		}
16176
16177		$searchButtonContainer.loading();
16178
16179		$.ajax({
16180			url: Urls.searchStoresByZip,
16181			type: 'GET',
16182			dataType: 'json',
16183			data: { Zip: $.trim($zip.val()) }
16184		}).done(function (result) {
16185			if (result && result.Success && result.Stores && result.Stores.length > 0) {
16186				clearZipError($zip, $error);
16187				renderZipSearchResults($storeSelector, result.Stores);
16188			} else {
16189				$storeSelector.find('.zip-search-results').empty();
16190				showZipError($zip, $error);
16191			}
16192		}).fail(function () {
16193			$storeSelector.find('.zip-search-results').empty();
16194			showZipError($zip, $error);
16195		}).always(function () {
16196			if ($searchButtonContainer.attr('loading')) {
16197				$searchButtonContainer.loading('destroy');
16198			}
16199		});
16200	}
16201
16202	$(document).off('click.ShipToStore.ZipSearch').on('click.ShipToStore.ZipSearch', '.store-selector .zip-search-form .store-search', function (e) {
16203		e.preventDefault();
16204		runZipSearch($(this).closest('.zip-search-form'));
16205	});
16206
16207	$(document).off('keydown.ShipToStore.ZipSearch').on('keydown.ShipToStore.ZipSearch', '.store-selector .zip-search-input', function (e) {
16208		if (e.key === 'Enter' || e.keyCode === 13) {
16209			e.preventDefault();
16210			runZipSearch($(this).closest('.zip-search-form'));
16211		}
16212	});
16213
16214	$(document).off('click.ShipToStore.Geolocation').on('click.ShipToStore.Geolocation', '.store-selector .geolocation', function (e) {
16215		var $geolocationLink = $(this);
16216		var $storeSelector = $geolocationLink.closest('.store-selector');
16217		var $zip = $storeSelector.find('.zip-search-input');
16218		var $error = $storeSelector.find('.zip-search-form .error');
16219
16220		if (!navigator.geolocation) {
16221			return;
16222		}
16223
16224		// Same fullscreen loader Save/setOPCAddress() uses, for consistency - no bespoke
16225		// modal-scoped spinner treatment.
16226		$('body').loading(loadingIndicatorSettings);
16227
16228		navigator.geolocation.getCurrentPosition(function (position) {
16229			$.ajax({
16230				url: Urls.searchStoresByZip,
16231				type: 'GET',
16232				dataType: 'json',
16233				data: { Latitude: position.coords.latitude, Longitude: position.coords.longitude }
16234			}).done(function (result) {
16235				if (result && result.Success && result.Stores && result.Stores.length > 0) {
16236					clearZipError($zip, $error);
16237					renderZipSearchResults($storeSelector, result.Stores);
16238				} else {
16239					$storeSelector.find('.zip-search-results').empty();
16240					showZipError($zip, $error);
16241				}
16242			}).fail(function () {
16243				$storeSelector.find('.zip-search-results').empty();
16244				showZipError($zip, $error);
16245			}).always(function () {
16246				if ($('body').attr('loading')) {
16247					$('body').loading('destroy');
16248				}
16249			});
16250		}, function () {
16251			showZipError($zip, $error);
16252			if ($('body').attr('loading')) {
16253				$('body').loading('destroy');
16254			}
16255		});
16256	});
16257
16258	$(document).off('click.ShipToStore.Apply').on('click.ShipToStore.Apply', '.store-selector .apply', function (e) {
16259		var $storeSelector = $(this).closest('.store-selector');
16260		var storeId = $storeSelector.data('pendingStoreId') || $storeSelector.find('select').val();
16261
16262		$storeSelector.dialog('close');
16263		if ($storeSelector.dialog('instance')) {
16264			$storeSelector.dialog('destroy');
16265		}
16266		$storeSelector.hide();
16267
16268		if (!storeId) {
16269			return;
16270		}
16271
16272		$shippingAddressForm.find('.formfield.myStoreID input').val(storeId);
16273
16274		setOPCAddress(shippingAddressFormSelector, 'dwfrm_shippingaddress_saveopcaddress').then(function (data) {
16275			updateShippingMethodList($shippingAddressForm, true);
16276		});
16277	});
16278
16279	$(document).off('click.ShipToStore.Cancel').on('click.ShipToStore.Cancel', '.store-selector .cancel', function (e) {
16280		var $storeSelector = $(this).closest('.store-selector');
16281
16282		// Revert anything touched during this open - the select and any zip-search pick - so
16283		// nothing from a canceled interaction lingers into a later Apply.
16284		var originalValue = $storeSelector.data('originalSelectValue');
16285		if (originalValue !== undefined) {
16286			$storeSelector.find('select').val(originalValue);
16287		}
16288		$storeSelector.removeData('pendingStoreId');
16289		$storeSelector.find('.zip-search-results').empty();
16290		$storeSelector.find('.zip-search-input').val('');
16291		clearZipError($storeSelector.find('.zip-search-input'), $storeSelector.find('.zip-search-form .error'));
16292
16293		$storeSelector.dialog('close');
16294		if ($storeSelector.dialog('instance')) {
16295			$storeSelector.dialog('destroy');
16296		}
16297		$storeSelector.hide();
16298	});
16299
16300	$(document).off('click.ShippingMethod.Select').on('click.ShippingMethod.Select', 'input[name*="_shippingMethodID"]', function (event) {
16301		var $this = $(this);
16302		var $shippingMethod = $this.closest('[shipping-method]');
16303		var $shipment = $this.closest('form');
16304
16305		if ($shippingMethod.attr('status') == 'disabled') {
16306			event.preventDefault();
16307		} else {
16308			selectShippingMethod($shipment.attr('shipment'), $this.val(), $shippingMethod.attr('is-ship-to-store') == 'true', $shippingMethod); 
16309		}
16310	});
16311
16312	$(document).off('focus.AutofillHandler').on('focus.AutofillHandler', 'div.billing-address-form input[type="text"], div.shipping-address-form input[type="text"]', function (event) {
16313		var $this = $(this);
16314		var $form = $this.closest('form');
16315		var autofillCount = $form.find('input:-webkit-autofill').length;
16316
16317		if (window.innerWidth <= 768) {
16318			if (autofillCount == 0) {
16319				$form.attr('auto-filled', false);
16320			}
16321
16322			if ($form.attr('auto-filled') != 'true') {
16323				dataBuffer.AutofillDetectorInterval = setInterval(function() {
16324					autofillCount = $form.find('input:-webkit-autofill').length;
16325					if (autofillCount > 0 && $form.attr('auto-filled') != 'true') {
16326						$form.attr('auto-filled', true);
16327						$this.attr('inputmode', 'none');
16328						$this.trigger('blur');
16329						setTimeout(function() {
16330							$this.removeAttr('inputmode');
16331						}, 250);
16332					}
16333				}, 250);
16334			}
16335		}
16336	});
16337}
16338
16339$(document).off('blur.AutofillHandler').on('blur.AutofillHandler', 'div.billing-address-form input[type="text"], div.shipping-address-form input[type="text"]', function (event) {
16340	if (dataBuffer && dataBuffer.AutofillDetectorInterval) {
16341		clearInterval(dataBuffer.AutofillDetectorInterval);
16342	}
16343});
16344
16345function initializeUserInterface() {
16346	$('#dwfrm_shippingaddress_ID').val('');
16347	$('.form-checkout.address-section').attr('panel-mode', 'blank');
16348	$('.form-checkout.shipping-address-section').attr('panel-mode', 'blank').attr('value', 'false');
16349
16350	var billingAddress = getAddressFromForm($billingAddressForm);
16351	var shippingAddress = getAddressFromForm($shippingAddressForm);
16352	var $billingSavedAddrDropdown = $('#dwfrm_billing_addressList');
16353	var didBillingAddressSuccessHandlerRun = false;
16354	var didShippingAddressSuccessHandlerRun = false;
16355	
16356	$('#dwfrm_billingaddress_country').trigger('change');
16357	$('#dwfrm_shippingaddress_country').trigger('change');
16358
16359	$.validator.addMethod('requiredExcludingPunctuation', ValidationHelper.ValidateRequiredExcludingPunctuation, Resources.INVALID_VALUE);
16360	$.validator.addMethod('postalCode', ValidationHelper.ValidatePostalCode, Resources.INVALID_POSTAL_CODE);
16361	$.validator.addMethod('postOfficeBoxOnly', ValidationHelper.ValidateAddressForPostOfficeBoxOnly, Resources.INVALID_POST_OFFICE_BOX_ONLY);
16362	
16363	billingAddressFormValidator = $billingAddressForm.validate();
16364	billingAddressFormValidator.settings.ignore = [":not(.required)"];
16365	billingAddressFormValidator.settings.onfocusout = ValidationSettings.Checkout.onfocusout;
16366	billingAddressFormValidator.settings.errorPlacement = ValidationSettings.Checkout.errorPlacement;
16367	$billingAddressForm.find('input[name$="_firstName"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16368	$billingAddressForm.find('input[name$="_lastName"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16369	$billingAddressForm.find('input[name$="_address1"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16370	$billingAddressForm.find('input[name$="_address1"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBoxOnly);
16371	$billingAddressForm.find('input[name$="_address2"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBoxOnly);
16372	$billingAddressForm.find('input[name$="_city"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16373	$billingAddressForm.find('input[name$="_postal"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16374
16375	shippingAddressFormValidator = $shippingAddressForm.validate();
16376	shippingAddressFormValidator.settings.ignore = [":not(.required)"];
16377	shippingAddressFormValidator.settings.onfocusout = ValidationSettings.Checkout.onfocusout;
16378	shippingAddressFormValidator.settings.errorPlacement = ValidationSettings.Checkout.errorPlacement;
16379	$shippingAddressForm.find('input[name$="_firstName"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16380	$shippingAddressForm.find('input[name$="_lastName"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16381	$shippingAddressForm.find('input[name$="_address1"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16382	$shippingAddressForm.find('input[name$="_address1"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBoxOnly);
16383	$shippingAddressForm.find('input[name$="_address2"]').rules('add', ValidationRules.ValidateAddressForPostOfficeBoxOnly);
16384	$shippingAddressForm.find('input[name$="_city"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16385	$shippingAddressForm.find('input[name$="_postal"]').rules('add', ValidationRules.RequiredExcludingPunctuation);
16386
16387	if (!billingAddress.address1 && $billingSavedAddrDropdown.find('option').length && $billingSavedAddrDropdown.val()) {
16388		$billingSavedAddrDropdown.trigger('change');
16389	} else {
16390		if (billingAddressFormValidator.checkForm()) {
16391			$billingAddressForm.find('input.shipping-same').prop('checked', false);
16392			$billingAddressForm.find('input.shipping-newsletter-signup').prop('checked', false);
16393			didBillingAddressSuccessHandlerRun = billingAddressSuccessHandler(null, true);
16394		} else {
16395			editBillingAddress();
16396		}
16397	}
16398
16399	renderMyStoresAddressSelector();
16400	var $shippingSavedAddrDropdown = $('#dwfrm_multipleshipping_addressList');
16401	if (billingAddressFormValidator.checkForm() && !didBillingAddressSuccessHandlerRun) {
16402		if (!shippingAddress.address1 && $shippingSavedAddrDropdown.find('option').length && $shippingSavedAddrDropdown.val()) {
16403			$shippingSavedAddrDropdown.trigger('change');
16404		} else {
16405			if (shippingAddressFormValidator.checkForm()) {
16406				didShippingAddressSuccessHandlerRun = shippingAddressSuccessHandler(true);
16407				verifyShipping();
16408			} else {
16409				editShippingAddress();
16410			}
16411		}
16412	}
16413	
16414	$('.form-checkout.shipping-address-section .select-address.multiple').show();
16415	$('.address-section').addClass('active');
16416	
16417	if (didBillingAddressSuccessHandlerRun && shippingAddressFormValidator.checkForm() && window.innerWidth <= 767) {
16418		setTimeout(function() {
16419			$([document.documentElement, document.body]).scrollTop($('.form-checkout.payment-section').offset().top - $('.header-wrapper').height() + 20);
16420		}, 1000);
16421	}
16422}
16423
16424function billingAddressSuccessHandler(responseData, skipUpdateShippingMethodList, isVerified, skipUseBillingAsShipping) {
16425	var $shippingSavedAddrDropdown = $('#dwfrm_multipleshipping_addressList');
16426	var shippingAddress = getAddressFromForm($shippingAddressForm);
16427
16428	$billingAddressForm.hide();
16429	$('.form-checkout.address-section').attr('panel-mode', 'view').attr('value', 'true');
16430	$('.billing-address-data').html('').append(buildDetailsBlockForForm($billingAddressForm)).show();
16431	$('.shipping-address-cont').removeClass('hidden');
16432
16433	if (responseData && responseData.status == 'success' && !skipUseBillingAsShipping && ($('#isShippingAddressSameAsBillingAddress').prop('checked') || $('[has-store-pickup-shipment-only]').attr('has-store-pickup-shipment-only') == 'true')) {
16434		populateBillingAddressFormToShippingAddressForm();
16435	}
16436	
16437	if (responseData && responseData.status == 'shippingAddressValidationError') {
16438		editShippingAddress();
16439	} else if (!skipUseBillingAsShipping && ($('#isShippingAddressSameAsBillingAddress').prop('checked') || $('[has-store-pickup-shipment-only]').attr('has-store-pickup-shipment-only') == 'true')) {
16440		$('.shipping-address-data.form-info').html('').append(buildDetailsBlockForForm($shippingAddressForm));
16441		$('.shipping-address-data').show();
16442
16443		$shippingAddressForm.hide();
16444		$shippingAddressForm.data('shipping-filled', true);
16445		$paymentFormContainer.addClass('active');
16446		$('.shipping-payment-cont').removeClass('hidden');
16447		submitShipping(null, isVerified);
16448	} else if (shippingAddressFormValidator.checkForm()) {
16449		shippingAddressSuccessHandler(skipUpdateShippingMethodList);
16450	} else if (!shippingAddress.address1 && $shippingSavedAddrDropdown.find('option').length && $shippingSavedAddrDropdown.val()) {
16451		$shippingSavedAddrDropdown.trigger('change');
16452	} else {
16453		editShippingAddress();
16454	}
16455
16456	$('.shipping-address-section').addClass('active').show();
16457
16458	if (!skipUpdateShippingMethodList) {
16459		updateShippingMethodList($shippingAddressForm, true);
16460	}
16461
16462	if ($shippingAddressForm.data('shipping-filled') == true) {
16463		$paymentFormContainer.addClass('active').find('.shipping-payment-cont').removeClass('hidden');
16464		$shippingAddressForm.hide();
16465		$('.shipping-address-data').show();
16466	}
16467
16468	if (window.innerWidth <= 767) {
16469		$(window).scrollTop($('.shipping-address-section').offset().top - $('.header-wrapper').height());
16470	}
16471	
16472	return true;
16473}
16474
16475function shippingAddressSuccessHandler(skipUpdateShippingMethodList, isCancel, isVerified) {
16476	var selectedAddressID;
16477	var currentAddressIDs = [];
16478	var $shippingSelect = $('#dwfrm_multipleshipping_addressList');
16479	var $blankOption = $('<option>', { 'class': 'placeholder', 'value': '', 'text': Resources.OPC_SELECT_ADDR });
16480
16481	$('.select-address.address-selector select option').map(function() { currentAddressIDs.push($(this).val()); });
16482
16483	if ($shippingAddressForm.hasClass('add') || ($shippingAddressForm.hasClass('edit') && !$shippingAddressForm.find('.set-default-shipping input').prop('checked'))) {
16484		selectedAddressID = "new";
16485		$shippingAddressForm.find('input[name$="_ID"]').val('');
16486
16487		if ($shippingSelect.find('option.placeholder').length == 0) {
16488			$shippingSelect.prepend($blankOption).prop('selectedIndex', 0);
16489		}
16490	} else {
16491		selectedAddressID = $('.select-address.address-selector select').val();
16492	}
16493
16494	writeShippingAddressFormToBuffer();
16495	var panelMode = $shippingAddressSection.attr('panel-mode');
16496	$shippingAddressForm.removeClass('add edit');
16497	$('.form-checkout.shipping-address-section').attr('panel-mode', 'view');
16498	
16499	var renderAddressSelectorCallback = function (data) {
16500		$shippingAddressForm.data('shipping-filled', true);
16501		$shippingAddressForm.hide();
16502		$shippingAddressForm.find('input[name="action"]').val('');
16503
16504		if (!isCancel && (panelMode == 'add' || panelMode == 'edit')) {
16505			if ($shippingSelect.find('option.placeholder').length == 0) {
16506				$shippingSelect.prepend($blankOption).prop('selectedIndex', 0);
16507			}
16508			if (data && data.selectedAddressID) {
16509				$shippingSelect.val(data.selectedAddressID).val(data.selectedAddressID).prop('selectedIndex', $shippingSelect.find('option:selected').index());
16510			} else if (data && !data.selectedAddressID) {
16511				$shippingSelect.prop('selectedIndex', 0);
16512			}
16513		}
16514
16515		$('.shipping-address-data.form-info').html('').append(buildDetailsBlockForForm($shippingAddressForm));
16516		$('.shipping-address-data').show();
16517		if (!isVerified) {
16518			verifyShipping(isCancel, isVerified);
16519		}
16520
16521		if (billingAddressFormValidator.checkForm()) {
16522			$('.shipping-payment-cont').removeClass('hidden');
16523    		$('.payment-section').addClass('active');
16524		} else {
16525			$('.shipping-payment-cont').addClass('hidden');
16526			$('.payment-section').removeClass('active');
16527			$('.billing-address-data.form-info').hide();
16528
16529			var formBody = $('.billing-address-form');
16530			formBody.show();
16531			formBody.find('input.required.field.error').eq(0).focus();
16532		}
16533
16534		if (!skipUpdateShippingMethodList) {
16535			updateShippingMethodList($shippingAddressForm, true, null, true);
16536		}
16537	}
16538
16539	var isAuthenticated = $sectionCheckout.attr('is-authenticated') == "true";
16540	if (isAuthenticated) {
16541		renderAddressSelector({
16542			container: '.select-address.address-selector',
16543			el: '.select-address.address-selector .field-wrapper',
16544			id: 'dwfrm_multipleshipping_addressList',
16545			selected: selectedAddressID,
16546			currentAddresses: currentAddressIDs,
16547			callback: renderAddressSelectorCallback
16548		});
16549	} else {
16550		renderAddressSelectorCallback();
16551	}
16552
16553	if (window.innerWidth <= 767) {
16554		$(window).scrollTop($('.payment-section').offset().top - $('.header-wrapper').height());
16555	}
16556	
16557	return true;
16558}
16559
16560function submitBilling(isVerified, skipUseBillingAsShipping) {
16561	$('#submitBilling').attr('disabled', true);
16562	if ($('.billing-address-type-selector input[value=1]').prop('checked') && $('#dwfrm_billing_addressList').val() == '') {
16563		$('#submitBilling').attr('disabled', false);
16564		return;
16565	}
16566
16567	if ($('#isShippingAddressSameAsBillingAddress').is(':checked') || $('[has-store-pickup-shipment-only]').attr('has-store-pickup-shipment-only') == 'true') {
16568		$('input[name$="_useAsShippingAddress"]').prop('checked', true);
16569	} else {
16570		$('input[name$="_useAsShippingAddress"]').prop('checked', false);
16571	}
16572
16573	billingAddressFormValidator.form();
16574	$('.custom-email-error').remove();
16575
16576	var isEmailValid = util.validateEmail($('#dwfrm_billingaddress_email').val());
16577	if (billingAddressFormValidator.valid() && isEmailValid) {
16578		$billingAddressForm = $(billingAddressFormSelector);
16579		$billingAddressFormContainer.find('.validation-error-container').removeAttr('role').hide();
16580		verifyBilling(null, isVerified, skipUseBillingAsShipping);
16581	} else {
16582		$billingAddressFormContainer.find('.validation-error-container').attr('role','alert').show();
16583		$('#submitBilling').attr('disabled', false);
16584		if (!isEmailValid && billingAddressFormValidator.valid()) {
16585			$('#dwfrm_billingaddress_email').addClass('error').parent().append('<span class="error custom-email-error">' + Resources.VALIDATE_EMAIL + '</span>');
16586		}
16587		$('.billing-address-type-selector input[value="2"]').trigger('click', [true]);
16588	}
16589}
16590
16591function verifyBilling(isCancel, isVerified, skipUseBillingAsShipping) {
16592	var addressDeliverable = function () {
16593		setOPCAddress(billingAddressFormSelector, $('#selectOpcAddress').attr('name'), skipUseBillingAsShipping).then(function (data) {
16594			billingAddressSuccessHandler(data, null, true, skipUseBillingAsShipping);
16595			$('#submitBilling').attr('disabled', false);
16596		});
16597	}
16598
16599	var addressNotDeliverable = function (addressVerifyData) {
16600		AddressVerificationDialog.initialize(options, 'billing', addressVerifyData);
16601		$('#submitBilling').attr('disabled', false);
16602	}
16603
16604	// Transactional SMS
16605	var populateSMSphone = function () {
16606		if (SitePreferences.TRANSACTIONAL_SMS) {
16607			var billingPhone = $billingAddressForm.find('[id*="_phone"]').val();
16608			$('.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]').val(billingPhone).trigger('input');
16609		}
16610	}
16611
16612	var country = $billingAddressForm.find('[id*="_country"]').val();
16613	if (!isVerified && options && options.Lob && options.Lob.AddressVerification.Enabled && country.toUpperCase() == 'US') {
16614		AddressVerificationHelper.VerifyAddress($billingAddressForm, options).then(addressDeliverable, addressNotDeliverable);
16615	} else {
16616		addressDeliverable();
16617	}
16618	populateSMSphone();
16619}
16620
16621function submitShipping(isCancel, isVerified) {
16622	$('#submitShipping').attr('disabled', true);
16623	$('.shipping-address-cont span.error').remove();
16624	$('.shipping-address-cont input, .shipping-address-cont select').removeClass('error');
16625
16626	shippingAddressFormValidator.form();
16627	if ($shippingAddressForm.valid()) {
16628		$shippingAddressFormContainer.find('.validation-error-container').removeAttr('role').hide();
16629		verifyShipping(isCancel, isVerified);
16630	} else {
16631		$shippingAddressFormContainer.find('.validation-error-container').attr('role','alert').show();
16632		$('#submitShipping').attr('disabled', false);
16633	}
16634}
16635
16636function verifyShipping(isCancel, isVerified) {
16637	var addressDeliverable = function () {
16638		setOPCAddress(shippingAddressFormSelector, $('#submitShipping').attr('name')).then(function (data) {
16639			var isShippingRestricted = handleShippingRestrictions(data);
16640			$('#submitShipping').attr('disabled', false);
16641			if (!isShippingRestricted) {
16642				shippingAddressSuccessHandler(false, isCancel, true);
16643				$('.payment-methods-section').show();
16644			}
16645		});
16646	}
16647
16648	var addressNotDeliverable = function (addressVerifyData) {
16649		AddressVerificationDialog.initialize(options, 'shipping', addressVerifyData);
16650		$('#submitShipping').attr('disabled', false);
16651	}
16652
16653	var country = $shippingAddressForm.find('[id*="_country"]').val();
16654	if (!isVerified && options && options.Lob && options.Lob.AddressVerification.Enabled && country.toUpperCase() == 'US') {
16655		AddressVerificationHelper.VerifyAddress($shippingAddressForm, options).then(addressDeliverable, addressNotDeliverable);
16656	} else {
16657		addressDeliverable();
16658	}
16659}
16660
16661function writeBufferToAddressForm(name, $selector) {
16662	if (dataBuffer[name]) {
16663		$selector.each(function (index, element) {
16664			var $field = $(element);
16665			$field.val(dataBuffer[name][$field.attr('name')] || '');
16666		});
16667		
16668		if (name == 'shippingAddressFields') {
16669			var $form = $('.shipping-address-form');
16670			var $countryDropdown = $form.find('[id*="_country"]');
16671			var $stateDropdown = $form.find('[id*="states_state"]');
16672			renderOptions('#' + $stateDropdown.attr('id'), '#' + $countryDropdown.attr('id'));
16673			
16674			if ($('#dwfrm_shippingaddress_states_state').length > 0 && dataBuffer['shippingAddressFields']['dwfrm_shippingaddress_states_state']) {
16675				$('#dwfrm_shippingaddress_states_state').val(dataBuffer['shippingAddressFields']['dwfrm_shippingaddress_states_state']);
16676			}
16677		}
16678	}
16679}
16680
16681function writeBufferToShippingAddressForm() {
16682	var $shippingAddressFields = $shippingAddressForm.find('.formfield input[type="text"], .formfield select').not('[disabled]').not('#dwfrm_shippingaddress_myStoreID');
16683	writeBufferToAddressForm('shippingAddressFields', $shippingAddressFields);
16684}
16685
16686function writeAddressFormToBuffer(name, $selector, doClear) {
16687	$selector.each(function (index, element) {
16688		var $field = $(element);
16689
16690		if ($field.val()) {
16691			if (!dataBuffer[name]) {
16692				dataBuffer[name] = {};
16693			}
16694
16695			dataBuffer[name][$field.attr('name')] = $field.val();
16696		}
16697	});
16698
16699	if (doClear) {
16700		$selector.val('');
16701		$selector.find('option[default]').prop('selected', true);
16702	}
16703}
16704
16705function writeShippingAddressFormToBuffer(doClear) {
16706	var $shippingAddressFields = $shippingAddressForm.find('.formfield input[type="text"], .formfield select').not('[disabled]').not('#dwfrm_shippingaddress_myStoreID');
16707	writeAddressFormToBuffer('shippingAddressFields', $shippingAddressFields, doClear);
16708}
16709
16710/**
16711 * create address object from form
16712 * @param $form
16713 * @returns Address Object
16714 */
16715function getAddressFromForm($form) {
16716	return {
16717		ID: util.htmlEncode($form.find('input[name$="_ID"]').val()),
16718		isStore: util.htmlEncode($form.find('input[name$="_isStore"]').prop('checked')),
16719		firstName: util.htmlEncode($form.find('input[name$="_firstName"]').val()),
16720		lastName: util.htmlEncode($form.find('input[name$="_lastName"]').val()),
16721		address1: util.htmlEncode($form.find('input[name$="_address1"]').val()),
16722		address2: util.htmlEncode($form.find('input[name$="_address2"]').val()),
16723		address3: util.htmlEncode($form.find('input[name$="_address3"]').val()),
16724		countryCode: util.htmlEncode($form.find('select[id$="_country"]').val()),
16725		stateCode: util.htmlEncode($form.find('select[id$="_state"]').val()),
16726		postalCode: $form.find('input[name$="_postal"]').val() ? util.htmlEncode($form.find('input[name$="_postal"]').val().toUpperCase()) : '',
16727		city: util.htmlEncode($form.find('input[name$="_city"]').val()),
16728		phone: util.htmlEncode($form.find('input[name$="_phone"]').val()),
16729		email: util.htmlEncode($form.find('input[name$="_email"]').val())
16730	};
16731}
16732
16733function populateBillingAddressFormToShippingAddressForm() {
16734	var billingAddress = getAddressFromForm($billingAddressForm);
16735
16736	$shippingAddressForm.find('input[name$="_ID"]').val('');
16737	$shippingAddressForm.find('input[name$="_firstName"]').val(billingAddress.firstName).trigger('change');
16738	$shippingAddressForm.find('input[name$="_lastName"]').val(billingAddress.lastName).trigger('change');
16739	$shippingAddressForm.find('input[name$="_address1"]').val(billingAddress.address1).trigger('change');
16740	$shippingAddressForm.find('input[name$="_address2"]').val(billingAddress.address2).trigger('change');
16741	$shippingAddressForm.find('input[name$="_address3"]').val(billingAddress.address3).trigger('change');
16742	$shippingAddressForm.find('input[name$="_postal"]').val(billingAddress.postalCode).trigger('change');
16743	$shippingAddressForm.find('input[name$="_city"]').val(billingAddress.city).trigger('change');
16744	$shippingAddressForm.find('select[id$=country]').val(billingAddress.countryCode).trigger('change');
16745	$shippingAddressForm.find('select[id$=state]').val(billingAddress.stateCode).trigger('change');
16746	$shippingAddressForm.find('input[name$="_phone"]').val(billingAddress.phone).trigger('change');
16747
16748    if ($('#dwfrm_multipleshipping_addressList').find('option.placeholder').length == 0) {
16749    	$('#dwfrm_multipleshipping_addressList').prepend('<option class="placeholder">' + Resources.OPC_SELECT_ADDR + '</option>').prop('selectedIndex', 0);
16750    }
16751}
16752
16753function buildDetailsBlockForForm($form, isCompact, hideNickname) {
16754	var address = getAddressFromForm($form);
16755	for (var addressKey in address) {
16756    	if (address[addressKey] != null && address[addressKey].search(/\^/) != -1) {
16757    		address[addressKey] = address[addressKey].replace(/\^/gi,"'");
16758    	}
16759    }
16760
16761	return buildDetailsBlockForAddress(address, isCompact, hideNickname, $form.attr('id'));
16762}
16763
16764function buildDetailsBlockForAddress(address, isCompact, hideNickname, formId) {
16765	var shippingAddressType = getShippingAddressType();
16766    var htmlText = $('<div/>
16766', {"class": "address-details-st"});
16767    var header = isCompact ? '' : formId == 'billingAddressForm' ? '<h6>Billing Recipient</h6>' : formId == 'shippingAddressForm' ? '<h6>Ship To:</h6>' : '';
16768    var editButton = (!isCompact && !shippingAddressType || shippingAddressType == SiteConstants.AddressTypes.Customer) ? '<p class="text-right edit-address-container"><a class="edit-address-det" tabindex="0">Edit<span class="visually-hidden">&nbsp;Address Information</span></a></p>' : '';
16769    var addNewButton = '';
16770	var addressNickname = '';
16771	
16772	if (formId == 'shippingAddressForm') {
16773		if (address.address1 && !hideNickname) {
16774			if (address.ID && !address.myStoreID) {
16775				addressNickname = address.ID;
16776			} else if (formId == 'shippingAddressForm' && shippingAddressType == SiteConstants.AddressTypes.Customer && dataBuffer['dwfrm_multipleshipping_addressList']) {
16777				var selectedAddressFromDropdown = dataBuffer['dwfrm_multipleshipping_addressList'].filter(function (addressOption) {
16778					return addressOption.UUID == $('#dwfrm_multipleshipping_addressList').val();
16779				});
16780				
16781				if (selectedAddressFromDropdown.length > 0 && selectedAddressFromDropdown[0].displayValue && $('[has-warehouse-shipment]').attr('has-warehouse-shipment') == 'true') {
16782					util.fillAddressFields(selectedAddressFromDropdown[0], $('#shippingAddressForm'));
16783					writeShippingAddressFormToBuffer();
16784					addressNickname = selectedAddressFromDropdown[0].displayValue;
16785				}
16786			}
16787			
16788			if (addressNickname) {
16789				addressNickname = '<span class="address-nickname">' + addressNickname + '</span>';
16790			}
16791
16792			$('.form-checkout.shipping-address-section').attr('panel-mode', 'view').attr('value', 'true');
16793		}
16794		
16795		if (!isCompact && shippingAddressType == SiteConstants.AddressTypes.Customer) {
16796			addNewButton = '<p class="add-address-container"><a class="add-address-det" tabindex="0">+ Add New Address</a></p>';
16797		}
16798	}
16799	
16800    var htmlcontent = '';
16801    if (address.address1) {
16802	    htmlcontent += header;
16803	    htmlcontent += '<div class="address" tabindex="0"><div>' + (address.myStoreID ? address.fullName : address.firstName) + ' ' + address.lastName + addressNickname + ' </div>';
16804	    htmlcontent += '<div>' + address.address1 + ' </div>';
16805	    if (address.address2) {
16806	    	htmlcontent += '<div>' + address.address2 + ' </div>';
16807	    }
16808
16809	    if (address.address3) {
16810	    	htmlcontent += '<div>' + address.address3 + ' </div>';
16811	    }
16812
16813	    htmlcontent += '<div>' + address.city;
16814
16815	    if (address.stateCode && address.stateCode.toUpperCase() != 'OTHER') {
16816	    	htmlcontent += ' ' + address.stateCode;
16817	    }
16818
16819	    htmlcontent += ' ' + address.postalCode;
16820		htmlcontent += ' ' + address.countryCode + '</div>';
16821		if (address.phone) {
16822			htmlcontent += '<div>' + address.phone + '</div>';
16823		}
16824
16825	    if (address.email) {
16826	    	htmlcontent += '<div class="billing-email-address">' + address.email + '</div>';
16827		}
16828		htmlcontent += '</div>';
16829
16830		if (formId != 'shippingAddressForm' || (!options || !options.Workforce || !options.Workforce.HasForceShippingAddress)) {
16831			htmlcontent += editButton;
16832		}
16833		if (formId == 'shippingAddressForm' && (!options || !options.Workforce || !options.Workforce.HasForceShippingAddress)) {
16834			htmlcontent += addNewButton;
16835		}
16836    }
16837
16838    htmlText.html(htmlcontent);
16839
16840    return htmlText;
16841}
16842
16843/**
16844 * @function
16845 * @description Helper method which constructs a URL for an AJAX request using the
16846 * entered address information as URL request parameters.
16847 */
16848function getShippingMethodURL($form, url, extraParams) {
16849    var params = {
16850		isStore: $form.find('input[name$="_isStore"]').prop('checked'),
16851		myStoreID: $form.find('input[name$="_myStoreID"]').val(),
16852    	address1: $form.find('input[name$="_address1"]').val(),
16853        address2: $form.find('input[name$="_address2"]').val(),
16854        address3: $form.find('input[name$="_address3"]').val(),
16855        countryCode: $form.find('select[id$="_country"]').val(),
16856        stateCode: $form.find('select[id$="_state"]').val(),
16857        postalCode: $form.find('input[name$="_postal"]').val(),
16858        city: $form.find('input[name$="_city"]').val()
16859    };
16860
16861    return util.appendParamsToUrl(url, $.extend(params, extraParams));
16862}
16863
16864function updateShippingMethodList($form, doUpdateSummary, isMyStore, do
16864DataLayerPush, $selectedShippingMethod) {
16865	if (!$shippingMethodList || $shippingMethodList.length === 0) { return; }
16866	
16867	var shippingMethodListData = {
16868		selectedPaymentMethod: $paymentForm.find('input[name*="_selectedPaymentMethodID"]').val()
16869	}
16870
16871	var $shipToStoreOptionInAddressPanel = $('.single.shipping-address-type-selector');
16872	if ($shipToStoreOptionInAddressPanel.length > 0) {
16873		if ($shipToStoreOptionInAddressPanel.find('input:checked').val() == SiteConstants.AddressTypes.Store) {
16874			shippingMethodListData.ShipToStoreOnly = true;
16875
16876			if (dataBuffer[CheckoutConstants.User.SelectedStoreShippingMethod]) {
16877				shippingMethodListData.SelectShippingMethod = dataBuffer[CheckoutConstants.User.SelectedStoreShippingMethod];
16878			}
16879		} else if ($shipToStoreOptionInAddressPanel.find('input:checked').val() == SiteConstants.AddressTypes.Customer) {
16880			shippingMethodListData.ShipToStoreExclude = true;
16881
16882			if (dataBuffer[CheckoutConstants.User.SelectedCustomerShippingMethod]) {
16883				shippingMethodListData.SelectShippingMethod = dataBuffer[CheckoutConstants.User.SelectedCustomerShippingMethod];
16884			}
16885		}
16886	}
16887
16888	if (isMyStore) {
16889		shippingMethodListData.ShipToStoreOnly = true;
16890	}
16891
16892	var shippingMethodListUrl = getShippingMethodURL($form, Urls.shippingMethodsList);
16893	$shippingMethodList.load(shippingMethodListUrl, shippingMethodListData, function () {
16894		var selectedStore = $shippingAddressForm.find('.formfield.myStoreID input').val();
16895		var $shipToStoreShippingMethods;
16896		if (options && options.AgentStore && options.AgentStore.StoreShippingAddressOnly) {
16897			$shipToStoreShippingMethods = $('[shipping-method]');
16898		} else {
16899			$shipToStoreShippingMethods = $('[shipping-method][is-ship-to-store="true"]');
16900		}
16901
16902		if ($shipToStoreShippingMethods.length > 0) {
16903			var selectedStoreFromShippingMethod = $shipToStoreShippingMethods.find('.store-selector select').val();
16904
16905			if (selectedStoreFromShippingMethod && selectedStoreFromShippingMethod != selectedStore && $shipToStoreShippingMethods.find('.input-radio').is(':checked')) {
16906				selectedStore = selectedStoreFromShippingMethod;
16907				$shippingAddressForm.find('.formfield.myStoreID input').val(selectedStore);
16908				setOPCAddress(shippingAddressFormSelector, 'dwfrm_shippingaddress_saveopcaddress').then(function (data) { });
16909			}
16910
16911			buildShipToStoreDetails($shipToStoreShippingMethods);
16912		}
16913
16914		if (selectedStore && dataBuffer && dataBuffer['mystores_addressList']) {
16915			var selectedStoreInBuffer = dataBuffer['mystores_addressList'].filter(function (address) {
16916				return address.myStoreID == selectedStore;
16917			})[0];
16918
16919			if (selectedStoreInBuffer && selectedStoreInBuffer.UUID != $('#mystores_addressList').val()) {
16920				$('#mystores_addressList').val(selectedStoreInBuffer.UUID);
16921				var shippingAddressType = getShippingAddressType();
16922				if (shippingAddressType == SiteConstants.AddressTypes.Store) {
16923					$('#mystores_addressList').trigger('change');
16924					doUpdateSummary = false;
16925				}
16926			}
16927		}
16928		$shippingMethodList.fadeIn('fast');
16929
16930		if (doUpdateSummary) {
16931			updateSummary();
16932		}
16933		
16934		tooltip.init({
16935			items: '.shipping-method-tooltip',
16936			tooltipClass: 'shipping-method-tooltip'
16937		}, $('[shipping-method]'));
16938		tooltip.init({
16939			items: '.tooltip.RewardsCredit',
16940			tooltipClass: 'RewardsCredit'
16941		}, $('[payment-method="REWARDS_CREDIT"]'));
16942		tooltip.init({
16943			items: '.tooltip.LoyaltyCredit',
16944			tooltipClass: 'LoyaltyCredit'
16945		}, $('[payment-method="LOYALTY_CREDIT"]'));
16946		tooltip.init({
16947			items: '.tooltip'
16948		}, $('.form-row.cvn'));
16949
16950		var isStoreAddress = isShipToStoreEnabled();
16951		setShipToStore(isStoreAddress);
16952
16953		if (doDataLayerPush) {
16954			pushDataLayerForShippingMethods($shippingMethodList, $selectedShippingMethod);
16955		}
16956	});
16957}
16958
16959/**
16960 * @function
16961 * @description updates the order summary based on a possibly recalculated basket after a shipping promotion has been applied
16962 */
16963function updateSummary() {
16964    $(document).trigger('Cart.Update', {
16965		'EventTriggers': {
16966			'update-shipping': false
16967		}
16968	});
16969}
16970
16971function getShippingAddressType() {
16972	var shippingAddressType = $('.single.shipping-address-type-selector input:checked').val();
16973	if (!shippingAddressType) {
16974		shippingAddressType = SiteConstants.AddressTypes.Customer;
16975	}
16976	return shippingAddressType;
16977}
16978
16979function renderMyStoresAddressSelector(parameters) {
16980	var templateSettings = {
16981    	interpolate: /{{([\s\S]+?)}}/g
16982    };
16983
16984    var templates = [];
16985	templates['option'] = _.template($('#template-address-selector-option').html(), templateSettings);
16986
16987	$.ajax({
16988		method: 'POST',
16989		url: Urls.getMyStoresAddressList,
16990		contentType: 'application/json',
16991		data: JSON.stringify({ storeQuery: StoreQueryFilters.ShipToStoreEnabled }),
16992		dataType: 'json',
16993		success: function(data) {
16994			var html = [];
16995			html['options'] = '';
16996
16997			dataBuffer['mystores_addressList'] = data.addresses;
16998			dataBuffer['store_name_lookup'] = {};
16999			data.addresses.map(function (element) {
17000				dataBuffer['store_name_lookup'][element.myStoreID] = element.fullName;
17001			});
17002
17003			var isStoreAddressSelected = false;
17004			for (var index = 0; index < data.addresses.length; index++) {
17005				var address = data.addresses[index];
17006
17007				if (address.isSelected) {
17008					isStoreAddressSelected = true;
17009				}
17010				
17011				var optionData = _.extend(address, {
17012					id: address.UUID,
17013					cssClass: '',
17014					selected: address.isSelected ? 'selected' : '',
17015					text: address.labelText
17016				});
17017
17018				optionData.ID = address.myStoreID;
17019				html['options'] += templates['option'](optionData);
17020			}
17021
17022			$('#mystores_addressList').html(html['options']);
17023
17024			if (isStoreAddressSelected) {
17025				setShipToStore(true);
17026				$('.single.shipping-address-type-selector input[value="' + SiteConstants.AddressTypes.Store + '"]').click();
17027			}
17028			
17029			if (parameters && parameters.callback) {
17030				parameters.callback();
17031			}
17032		}
17033	});
17034}
17035
17036function renderAddressSelector(parameters) {
17037	var templateSettings = {
17038    	interpolate: /{{([\s\S]+?)}}/g
17039    };
17040
17041    var templates = [];
17042    templates['selector'] = _.template($('#template-address-selector').html(), templateSettings);
17043    templates['option'] = _.template($('#template-address-selector-option').html(), templateSettings);
17044
17045	$.ajax({
17046		method: 'POST',
17047		url: Urls.getAddressList,
17048		contentType: 'application/json',
17049		data: JSON.stringify({ storeQuery: StoreQueryFilters.ShipToStoreEnabled }),
17050		dataType: 'json',
17051		success: function(data) {
17052			var html = [];
17053			var hasAddressBookAddresses = false;
17054			var selectedAddressID = null;
17055
17056			dataBuffer[parameters.id] = data.addresses;
17057
17058			var isAddressSelectedInData = false;
17059			for (var index = 0; index < data.addresses.length; index++) {
17060				var address = data.addresses[index];
17061				
17062				if (address.isSelected) {
17063					isAddressSelectedInData = true;
17064					selectedAddressID = address.ID;
17065					break;
17066				}
17067			}
17068			
17069			html['options'] = '';
17070			for (var index = 0; index < data.addresses.length; index++) {
17071				var address = data.addresses[index];
17072
17073				if (address.isShippingAddress) {
17074					var addressId = address.key ? address.key : address.ID;
17075					var isSelected = false;
17076
17077					if (!$shippingAddressForm.find('input[name$="_address1"]').val() && $('[has-warehouse-shipment]').attr('has-warehouse-shipment') == 'true') {
17078						if (!isAddressSelectedInData) {
17079							if (!selectedAddressID && parameters.currentAddresses && parameters.currentAddresses.length > 0 && parameters.currentAddresses.indexOf(address.UUID) >= 0) {
17080								isSelected = true;
17081								selectedAddressID = address.ID;
17082							} else if (!selectedAddressID && parameters.selected && address.ID == parameters.selected && parameters.selected != "new") {
17083								isSelected = true;
17084								selectedAddressID = address.ID;
17085							} else if (!selectedAddressID && address.ID == $('#dwfrm_multipleshipping_addressList:visible option').val() && parameters.selected != "new") {
17086								isSelected = true;
17087								selectedAddressID = address.ID;
17088							} else if (!selectedAddressID && address.isDefault && parameters.selected != "new") {
17089								isSelected = true;
17090								selectedAddressID = address.ID;
17091							}
17092						}
17093					}
17094
17095					var optionData = _.extend(address, {
17096						id: address.UUID,
17097						cssClass: '',
17098						selected: isSelected || address.isSelected ? 'selected' : '',
17099						text: '(' + (!address.displayValue ? addressId : address.displayValue) + ') ' + address.labelText
17100					});
17101
17102					if (!address.isMyStore) {
17103						hasAddressBookAddresses = true;
17104					}
17105
17106					html['options'] += templates['option'](optionData);
17107				}
17108			}
17109
17110			if (hasAddressBookAddresses) {
17111				html['selector'] = templates['selector']({
17112					id: parameters.id,
17113					options: html['options']
17114				});
17115
17116				var $html = $(html['selector']);
17117
17118				if (isSelected || address.isSelected) {
17119					$html.find('option.placeholder').remove();
17120				}
17121
17122				$(parameters.el).html($html);
17123				$(parameters.container).show();
17124			} else {
17125				$(parameters.container).hide();
17126			}
17127
17128			if (parameters && parameters.callback) {
17129				parameters.callback({
17130					selectedAddressID: selectedAddressID,
17131					isSelected: isSelected,
17132					optionData: optionData
17133				});
17134			}
17135		}
17136	});	
17137}
17138
17139function editBillingAddress() {
17140	$('.bill-address-fields').removeClass('collapsed').show();
17141	$('.billing-select-address').hide();
17142	
17143	$('.billing-address-preview').hide();
17144	$('.billing-address-data.form-info').hide();
17145	
17146	$billingAddressFormContainer.show();
17147	$billingAddressForm.show();
17148	
17149	$shippingAddressFormContainer.data('hidden-editable', $('.shipping-address-data.form-info').html().length ? true : false).hide();
17150	
17151	$('.form-checkout.shipping-address-section').removeClass('active').find('.shipping-address-cont').addClass('hidden');
17152	$('.form-checkout.payment-section').removeClass('active').find('.shipping-payment-cont').addClass('hidden');
17153}
17154
17155function editShippingAddress() {
17156	writeShippingAddressFormToBuffer();
17157	openShippingAddressForm();
17158}
17159
17160function openShippingAddressForm() {
17161	$('.form-checkout.shipping-address-section').attr('panel-mode', 'edit');
17162	$shippingAddressFormContainer.data('hidden-editable', true).hide();
17163	$('.shipping-address-data').hide();
17164	$shippingAddressFormContainer.show();
17165	$shippingAddressForm.removeClass('add edit').addClass('edit');
17166	$shippingAddressForm.find('input[name="action"]').val('edit');
17167		
17168	$billingAddressForm.hide();
17169	$('.billing-address-data.form-info').css('display', 'block');
17170	$('.form-checkout.payment-section').removeClass('active').find('.shipping-payment-cont').addClass('hidden');
17171	
17172	$('.shipping-select-address').hide();
17173	$('.editaddress-shipping').removeClass('collapsed');
17174}
17175
17176function setShipToStore(isStoreAddress) {
17177	$sectionCheckout.attr('is-my-store', isStoreAddress);
17178	$('#dwfrm_shippingaddress_isStore').prop('checked', isStoreAddress);
17179}
17180
17181function isShipToStoreEnabled() {
17182	return $('#dwfrm_shippingaddress_isStore').prop('checked');
17183}
17184
17185function buildShipToStoreDetails($shippingMethods) {
17186	$shippingMethods.each(function(index, element) {
17187		var $shippingMethod = $(element);
17188		var $selectedStore = $shippingMethod.find('.store-selector option:checked');
17189		var storeSelectorCount = $shippingMethod.find('.store-selector option').length;
17190		var storeData = $selectedStore.data('address');
17191		if (storeData) {
17192			var storeHtml = '';
17193			if (storeData.name) {
17194				storeHtml += '<p>' + storeData.name + '</p>';
17195			}
17196			if (storeData.address1) {
17197				storeHtml += '<p>' + storeData.address1 + '</p>';
17198			}
17199			if (storeData.address2) {
17200				storeHtml += '<p>' + storeData.address2 + '</p>';
17201			}
17202			if (storeData.city || storeData.state || storeData.postalCode) {
17203				storeHtml += '<p>' + storeData.city + ', ' + storeData.state + ' ' + storeData.postalCode + '</p>';
17204			}
17205
17206			if (storeSelectorCount > 1 && !isStoreShippingAddressOnly) {
17207				storeHtml += '<a class="action change" tabindex="0">Change Store</a>';
17208			}
17209
17210			$shippingMethod.find('.store-details').html(storeHtml);
17211		}
17212	});
17213}
17214
17215/**
17216 * @function
17217 * @description selects a shipping method for the default shipment and updates the summary section on the right hand side
17218 * @param
17219 */
17220function selectShippingMethod(shipmentID, shippingMethodID, isShipToStore, $selectedShippingMethod) {
17221    var shippingAddressType = getShippingAddressType();
17222	if (shippingAddressType == SiteConstants.AddressTypes.Customer) {
17223		dataBuffer[CheckoutConstants.User.SelectedCustomerShippingMethod] = shippingMethodID;
17224	} else if (shippingAddressType == SiteConstants.AddressTypes.Store) {
17225		dataBuffer[CheckoutConstants.User.SelectedStoreShippingMethod] = shippingMethodID;
17226	}
17227
17228	if (isShipToStore || isStoreShippingAddressOnly) {
17229		setShipToStore(true); 
17230	} else {
17231		setShipToStore(false);
17232	}
17233
17234    // attempt to set shipping method
17235    var url = getShippingMethodURL($shippingAddressForm, Urls.selectShippingMethodsList, {shipmentID: shipmentID, shippingMethodID: shippingMethodID});
17236    ajax.getJson({
17237        url: url,
17238        callback: function (data) {
17239            // display promotion in UI and update the summary section,
17240            // if some promotions were applied
17241            $('.shippingpromotions').empty();
17242
17243            $('[name*="_shippingMethodID"]').prop('disabled', false);
17244            updateShippingMethodList($shippingAddressForm, true, null, true, $selectedShippingMethod);
17245        }
17246    });
17247}
17248
17249/**
17250 * @description Make the AJAX request to set address data
17251 * @param {Element} form The form element that present address object
17252 * @param {Element} Action name to apply to form
17253 * @returns {Promise}
17254 */
17255var setOPCAddress = function (form, name, skipUseBillingAsShipping) {
17256	var $form = $(form);
17257	var url = (form == '#billingAddressForm') ? Urls.setOpcAddress : $form.attr('action');
17258	var sanitizedFormData = {};
17259	var serializedFormDataArray = $form.serializeArray();
17260	var hasCountryField = false;
17261	for (var serializedFormDataKey in serializedFormDataArray) {
17262		var serializedFormElement = serializedFormDataArray[serializedFormDataKey];
17263		if (skipUseBillingAsShipping && serializedFormElement.name == 'dwfrm_billingaddress_useAsShippingAddress') {
17264			sanitizedFormData[serializedFormElement.name] = false;
17265		} else if (skipUseBillingAsShipping && serializedFormElement.name == 'isShippingAddressSameAsBillingAddress') {
17266			sanitizedFormData[serializedFormElement.name] = 'off';
17267		} else if (serializedFormElement && serializedFormElement.name && sanitizedFormData[serializedFormElement.name]) {
17268			sanitizedFormData[serializedFormElement.name] += ',' + serializedFormElement.value;
17269		} else {
17270			sanitizedFormData[serializedFormElement.name] = serializedFormElement.value;
17271		}
17272		if (serializedFormElement && serializedFormElement.name && serializedFormElement.name.indexOf('_country') < 0) {
17273			hasCountryField = true;
17274		}
17275	}
17276	if (hasCountryField) {
17277		var $countryField = $form.find('[name$="_country"]');
17278		sanitizedFormData[$countryField.attr('name')] = $countryField.val();
17279	}
17280	sanitizedFormData[name] = name;
17281
17282    return new Promise(function (resolve, reject) {
17283		$.ajax({
17284			type: 'POST',
17285			url: util.ajaxUrl(url),
17286			data: sanitizedFormData,
17287			success: function(data) {
17288				resolve(data);
17289			},
17290			error: function(data) {
17291				reject(data);
17292			}
17293		});
17294    });
17295};
17296
17297function pushDataLayerForShippingMethods($shippingMethodList, $selectedShippingMethod) {
17298	var $selectedShippingMethods;
17299	if ($selectedShippingMethod) {
17300		$selectedShippingMethods = $selectedShippingMethod;
17301	} else {
17302		$selectedShippingMethods = $shippingMethodList.find('input[type="radio"]:checked');
17303	}
17304	$selectedShippingMethods.each(function(index, element) {
17305		var $this = $(this);
17306		var $shippingMethod = $this.closest('[shipping-method]');
17307		var dataObject = {
17308			value: parseFloat($shippingMethod.attr('amount')),
17309			shipping_tier: $shippingMethod.attr('shipping-method')
17310		}
17311
17312		GoogleTagManagerClient.PushDataLayerForDataLayerType('AddShippingInfo', dataObject, { ContainerSelector: '.order-item[shipment="' + $shippingMethod.closest('form').attr('shipment') + '"]' }).catch(function() {});
17313	});
17314}
17315
17316function handleShippingRestrictions(data) {
17317	$('.shipping-address-cont').find('> .error').remove();
17318	var hasShippingRestrictions = data.status == 'shippingRestrictionsError';
17319	if (hasShippingRestrictions) {
17320		openRestrictionsPopup(data);
17321	}
17322
17323	return hasShippingRestrictions;
17324}
17325
17326function openRestrictionsPopup(data) {
17327	var payload = JSON.parse(data.payload);
17328	var popup = require('../../popup');
17329	
17330	var restrictionList = '';
17331	if (payload.list && payload.list.length > 0) {
17332		var list = $.map(payload.list, function (el) {
17333			var restrictionName = el.name ? el.name + ':<br/>' : '';
17334			
17335			return '<li>' + restrictionName + el.message + '</li>';
17336		}).join('');
17337		
17338		restrictionList = '<ul class="shipping-restrictions-popup__list">' + list + '</ul>';
17339	}
17340	
17341	popup.init({
17342		title: payload.title,
17343		content: payload.message + restrictionList
17344	}).showPopup();
17345}
17346
17347function renderOptions(optionParent, optionsStoreId) {
17348	var optionsHtml;
17349	var optionsStoreOptions = $(optionsStoreId + ' option:selected').data('options');
17350	var statesList;
17351	
17352	if (optionsStoreOptions) {
17353		statesList = JSON.parse(optionsStoreOptions.replace(/\'/g, '\"'));
17354		
17355		if (statesList.length > 1) {
17356			optionsHtml += '<option value="">' + Resources.SelectPlaceholder + '</option>';
17357		}
17358		
17359		statesList.forEach(function (state) {
17360			optionsHtml += '<option class="select-option" label="' + state.label + '" value="' + state.code + '">' + state.label + '</option>';
17361		});
17362
17363		var $optionParent = $(optionParent);
17364		$optionParent.empty().append(optionsHtml);
17365
17366		if (statesList.length <= 1) {
17367			$optionParent.prop('selectedIndex', statesList.length - 1);
17368			$optionParent.removeClass('required');
17369			$optionParent.closest('.formfield').hide();
17370		} else {
17371			$optionParent.addClass('required');
17372			$optionParent.closest('.formfield').show();
17373		}
17374	}
17375}
17376
17377$(document).on('update-shipping', function() {
17378	if ($('.shipping-methods-section').length == 0) { return; }
17379	updateShippingMethodList($shippingAddressForm);
17380});
17381
17382exports.init = function (optionsParameter) {
17383	options = optionsParameter || {};
17384
17385	if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
17386		AddressVerificationDialog = require('../../Lob/AddressVerification/AddressVerificationDialog');
17387	}
17388
17389	if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
17390		AddressAutocompleteDialog = require('../../Lob/AddressAutocomplete/AddressAutocompleteDialog');
17391	}
17392
17393	isStoreShippingAddressOnly = !!(options && options.AgentStore && options.AgentStore.StoreShippingAddressOnly);
17394	if (isStoreShippingAddressOnly) {
17395		setShipToStore(true);
17396	}
17397	initializeEvents();
17398	initializeUserInterface();
17399};
17400
17401exports.updateSummary = updateSummary;
17402},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper":112,"../../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants":113,"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../Lob/AddressAutocomplete/AddressAutocompleteDialog":1,"../../Lob/AddressVerification/AddressVerificationDialog":2,"../../ajax":3,"../../dialog":36,"../../findMoreStores":37,"../../popup":83,"../../tooltip":101,"../../util":102,"lodash":133,"promise":134}],65:[function(require,module,exports){
17403'use strict';
17404
17405var $errorBox = $('<div />', { 'id': 'errorBoxOPC', 'class': 'payment-declined p-2 mt-2', 'role': 'alert', 'tabindex': '0' });
17406var $errorBoxIcon = $('<i />', { 'class': 'fa fa-exclamation-triangle', 'role': 'presentation' });
17407var $errorBoxHeadline = $('<span />', { 'class': 'opc-error-box-headline' });
17408var $errorBoxMessage = $('<span />', { 'class': 'opc-error-box-message' });
17409
17410function getErrorMessage(fieldType) {
17411	var errorMessage;
17412	switch (true) {
17413		case (fieldType.indexOf('_loyaltyNumber') > -1):
17414            errorMessage = window.Resources.OPC_LOYALTY_NUMBER_ERROR;
17415            break;
17416		case (fieldType.indexOf('_loyaltyZipCode') > -1):
17417			errorMessage = window.Resources.OPC_LOYALTY_ZIP_ERROR;
17418			break;
17419		case (fieldType.indexOf('_loyaltyPhoneNumber') > -1):
17420			errorMessage = window.Resources.OPC_LOYALTY_PHONE_ERROR;
17421			break;
17422		case (fieldType.indexOf('_rewardsNumber') > -1):
17423            errorMessage = window.Resources.OPC_REWARDS_NUMBER_ERROR;
17424            break;
17425		case (fieldType.indexOf('_rewardsZipCode') > -1):
17426			errorMessage = window.Resources.OPC_REWARDS_ZIP_ERROR;
17427			break;
17428		case (fieldType.indexOf('_rewardsPhoneNumber') > -1):
17429			errorMessage = window.Resources.OPC_REWARDS_PHONE_ERROR;
17430			break;
17431		case (fieldType.indexOf('_giftCard_number') > -1):
17432            errorMessage = window.Resources.OPC_GIFT_CARD_ERROR;
17433            break;
17434		case (fieldType.indexOf('_giftCard_pin') > -1):
17435            errorMessage = window.Resources.OPC_GIFT_CARD_PIN_ERROR;
17436            break;
17437        case (fieldType.indexOf('_privateLabelCredit_number') > -1):
17438            errorMessage = window.Resources.OPC_PRIVATE_LABEL_ERROR;
17439            break;
17440        case (fieldType.indexOf('_creditCard_owner') > -1):
17441            errorMessage = window.Resources.OPC_CARD_OWNER_ERROR;
17442            break;
17443		case (fieldType.indexOf('_creditCard_number') > -1):
17444			errorMessage = window.Resources.OPC_CARD_NUMBER_ERROR;
17445			break;
17446		case (fieldType.indexOf('_creditCard_cvn') > -1):
17447			errorMessage = window.Resources.OPC_SECURITY_CODE_ERROR;
17448			break;
17449		case (fieldType.indexOf('_transactionalsms_smsPhone') > -1):
17450				errorMessage = window.Resources.TRANSACTIONAL_SMS_MISSING_ERROR;
17451				break;
17452        default:
17453            return;
17454    }
17455	return errorMessage;
17456}
17457
17458function getPaymentFormErrorMessages($form) {
17459	if (!$form || $form.length == 0 || $form.find('.field.error').length == 0) {
17460		return;
17461	}
17462	var errorsList = new Array();
17463	$form.find('.field.error').each(function (index, element) {
17464		var errorMessage = getErrorMessage($(element).attr('name'));
17465		if (errorMessage) {
17466			errorsList.push(errorMessage);
17467		}
17468	});
17469	return errorsList;
17470}
17471
17472function paymentErrorBox(headline, message, errorItems) {
17473	$errorBox.html('');
17474
17475	if (!headline || !message) {
17476		return;
17477	}
17478
17479	$errorBoxHeadline.html('&nbsp;' + headline + ':&nbsp;');
17480	$errorBoxMessage.html(message + '&nbsp;');
17481	$errorBox.append($errorBoxIcon).append($errorBoxHeadline).append($errorBoxMessage);
17482
17483	if (errorItems && errorItems.length > 0) {
17484		for (var i = 0; i < errorItems.length; i++) {
17485			var $newErrorItem = errorListItem(errorItems[i]);
17486			$errorBox.append($newErrorItem);
17487		}
17488	}
17489
17490	return $errorBox;
17491}
17492
17493function errorListItem(errorMsg) {
17494	var $errorListItem = $('<span />', { 'class': 'opc-error-box-item' });
17495		$errorListItem.html(errorMsg + '&nbsp;');
17496
17497	return $errorListItem;
17498}
17499
17500exports.getErrorMessages = getPaymentFormErrorMessages;
17501exports.errorBox = paymentErrorBox;
17502},{}],66:[function(require,module,exports){
17503'use strict';
17504
17505var ajax = require('../../ajax'),
17506    formPrepare = require('./formPrepare'),
17507    progress = require('../../progress'),
17508    tooltip = require('../../tooltip'),
17509    util = require('../../util');
17510
17511var shippingMethods;
17512/**
17513 * @function
17514 * @description Initializes gift message box, if shipment is gift
17515 */
17516function giftMessageBox() {
17517    // show gift message box, if shipment is gift
17518    $('.gift-message-text').toggleClass('hidden', $('input[name$="_shippingAddress_isGift"]:checked').val() !== 'true');
17519}
17520
17521/**
17522 * @function
17523 * @description updates the order summary based on a possibly recalculated basket after a shipping promotion has been applied
17524 */
17525function updateSummary() {
17526    var $summary = $('#secondary.summary');
17527    // indicate progress
17528    progress.show($summary);
17529
17530    // load the updated summary area
17531    $summary.load(Urls.summaryRefreshURL, function () {
17532        // hide edit shipping method link
17533        $summary.fadeIn('fast');
17534        $summary.find('.checkout-mini-cart .minishipment .header a').hide();
17535        $summary.find('.order-totals-table .order-shipping .label a').hide();
17536        $(document).trigger('update-summary');
17537    });
17538}
17539
17540/**
17541 * @function
17542 * @description Helper method which constructs a URL for an AJAX request using the
17543 * entered address information as URL request parameters.
17544 */
17545function getShippingMethodURL(url, extraParams) {
17546    var $form = $('.address');
17547    var params = {
17548        address1: $form.find('input[name$="_address1"]').val(),
17549        address2: $form.find('input[name$="_address2"]').val(),
17550        address3: $form.find('input[name$="_address3"]').val(),
17551        countryCode: $form.find('select[id$="_country"]').val(),
17552        stateCode: $form.find('select[id$="_state"]').val(),
17553        postalCode: $form.find('input[name$="_postal"]').val(),
17554        city: $form.find('input[name$="_city"]').val()
17555    };
17556    return util.appendParamsToUrl(url, $.extend(params, extraParams));
17557}
17558
17559/**
17560 * @function
17561 * @description selects a shipping method for the default shipment and updates the summary section on the right hand side
17562 * @param
17563 */
17564function selectShippingMethod(shippingMethodID) {
17565    // nothing entered
17566    if (!shippingMethodID) {
17567        return;
17568    }
17569    // attempt to set shipping method
17570    var url = getShippingMethodURL(Urls.selectShippingMethodsList, {shippingMethodID: shippingMethodID});
17571    ajax.getJson({
17572        url: url,
17573        callback: function (data) {
17574            updateSummary();
17575            if (!data || !data.shippingMethodID) {
17576                window.alert('Couldn\'t select shipping method.');
17577                return false;
17578            }
17579            // display promotion in UI and update the summary section,
17580            // if some promotions were applied
17581            $('.shippingpromotions').empty();
17582
17583            // TODO the for loop below isn't doing anything?
17584            // if (data.shippingPriceAdjustments && data.shippingPriceAdjustments.length > 0) {
17585            //     var len = data.shippingPriceAdjustments.length;
17586            //     for (var i=0; i < len; i++) {
17587            //         var spa = data.shippingPriceAdjustments[i];
17588            //     }
17589            // }
17590        }
17591    });
17592}
17593
17594/**
17595 * @function
17596 * @description Make an AJAX request to the server to retrieve the list of applicable shipping methods
17597 * based on the merchandise in the cart and the currently entered shipping address
17598 * (the address may be only partially entered).  If the list of applicable shipping methods
17599 * has changed because new address information has been entered, then issue another AJAX
17600 * request which updates the currently selected shipping method (if needed) and also updates
17601 * the UI.
17602 */
17603function updateShippingMethodList() {
17604    var $shippingMethodList = $('#shipping-method-list');
17605    if (!$shippingMethodList || $shippingMethodList.length === 0) { return; }
17606    var url = getShippingMethodURL(Urls.shippingMethodsJSON);
17607
17608    ajax.getJson({
17609        url: url,
17610        callback: function (data) {
17611            if (!data) {
17612                window.alert('Couldn\'t get list of applicable shipping methods.');
17613                return false;
17614            }
17615            if (shippingMethods && shippingMethods.toString() === data.toString()) {
17616                // No need to update the UI.  The list has not changed.
17617                return true;
17618            }
17619
17620            // We need to update the UI.  The list has changed.
17621            // Cache the array of returned shipping methods.
17622            shippingMethods = data;
17623            // indicate progress
17624            progress.show($shippingMethodList);
17625
17626            // load the shipping method form
17627            var smlUrl = getShippingMethodURL(Urls.shippingMethodsList);
17628            $shippingMethodList.load(smlUrl, function () {
17629                $shippingMethodList.fadeIn('fast');
17630                // rebind the radio buttons onclick function to a handler.
17631                $shippingMethodList.find('[name$="_shippingMethodID"]').click(function () {
17632                    selectShippingMethod($(this).val());
17633                });
17634
17635                // update the summary
17636                updateSummary();
17637                progress.hide();
17638                tooltip.init();
17639                //if nothing is selected in the shipping methods select the first one
17640                if ($shippingMethodList.find('.input-radio:checked').length === 0) {
17641                    $shippingMethodList.find('.input-radio:first').prop('checked', 'checked');
17642                }
17643            });
17644        }
17645    });
17646}
17647
17648exports.init = function () {
17649    formPrepare.init({
17650        continueSelector: '[name$="shippingAddress_save"]',
17651        formSelector:'[id$="singleshipping_shippingAddress"]'
17652    });
17653    $('input[name$="_shippingAddress_isGift"]').on('click', giftMessageBox);
17654
17655    $('.address').on('change',
17656        'input[name$="_addressFields_address1"], input[name$="_addressFields_address2"], input[name$="_addressFields_address3"], select[name$="_addressFields_states_state"], input[name$="_addressFields_city"], input[name$="_addressFields_zip"]',
17657        updateShippingMethodList
17658    );
17659
17660    giftMessageBox();
17661    updateShippingMethodList();
17662};
17663
17664exports.updateShippingMethodList = updateShippingMethodList;
17665
17666},{"../../ajax":3,"../../progress":86,"../../tooltip":101,"../../util":102,"./formPrepare":61}],67:[function(require,module,exports){
17667'use strict';
17668
17669var _ = require('lodash');
17670var AddressAutocompleteDialog;
17671var AddressVerificationDialog;
17672var AddressVerificationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper');
17673var ApplePay = require('./checkout/ApplePay');
17674var CheckoutConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants');
17675var GiftCard = require('./checkout/GiftCardPaymentPanel');
17676var loadingIndicator = require('../components/LoadingIndicator');
17677var PaymentMethodConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
17678var RewardsCredit = require('./checkout/RewardsCredit');
17679var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
17680var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
17681var tooltip = require('../tooltip');
17682var TPromise = require('promise');
17683var util = require('../util');
17684var ValidationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
17685var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
17686var ValidationSettings = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
17687
17688var checkoutFormSelector = '#checkout-form';
17689var $checkoutForm = $(checkoutFormSelector);
17690var creditCardTypeListSelector = '.payment-card-list';
17691var $creditCardTypeList = $(creditCardTypeListSelector);
17692var creditCardNumberInputSelector = '.formfield.card-number input';
17693var $creditCardNumberInput = $(creditCardNumberInputSelector);
17694var creditCardExpirationMonthInputSelector = '.monthfield input';
17695var $creditCardExpirationMonthInput = $(creditCardExpirationMonthInputSelector);
17696var creditCardExpirationYearInputSelector = '.yearfield input';
17697var $creditCardExpirationYearInput = $(creditCardExpirationYearInputSelector);
17698var creditCardCVVInputSelector = '.form-row.cvn input';
17699var $creditCardCVVInput = $(creditCardCVVInputSelector);
17700var addressInputSelector = '.formfield.address1 input';
17701var $addressInput = $(addressInputSelector);
17702var submitPaymentSelector = '#submitOPCPayment';
17703var $submitPayment = $(submitPaymentSelector);
17704var $rewardsAccordion = $('.rewards');
17705var $rewardsPaymentContainer = $rewardsAccordion.find('[payment-method="REWARDS_CREDIT"]');
17706var $rewardsForm = $rewardsPaymentContainer.find('.rewards-form');
17707var $rewardsApplied = $rewardsPaymentContainer.find('.rewards-applied');
17708
17709var options = {};
17710var dataBuffer = {};
17711
17712function initialize(options) {
17713	$.validator.addMethod('requiredExcludingPunctuation', ValidationHelper.ValidateRequiredExcludingPunctuation, Resources.INVALID_VALUE);
17714	$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_ZIP_CODE);
17715	$.validator.addMethod('postalCode', ValidationHelper.ValidatePostalCode, Resources.INVALID_POSTAL_CODE);
17716	var checkoutFormValidator = $checkoutForm.validate();
17717	checkoutFormValidator.settings.onfocusout = ValidationSettings.Checkout.onfocusout;
17718	checkoutFormValidator.settings.errorPlacement = ValidationSettings.Checkout.errorPlacement;
17719	if ($checkoutForm.find('select[name$="_country"]').val() == 'US') {
17720		$checkoutForm.find('input[name$="_postal"]').rules('add', ValidationRules.ZipCode);
17721	} else {
17722		$checkoutForm.find('input[name$="_postal"]').rules('add', ValidationRules.PostalCode);
17723	}
17724
17725    $(document).on('click', submitPaymentSelector, function(event) {
17726    	event.preventDefault();
17727
17728		var addressDeliverable = function () {
17729			submitPayment();
17730		}
17731	
17732		var addressNotDeliverable = function (addressVerifyData) {
17733			AddressVerificationDialog.initialize(options, 'billing', addressVerifyData);
17734		}
17735	
17736		if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
17737			AddressVerificationHelper.VerifyAddress($checkoutForm, options).then(addressDeliverable, addressNotDeliverable);
17738		} else {
17739			addressDeliverable();
17740		}
17741    });
17742
17743	if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
17744		$checkoutForm.off('keyup.AddressAutocomplete').on('keyup.AddressAutocomplete', 'input[type="text"]', function (event) {
17745			var $this = $(this);
17746			var $form = $this.closest('form');
17747			var formValidator = $form.validate();
17748
17749			if (event.keyCode == 8 || event.keyCode == 46) {
17750				$this.parent().find('.address-autocomplete-dialog').remove();
17751			} else if (!formValidator.checkForm() && ($this.attr('id').indexOf('_address1') >= 0 || $this.attr('id').indexOf('_address2') >= 0)) {
17752				var addressAutocompleteHandler = function (addressAutocompleteData) {
17753					AddressAutocompleteDialog.initialize(options, addressAutocompleteData);
17754				}
17755			
17756				AddressVerificationHelper.AutocompleteAddress($form, $this, options).then(addressAutocompleteHandler);
17757			} else {
17758				$this.parent().find('.address-autocomplete-dialog').remove();
17759			}
17760		});
17761		
17762		$(document).off('SetAddressFromAddressAutocomplete').on('SetAddressFromAddressAutocomplete', function (event, data) {
17763			AddressVerificationHelper.SetAddressFromAddressVerificationDialog(data.$form, data.Address);
17764			$checkoutForm.find('#dwfrm_billing_billingAddress_email_emailAddress, input.phone, input.postal').trigger('blur.RewardsAccountSearch');
17765		});
17766	}
17767
17768	if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
17769		$(document).off('SetAddressFromAddressVerification').on('SetAddressFromAddressVerification', function (event, addressData) {
17770			AddressVerificationHelper.SetAddressFromAddressVerificationDialog($checkoutForm, addressData);
17771			if (!addressData.DisableSubmitPayment) {
17772				submitPayment();
17773			}
17774		});
17775	}
17776
17777	$checkoutForm.off('blur.RewardsAccountSearch').on('blur.RewardsAccountSearch', '#dwfrm_billing_billingAddress_email_emailAddress, input.phone, input.postal', function(event) {
17778		if (dataBuffer['RewardsAccountSearchRequest'] && dataBuffer['RewardsAccountSearchRequest'].readyState !== 4) {
17779			dataBuffer['RewardsAccountSearchRequest'].abort();
17780			dataBuffer['RewardsAccountSearchRequest'] = null;
17781		}
17782
17783		var $this = $(this);
17784		var field = $this.closest('[field]').attr('field');
17785		var value = $this.val();
17786		if (field && value) {
17787			if (!dataBuffer['RewardsData']) {
17788				dataBuffer['RewardsData'] = {};
17789			}
17790			dataBuffer['RewardsData'][field] = value;
17791
17792			switch (field) {
17793				case 'emailAddress':
17794					$rewardsForm.find('#dwfrm_billing_rewards_rewardsEmail').val(value);
17795					break;
17796				case 'phone':
17797					$rewardsForm.find('.field.phonenumber').val(value);
17798					break;
17799				case 'postal':
17800					$rewardsForm.find('.field.zipcode').val(value);
17801					break;
17802			}
17803
17804			var isRewardsDataReady = dataBuffer['RewardsData']['emailAddress'] && dataBuffer['RewardsData']['phone'] && dataBuffer['RewardsData']['postal'];
17805			var isRewardsNumberFieldEmpty = !$rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val();
17806			if (isRewardsDataReady && isRewardsNumberFieldEmpty) {
17807				var payload = {
17808					EmailAddress: dataBuffer['RewardsData']['emailAddress'],
17809					PhoneNumber: dataBuffer['RewardsData']['phone'],
17810					ZipCode: dataBuffer['RewardsData']['postal'],
17811				}
17812
17813				if ($rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val()) {
17814					payload.RewardsNumber = $rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val();
17815				}
17816
17817				dataBuffer['RewardsAccountSearchRequest'] = $.ajax({
17818					type: 'POST',
17819					url: Urls.searchForRewards,
17820					contentType: 'application/json',
17821					dataType: 'json',
17822					data: JSON.stringify(payload),
17823					success: function(data) {
17824						renderRewardsData(data);
17825					},
17826					complete: function(xhr) {
17827
17828					}
17829				});
17830			}
17831		}
17832	});
17833
17834	$rewardsForm.off('keyup.RewardsReset change.RewardsReset').on('keyup.RewardsReset change.RewardsReset', 'input', function(event) {
17835		var isRewardsFormPopulated = false;
17836		$rewardsForm.find('input').each(function() {
17837			if ($(this).val()) {
17838				isRewardsFormPopulated = true;
17839			}
17840		});
17841		if (isRewardsFormPopulated) {
17842			$rewardsForm.find('.btn.reset-echo').removeAttr('disabled');
17843		} else {
17844			$rewardsForm.find('.btn.reset-echo').attr('disabled', 'true');
17845		}
17846	});
17847
17848	$rewardsForm.off('click.RewardsReset').on('click.RewardsReset', '.btn.reset-echo', function(event) {
17849		event.preventDefault();
17850
17851		if ($rewardsApplied.find('.reward-row[status="applied"]').length > 0) {
17852			$rewardsApplied.find('.reward-row[status="applied"] .btn.remove').trigger('RewardsCredit.Remove', { ResetSessionData: true });
17853		} else {
17854			$.ajax({
17855				url: Urls.resetRewardsSessionData,
17856				type: 'POST'
17857			});
17858		}
17859		$rewardsApplied.html('');
17860		$rewardsForm.find('input').val('');
17861		$rewardsForm.find('.btn.reset-echo').attr('disabled', 'true');
17862		$rewardsForm.find('[match]').removeAttr('match');
17863		$rewardsAccordion.removeAttr('rewards-found');
17864	});
17865
17866	$(document).off('RewardsAccountSearch').on('RewardsAccountSearch', function(event) {
17867		searchRewards();
17868	});
17869
17870	updateCart('dwfrm_cart_updateCart');
17871
17872	$rewardsForm.off('click.RewardsAccountSearch').on('click.RewardsAccountSearch', '.btn.search-echo', function(event) {
17873		event.preventDefault();
17874		searchRewards();
17875	});
17876
17877	$creditCardNumberInput.off('keydown.CreditCardNumberHandler').on('keydown.CreditCardNumberHandler', function(event) {
17878		$creditCardTypeList.val('');
17879	});
17880
17881	$creditCardNumberInput.off('keyup.CreditCardNumberHandler').on('keyup.CreditCardNumberHandler', function(event) {
17882		var $this = $(this);
17883		var creditCardNumber = $this.val();
17884		
17885		var selectedCard = util.getCreditCardType(creditCardNumber);
17886		if (selectedCard) {
17887			if ($creditCardNumberInput.filter(':visible').length < 1) {
17888				$creditCardNumberInput.val(creditCardNumber);
17889			}
17890			
17891			$('input[name*="_creditCard_type"]').val(selectedCard.PaymentCard);
17892			$('input[name*="_selectedPaymentMethodID"]').val(selectedCard.PaymentMethod);
17893			$creditCardTypeList.find('input[type="radio"][value="' + selectedCard.PaymentCardDisplayKey + '"]').first().prop('checked', true).trigger('change', { doReset: false });
17894			var $paymentCardImage = $creditCardTypeList.find('input[value="' + selectedCard.PaymentCardDisplayKey + '"]');
17895			var paymentCardImageUrl;
17896			if ($paymentCardImage && $paymentCardImage.length > 0) {
17897				paymentCardImageUrl = $paymentCardImage.attr('payment-card-image');
17898			}
17899
17900			if (paymentCardImageUrl) {
17901				$creditCardNumberInput.css('background-image', 'url(' + paymentCardImageUrl + ')');
17902			}
17903
17904			if (creditCardNumber.length >= 16) {
17905				$creditCardExpirationMonthInput.focus();
17906			}
17907			if (selectedCard.PaymentMethod == 'Alliance_Data') {
17908				$('.formfield.card-name').hide();
17909				$('.formfield.card-name input').removeClass('required');
17910				$('.card-expiration-and-cvn').addClass('number-only');
17911				$('.card-expiration-and-cvn input').removeClass('required');
17912			}
17913		} else {
17914			$creditCardTypeList.find('input').prop('checked', false);
17915			$creditCardTypeList.find('option').prop('selected', false);
17916			$creditCardNumberInput.css('background-image', '');
17917			$('.formfield.card-name').show();
17918			$('.formfield.card-name input').addClass('required');
17919			$('.card-expiration-and-cvn').removeClass('number-only');
17920			$('.card-expiration-and-cvn input').addClass('required');
17921		}
17922	});
17923
17924	$creditCardExpirationMonthInput.off('keydown.FieldBlock').on('keypress.FieldBlock', function(event) {
17925		if ($(this).val().length >= 2) {
17926			event.preventDefault();
17927			event.stopPropagation();
17928		}
17929	});
17930
17931	$creditCardExpirationMonthInput.off('keyup.FieldShift').on('keyup.FieldShift', function(event) {
17932		if ($(this).val().length >= 2) {
17933			$creditCardExpirationYearInput.focus();
17934		}
17935	});
17936
17937	$creditCardExpirationYearInput.off('keydown.FieldBlock').on('keypress.FieldBlock', function(event) {
17938		if ($(this).val().length >= 2) {
17939			event.preventDefault();
17940			event.stopPropagation();
17941		}
17942	});
17943
17944	$creditCardExpirationYearInput.off('keyup.FieldShift').on('keyup.FieldShift', function(event) {
17945		if ($(this).val().length >= 2) {
17946			$creditCardCVVInput.focus();
17947		}
17948	});
17949
17950	$creditCardCVVInput.off('keyup.FieldShift').on('keyup.FieldShift', function(event) {
17951		var cvvLength = $('input[name*="_creditCard_type"]').val() == 'Amex' ? 4 : 3;
17952
17953		if ($(this).val().length >= cvvLength) {
17954			$addressInput.focus();
17955		}
17956	});
17957
17958	$(document).on('click', '.accordion-head', function(event) {
17959		$(this).toggleClass('active');
17960	});
17961
17962	$(document).off('click', 'button[name$="addCoupon"]').on('click', 'button[name$="addCoupon"]', function (event) {
17963		event.preventDefault();
17964		
17965		var $this = $(this);
17966		var $container = $this.closest('.cart-coupon-code');
17967		var $applyButton = $container.find('.btn-coupon-code');
17968		$('.error.coupon-error').text('');
17969		
17970		(function() {
17971			$applyButton.loading({
17972				IsLabelShown: false,
17973				BackgroundColor: 'transparent'
17974			});
17975
17976			return TPromise.resolve($.ajax({
17977				type: 'POST',
17978				url: util.ajaxUrl(Urls.onePageSubmit),
17979				data: {
17980					dwfrm_cart_addCoupon: 'dwfrm_cart_addCoupon',
17981					dwfrm_cart_couponCode: $('.accordion.coupon #dwfrm_cart_couponCode').val(),
17982					respType: 'json'
17983				}
17984			}));
17985		}()).then(function (response) {
17986			if (response.success && response.success !== CheckoutConstants.ResponseCodes.AlreadyInBasket) {				
17987				$(document).trigger('Cart.Update');
17988			} else {
17989				var message = response.success == CheckoutConstants.ResponseCodes.AlreadyInBasket ? Resources.COUPON_ADD_ERROR_DUP : Resources.COUPON_ADD_ERROR;
17990				$('<span/>', {'class': 'error coupon-error', 'text': message}).insertAfter('.accordion.coupon .cart-coupon-code .pcode-coupon');
17991				if ($applyButton.attr('loading')) {
17992					$applyButton.loading('destroy');
17993				}
17994			}
17995		});
17996	});
17997
17998	$(document).off('click', 'button[name$="deleteCoupon"]').on('click', 'button[name$="deleteCoupon"]', function (event) {
17999		event.preventDefault();
18000
18001		var $this = $(this);
18002		var $container = $this.closest('.coupon');
18003		var name = $this.attr('name');
18004		(function(){
18005			$container.loading({
18006				IsLabelShown: false,
18007				BackgroundColor: 'transparent'
18008			});
18009
18010			return TPromise.resolve($.ajax({
18011				type: 'POST',
18012				url: util.ajaxUrl(Urls.onePageSubmit),
18013				data: $('form#cart-items-form').serialize() + '&' + name + '=' + name + '&respType=json'
18014			}));
18015		}()).then(function (response) {
18016			$(document).trigger('Cart.Update');
18017		});
18018	});
18019
18020	$(document).off('click', '.order-total-payments [payment-method] .remove').on('click', '.order-total-payments [payment-method] .remove', function (e) {
18021		var paymentInstrumentUUID = $(e.currentTarget).closest('[payment-method').attr('uuid');
18022	
18023		if (paymentInstrumentUUID) {
18024			var requestPayload = {
18025				UUID: paymentInstrumentUUID
18026			}
18027		
18028			$.ajax({
18029				url: Urls.removePaymentInstrument,
18030				type: 'POST',
18031				contentType: 'application/json',
18032				data: JSON.stringify(requestPayload),
18033				dataType: 'json',
18034				success: function(data) {
18035					location.reload();
18036				}
18037			});
18038		}
18039	});
18040
18041	$(document).on('change', '.formfield.country select', function (event) {
18042		var $this = $(this);
18043		var $country = $this.find('option:selected');
18044		var country = $country.val();
18045		var stateListData = $country.data('options');
18046		var stateList;
18047		if (stateListData) {
18048			try {
18049				stateList = JSON.parse(stateListData.replace(/\'/g, '\"'));
18050			} catch (exception) { }
18051
18052			if (stateList) {
18053				var $form = $this.closest('fieldset');
18054				var stateListHtml;
18055				var postalCodePlaceholder;
18056				if (country == 'US') {
18057					stateListHtml = '<option class="select-option" label="State" selected disabled>State</option>';
18058					postalCodePlaceholder = 'ZIP Code';
18059					$form.find('input[name$="_postal"]').rules('remove', 'postalCode zipCode');
18060					$form.find('input[name$="_postal"]').rules('add', ValidationRules.ZipCode);
18061				} else if (country == 'CA') {
18062					stateListHtml = '<option class="select-option" label="Province" selected disabled>Province</option>';
18063					postalCodePlaceholder = 'Postal Code';
18064					$form.find('input[name$="_postal"]').rules('remove', 'postalCode zipCode');
18065					$form.find('input[name$="_postal"]').rules('add', ValidationRules.PostalCode);
18066				} else {
18067					stateListHtml = '<option class="select-option" label="Province/State" selected disabled>Province/State</option>';
18068					postalCodePlaceholder = 'Postal Code';
18069					$form.find('input[name$="_postal"]').rules('remove', 'postalCode zipCode');
18070					$form.find('input[name$="_postal"]').rules('add', ValidationRules.PostalCode);
18071				}
18072				for (var stateListKey in stateList) {
18073					var state = stateList[stateListKey];
18074					stateListHtml += '<option class="select-option" label="' + state.label + '" value="' + state.code + '">' + state.label + '</option>';
18075				}
18076				$form.find('.formfield.state select').html(stateListHtml);
18077				$form.find('.formfield.zip input').attr('placeholder', postalCodePlaceholder);
18078			}
18079		}
18080	});	
18081
18082	$(document).off('Cart.Update').on('Cart.Update', function (event, data) {
18083    	updateCart('dwfrm_cart_updateCart', data);
18084    });
18085
18086	searchRewards();
18087	updateCart('dwfrm_cart_updateCart');
18088}
18089
18090var updateCartBuffer;
18091var updateCartXmlHttpRequestBuffer;
18092function updateCart(triggerElementName, options, isCartPage) {
18093	if (updateCartXmlHttpRequestBuffer) {
18094		for (var updateCartXmlHttpRequestKey in updateCartXmlHttpRequestBuffer) {
18095			var updateCartXmlHttpRequest = updateCartXmlHttpRequestBuffer[updateCartXmlHttpRequestKey];
18096			
18097			if (updateCartXmlHttpRequest.readyState !== 4) { 
18098				updateCartXmlHttpRequest.abort();
18099			}
18100		}
18101	}
18102	updateCartXmlHttpRequestBuffer = [];
18103	updateCartBuffer = [];
18104
18105	getCartHtml(triggerElementName, options, isCartPage);
18106	if (!isCartPage) {
18107		getOrderSummaryCalloutHtml(triggerElementName, options, isCartPage);
18108	}
18109}
18110
18111function getCartHtml(triggerElementName, options, isCartPage) {
18112    var $form = $('#checkout-form');
18113    updateCartBuffer['Cart'] = null;
18114
18115	updateCartXmlHttpRequestBuffer['getCartHtml'] = $.ajax({
18116		url: Urls.onePageSubmit,
18117		type: "POST",
18118		data: triggerElementName + '=' + triggerElementName + '&selectedPaymentMethod=' + $('input[name*="_selectedPaymentMethodID"]').val() + '&format=ajax',
18119		success : function (data) {
18120			updateCartBuffer['Cart'] = data;
18121			getCartData(triggerElementName, options, isCartPage);
18122			updateCartRender(triggerElementName, options, isCartPage);
18123		}
18124	});
18125}
18126
18127function getOrderSummaryCalloutHtml(triggerElementName, options, isCartPage) {
18128    updateCartBuffer['OrderSummaryCallout'] = null;
18129
18130	updateCartXmlHttpRequestBuffer['getOrderSummaryCalloutHtml'] = $.ajax({
18131		url: Urls.getOrderSummaryCalloutContentSlot,
18132		dataType: 'html',
18133		success: function (data) {
18134			updateCartBuffer['OrderSummaryCallout'] = data;
18135			updateCartRender(triggerElementName, options, isCartPage);
18136		}
18137	});
18138}
18139
18140function getCartData(triggerElementName, options, isCartPage) {
18141	updateCartBuffer['CartData'] = null;
18142
18143	updateCartXmlHttpRequestBuffer['getCartData'] = $.ajax({
18144	    url : Urls.getCartData,
18145		type : "POST",
18146		data: {
18147			isCart: isCartPage
18148		},
18149	    success : function (data) {
18150	    	$('[has-local-delivery-shipment]').attr('has-local-delivery-shipment', data.HasLocalDeliveryShipment || false);
18151	    	$('[has-physical-shipment]').attr('has-physical-shipment', data.HasPhysicalShipment || false);
18152			$('[has-warehouse-shipment]').attr('has-warehouse-shipment', data.HasWarehouseShipment || false);
18153			$('[has-warehouse-shipment]').attr('has-shipping-methods', data.ShippingMethods ? data.ShippingMethods.join(' ') : false);
18154			$('[has-subscription-products]').attr('has-subscription-products', data.HasSubscriptionProducts || false);
18155	    	$('[has-gift-cards]').attr('has-gift-cards', data.HasGiftCards || false);
18156	    	$('[has-gift-cards-only]').attr('has-gift-cards-only', data.HasGiftCardsOnly || false);
18157			$('[has-store-pickup-shipment]').attr('has-store-pickup-shipment', (data.Bopis && data.Bopis.HasStorePickupShipment) || false);
18158			$('[has-store-pickup-shipment-only]').attr('has-store-pickup-shipment-only', (data.Bopis && data.Bopis.HasStorePickupShipmentOnly) || false);
18159	    	$(document).trigger('CartData.Update', data);
18160
18161	    	updateCartBuffer['CartData'] = data;
18162	    	if (data.Bopis) {
18163	    		updateCartBuffer['Bopis'] = data.Bopis;
18164	    	}
18165	    	
18166	    	if (!data.IsGlobaleValidOrInactive) {
18167	    		$('.section-checkout').addClass('global-e-validation-error');
18168	    		$('.section-checkout').find('.cart-errors').addClass('active');
18169	    	} else {
18170	    		$('.section-checkout').removeClass('global-e-validation-error');
18171	    		$('.section-checkout').find('.cart-errors').removeClass('active');
18172	    	}
18173
18174	    	if (data.ShippingRestrictions && data.ShippingRestrictions.Status == 'shippingRestrictionsError') {
18175				if (isCartPage) {
18176					$('.opclogin-buttons').addClass('hide');
18177				} else {
18178					$('.payment-methods-section').hide();
18179				}
18180			} else {
18181				if (isCartPage) {
18182					$('.opclogin-buttons').removeClass('hide');
18183					$('.guest-checkout .error-form').remove();
18184				} else {
18185					$('.payment-methods-section').show();
18186				}
18187			}
18188	    	
18189	    	handleOrderTotal(triggerElementName, options, isCartPage);
18190	    }
18191	});
18192}
18193
18194function handleOrderTotal(triggerElementName, options, isCartPage) {
18195	updateCartBuffer['PaymentInstrumentHtml'] = null;
18196	var removeAction = '<a class="remove">Remove</a>';
18197	
18198	if (updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length > 0) {
18199		updateCartBuffer['PaymentInstrumentHtml'] = '';
18200	
18201		for (var paymentInstrumentKey in updateCartBuffer['CartData'].OrderTotal.paymentInstruments) {
18202			var paymentInstrument = updateCartBuffer['CartData'].OrderTotal.paymentInstruments[paymentInstrumentKey];
18203			var paymentInstrumentUUIDAttribute = 'uuid="' + paymentInstrument.UUID + '"';
18204			
18205			switch (paymentInstrument.paymentMethod.Id) {
18206				case PaymentMethodConstants.RewardsCredit:
18207		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
18208		    			'<span class="label"><span class="name">B Rewarded</span>&nbsp;<span class="number">Reward</span></span>' +
18209		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
18210		    		'</div>';
18211		    	break;
18212			
18213				default:
18214		    		updateCartBuffer['PaymentInstrumentHtml'] += '<div ' + paymentInstrumentUUIDAttribute + ' payment-method="' + paymentInstrument.paymentMethod.Id + '" allow-remove="' + paymentInstrument.allowRemove + '">' +
18215		    			'<span class="label"><span class="name">' + paymentInstrument.paymentMethod.Name + '</span>&nbsp;<span class="number">' + paymentInstrument.numberLastFour + '</span>' + removeAction + '</span>' +
18216		    			'<span class="value">' + paymentInstrument.amountDisplay + '</span>' +
18217		    		'</div>';
18218		    	break;
18219		    }
18220		}
18221	} else {
18222		$(document).trigger('GiftCard.Clear');
18223		$(document).trigger('RewardsCredit.Clear');
18224	}
18225	
18226	if (updateCartBuffer['CartData'].OrderTotal.balance < updateCartBuffer['CartData'].OrderTotal.total) {
18227		updateCartBuffer['BalanceDueHtml'] = '<span class="label">Balance Due</span>' +
18228			'<span class="value">' + updateCartBuffer['CartData'].OrderTotal.balanceDisplay + '</span>';
18229	}
18230	
18231	updateCartRender(triggerElementName, options, isCartPage);
18232}
18233
18234function updateCartRender(triggerElementName, options, isCartPage) {
18235	if (updateCartBuffer['Cart'] && (isCartPage || (updateCartBuffer['OrderSummaryCallout'] && updateCartBuffer['CartData']))) {
18236		var $basketContainerHtml = $(updateCartBuffer['Cart']);
18237	
18238		if (updateCartBuffer['CartData'] && updateCartBuffer['CartData'].OrderTotal) {
18239			$basketContainerHtml.find('.order-total-price .value').html(updateCartBuffer['CartData'].OrderTotal.totalDisplay);
18240	    	$('.payment-section .order-total-price .value').html(updateCartBuffer['CartData'].OrderTotal.totalDisplay);
18241		
18242			if (updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length > 0) {
18243				$basketContainerHtml.find('.order-totals-table').attr('payments-applied', updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length);
18244	    		$('.payment-section').attr('payments-applied', updateCartBuffer['CartData'].OrderTotal.paymentInstruments.length);
18245	    	} else {
18246	    		$basketContainerHtml.find('.order-totals-table').removeAttr('payments-applied');
18247	    		$('.payment-section').removeAttr('payments-applied');
18248	    	}
18249		
18250			$basketContainerHtml.find('.order-totals-table').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
18251			$('#wrapper').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
18252	    	$('.payment-section').attr('balance-due', updateCartBuffer['CartData'].OrderTotal.balance);
18253		
18254			if (updateCartBuffer['CartData'].OrderTotal.balance > 0) {
18255				$('.payment-method-options').show();
18256				$('.payment-section-footer[data-method="PayPal"] button').hide();
18257			} else {
18258				$('.payment-method-options').hide();
18259				$('.payment-section-footer[data-method="PayPal"] button').show();
18260			}
18261		
18262	    	if (updateCartBuffer['BalanceDueHtml']) {	
18263	    		$basketContainerHtml.find('.order-balance-due').html(updateCartBuffer['BalanceDueHtml']);
18264	    		$('.payment-section .order-balance-due').html(updateCartBuffer['BalanceDueHtml']);    		
18265	    	} else {
18266	    		$('.payment-section .order-balance-due').html('');
18267	    	}
18268	    	
18269	    	if (updateCartBuffer['PaymentInstrumentHtml']) {
18270	    		$basketContainerHtml.find('.order-total-payments').html(updateCartBuffer['PaymentInstrumentHtml']);
18271	    		$('.payment-section .order-total-payments').html(updateCartBuffer['PaymentInstrumentHtml']);
18272	    	} else {
18273	    		$('.payment-section .order-total-payments').html('');
18274	    	}
18275		}
18276
18277		$('.accordion.coupon .cart-coupon-code').html($basketContainerHtml.find('.cart
18277-coupon-code').first().html());
18278		$('.basket-container').html($basketContainerHtml);
18279		
18280		if (updateCartBuffer['CartData']) {
18281			var orderTotalForAffirm = updateCartBuffer['CartData'].OrderTotal.balance;
18282			if (!orderTotalForAffirm && updateCartBuffer['CartData'].OrderTotal.adjustedMerchandizeTotalPrice) {
18283				orderTotalForAffirm = updateCartBuffer['CartData'].OrderTotal.adjustedMerchandizeTotalPrice;
18284			}
18285			
18286			if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui && orderTotalForAffirm >= updateCartBuffer['CartData'].AffirmMinimumOrderTotal) {
18287				$('.affirm-product-modal').attr('data-amount', orderTotalForAffirm * 100);
18288				affirm.ui.refresh();
18289			} else {
18290				$('.affirm-product-modal').attr('data-amount', 5000);
18291			}
18292		}
18293
18294		if ($('.basket-container').find('.product-availability-list .not-available').length > 0) {
18295			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').attr('disabled', 'disabled');
18296			$('#errorBoxOPC').remove();
18297			$('#submitOPCPayment').after('<div id="errorBoxOPC" class="p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>' + Resources.Validation.Messages.Products.Availability + ':</b> ' + Resources.Validation.Messages.Products.AvailabilityExceeded + '</div>');
18298		} else if (updateCartBuffer['Bopis'] && updateCartBuffer['Bopis'].NumberOfBopisStoresInCart > updateCartBuffer['Bopis'].MaximumNumberOfBopisStoresInCart) {
18299			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').attr('disabled', 'disabled');
18300			$('#errorBoxOPC').remove();
18301			$('#submitOPCPayment').after('<div id="errorBoxOPC" class="p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>' + Resources.Validation.Messages.General.CartLimit + ':</b> ' + Resources.Validation.Messages.Bopis.MaximumStoresExceeded + '</div>');
18302		} else {
18303			$('#submitOPCPayment, #submitOPCPaymentPrivateLabel').removeAttr('disabled');
18304			$('#errorBoxOPC').remove();
18305		}
18306		
18307		if (updateCartBuffer['OrderSummaryCallout']) {	
18308			$('.callout-ordersummary').html(updateCartBuffer['OrderSummaryCallout']);
18309		}
18310
18311	    if ($('.banner-cart-holder .btn-minicart').length > 0) {
18312			$.ajax({url : Urls.cartMiniCart, data : {desktop: '1'}, dataType: 'html', success : function(data) {
18313	            $('.banner-cart-holder').html(data);
18314	        }});
18315			
18316	    	$.ajax({url : Urls.cartMiniCart, dataType: 'html', success : function(data) {
18317	    		var parentMiniCart = $(".top-banner-inner-links #mini-cart");
18318	    		
18319	    		if (parentMiniCart.length > 0) {
18320	    			parentMiniCart.html(data);
18321	    		}
18322	        }});
18323		}
18324	    
18325        $.ajax({url: Urls.cartItemQuantity, data: {desktop: '1'}, dataType: 'html', success: function (data) {
18326        	$('.confirmation-message .item-count').html(data);
18327	        
18328	        var quantity = parseInt($('.confirmation-message .item-count').find('span').text(), 10);
18329	        if (!quantity) {
18330	            $('.checkout-buttons.mobile-only').hide();	            
18331			}
18332		}});
18333		
18334	    
18335	    if (triggerElementName.indexOf('deleteProduct') > -1) {
18336	    	$(document).trigger('refresh-required');
18337	    }
18338
18339		if (!options || !options.EventTriggers || options.EventTriggers['update-summary']) {
18340			$(document).trigger('update-summary');
18341		}
18342		
18343		if (!options || !options.EventTriggers || options.EventTriggers['update-shipping']) {
18344			$(document).trigger('update-shipping');
18345		}
18346	    
18347	    $('[minimum-cart-total], [maximum-cart-total]').each(function(index, element) {
18348	    	var $this = $(this);
18349	    	var paymentMethodCartTotalMinimum = parseFloat($this.attr('minimum-cart-total'));
18350	    	var paymentMethodCartTotalMaximum = parseFloat($this.attr('maximum-cart-total'));
18351	    
18352	    	var isCartTotalMinimumValid = false;
18353	    	if (isNaN(paymentMethodCartTotalMinimum) || updateCartBuffer['CartData'].OrderTotal.balance >= paymentMethodCartTotalMinimum) {
18354	    		isCartTotalMinimumValid = true;
18355	    	}
18356	    	
18357	    	var isCartTotalMaximumValid = false;
18358	    	if (isNaN(paymentMethodCartTotalMaximum) || updateCartBuffer['CartData'].OrderTotal.balance <= paymentMethodCartTotalMaximum) {
18359	    		isCartTotalMaximumValid = true;
18360	    	}
18361	    	
18362	    	if (isCartTotalMinimumValid && isCartTotalMaximumValid) {
18363	    		$this.closest('.payment-method-option').attr('valid-for-balance-due', 'true');
18364	    	} else {
18365	    		$this.closest('.payment-method-option').attr('valid-for-balance-due', 'false');
18366	    	}
18367	    });
18368		
18369	    if (updateCartBuffer['CartData'] && !updateCartBuffer['CartData'].HasPhysicalShipment && $('.single.shipping-address-type-selector input
18369:checked').val() != SiteConstants.AddressTypes.Customer) {
18370	    	$('.single.shipping-address-type-selector input[value="' + SiteConstants.AddressTypes.Customer + '"]').click();
18371	    }
18372		
18373		tooltip.init({
18374			items: '.shipping-method-tooltip',
18375			tooltipClass: 'shipping-method-tooltip'
18376		}, $('[shipping-method]'));
18377
18378		tooltip.init({
18379			items: '.tooltip'
18380		});
18381
18382		triggerSrFocus();
18383		$(document).trigger('QuantityStepper.TargetFocus');
18384	    $(document).trigger('Cart.UpdateSuccess');
18385	}
18386}
18387
18388function triggerSrFocus() {
18389	$('[role="alert"]').last().focus();
18390}
18391
18392function submitPayment(data) {
18393	$submitPayment.loading({
18394		IsImageShown: false,
18395		Label: Resources.Loading.Checkout.PlacingOrder
18396	});
18397	$submitPayment.attr('disabled', true);
18398	$('#errorBoxOPC').remove();
18399
18400	$checkoutForm.validate().form();
18401	
18402	if ($checkoutForm.valid()) {
18403		setOPCPaymentData($checkoutForm, $submitPayment.attr('name')).then(function(response) {
18404			var data;
18405			try {
18406				data = JSON.parse(response);
18407			} catch (e) {
18408				data = response;
18409			}
18410
18411			if (data.hasOwnProperty('status') && data.status === 'success') {
18412				window.location.href = Urls.confirmationPage;
18413			} else if (data.hasOwnProperty('status') && data.status === 'affirmRedirect') {
18414				var _affirm_config = {
18415					public_api_key: data.affirm.PublicApiKey,
18416					script: data.affirm.Script
18417				};
18418				(function(l,g,m,e,a,f,b){var d,c=l[m]||{},h=document.createElement(f),n=document.getElementsByTagName(f)[0],k=function(a,b,c){return function(){a[b]._.push([c,arguments])}};c[e]=k(c,e,"set");d=c[e];c[a]={};c[a]._=[];d._=[];c[a][b]=k(c,a,b);a=0;for(b="set add save post open empty reset on off trigger ready setProduct".split(" ");a<b.length;a++)d[b[a]]=k(c,e,b[a]);a=0;for(b=["get","token","url","items"];a<b.length;a++)d[b[a]]=function(){};h.async=!0;h.src=g[f];n.parentNode.insertBefore(h,n);delete g[f];d(g);l[m]=c})(window,_affirm_config,"affirm","checkout","ui","script","ready");
18419				
18420				affirm.checkout(JSON.parse(data.affirm.AffirmCheckout));
18421				affirm.checkout.post();
18422				affirm.ui.ready(
18423					function() {
18424						affirm.ui.error.on("close", function(){
18425							// window.location.replace(data.affirm.RedirectUrl);
18426						});
18427					}
18428				);
18429			} else if (data.hasOwnProperty('status') && data.status === 'shippingRestrictionsError'){
18430				var popup = require('../popup');
18431				var payload = JSON.parse(data.payload);
18432
18433				var list = $.map(payload.list, function (el) {
18434					return '<li>' + el.name + ':<br>' + el.message + '</li>';
18435				}).join('');
18436				
18437				popup.init({
18438					title: payload.title,
18439					content: payload.message + '<ul class="shipping-restrictions-popup__list">' + list + '</ul>',
18440				}).showPopup();
18441			} else if (data.hasOwnProperty('status') && data.status === 'ValidationError') {
18442				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
18443				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>Validation Error:</b> ' + message + '</div>');
18444			} else {
18445				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
18446				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>Payment Declined:</b> ' + message + '</div>');
18447			}
18448			
18449			$submitPayment.loading('destroy');
18450			$submitPayment.attr('disabled', false);
18451		}).catch(function(response) {
18452			if (response.status == 302 && response.responseText) {
18453				location.href = response.responseText;
18454			}
18455		});
18456	} else {
18457		$submitPayment.loading('destroy');
18458		$submitPayment.attr('disabled', false);
18459	}
18460}
18461
18462var setOPCPaymentData = function ($form, name) {
18463	var payload = $form.serialize() + '&' + name + '=' + name;
18464	var $checkoutVariables = $('.button-and-terms input[type="hidden"]');
18465	$checkoutVariables.each(function() {
18466		var $this = $(this);
18467		payload += '&' + $this.attr('name') + '=' + $this.val();
18468	});
18469
18470    return TPromise.resolve($.ajax({
18471        type: 'POST',
18472        url: util.ajaxUrl(Urls.setOpcAddress),
18473        data: payload
18474    }));
18475};
18476
18477function searchRewards() {
18478	if (dataBuffer['RewardsAccountSearchRequest'] && dataBuffer['RewardsAccountSearchRequest'].readyState !== 4) {
18479		dataBuffer['RewardsAccountSearchRequest'].abort();
18480		dataBuffer['RewardsAccountSearchRequest'] = null;
18481	}
18482	$rewardsApplied.html('');
18483
18484	var payload = {
18485		EmailAddress: $rewardsForm.find('#dwfrm_billing_rewards_rewardsEmail').val(),
18486		PhoneNumber: $rewardsForm.find('.field.phonenumber').val(),
18487		ZipCode: $rewardsForm.find('.field.zipcode').val(),
18488	}
18489
18490	if ($rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val()) {
18491		payload.RewardsNumber = $rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val();
18492	}
18493
18494	$('.btn.search-echo').loading({
18495		IsLabelShown: false,
18496		BackgroundColor: 'transparent'
18497	});
18498
18499	dataBuffer['RewardsAccountSearchRequest'] = $.ajax({
18500		type: 'POST',
18501		url: Urls.searchForRewards,
18502		contentType: 'application/json',
18503		dataType: 'json',
18504		data: JSON.stringify(payload),
18505		success: function(data) {
18506			renderRewardsData(data);
18507		},
18508		complete: function(xhr) {
18509			if ($('.btn.search-echo').attr('loading')) {
18510				$('.btn.search-echo').loading('destroy');
18511			}
18512		}
18513	});
18514}
18515
18516function renderRewardsData(data) {
18517	if (data.Match && data.Rewards) {
18518		if (data.Rewards.rewardsNumber) {
18519			$rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val(data.Rewards.rewardsNumber);
18520		} else {
18521			$rewardsForm.find('#dwfrm_billing_rewards_rewardsNumber').val('');
18522		}
18523		if (data.Rewards.email) {
18524			$rewardsForm.find('#dwfrm_billing_rewards_rewardsEmail').val(data.Rewards.email);
18525		} else {
18526			$rewardsForm.find('#dwfrm_billing_rewards_rewardsEmail').val('');
18527		}
18528		if (data.Rewards.phone) {
18529			$rewardsForm.find('.field.phonenumber').val(data.Rewards.phone);
18530		} else {
18531			$rewardsForm.find('.field.phonenumber').val('');
18532		}
18533		if (data.Rewards.zipValue) {
18534			$rewardsForm.find('.field.zipcode').val(data.Rewards.zipValue);
18535		} else {
18536			$rewardsForm.find('.field.zipcode').val('');
18537		}
18538
18539		if (data.Match.RewardsEmail) {
18540			$rewardsForm.find('.rewards-email-container').attr('match', 'true');
18541		} else {
18542			$rewardsForm.find('.rewards-email-container').attr('match', 'false');
18543		}
18544		if (data.Match.RewardsPhone) {
18545			$rewardsForm.find('.input-container.phone').attr('match', 'true');
18546		} else {
18547			$rewardsForm.find('.input-container.phone').attr('match', 'false');
18548		}
18549		if (data.Match.RewardsZip) {
18550			$rewardsForm.find('.input-container.zip').attr('match', 'true');
18551		} else {
18552			$rewardsForm.find('.input-container.zip').attr('match', 'false');
18553		}
18554		
18555		var rewardsRowTemplate = _.template($('#template-rewards-credit-row-echo').html(), TemplateConstants.Lodash.Settings);
18556		var status = '';
18557		var message = '';
18558		if (parseFloat(data.Rewards.creditAmount) <= 0) {
18559			status = 'message';
18560			message = 'no rewards available';
18561		} else if ($rewardsForm.closest('[is-applied]').attr('is-applied') == 'true') {
18562			status = 'applied';
18563		} else {
18564			status = 'pending';
18565		}
18566
18567		var additionalData = '<table class="rewards-information">' +
18568								'<tr><td>Address</td><td>' + data.Rewards.address + '</td></tr>' +
18569								'<tr><td>City</td><td>' + data.Rewards.city + '</td></tr>' +
18570								'<tr><td>State</td><td>' + data.Rewards.state + '</td></tr>' +
18571								'<tr><td>Zip</td><td>' + data.Rewards.zipValue + '</td></tr>' +
18572								'<tr><td>Country</td><td>' + data.Rewards.country + '</td></tr>' +
18573								'<tr><td>Phone</td><td>' + data.Rewards.phone + '</td></tr>' +
18574								(data.Rewards.rewardsStatus ? '<tr><td>Rewards Status</td><td>' + data.Rewards.rewardsStatus.charAt(0).toUpperCase() + data.Rewards.rewardsStatus.slice(1) + '</td></tr>' : '') +
18575							'</table><table class="rewards-points">' +
18576								'<tr><td>Points</td><td>' + data.Rewards.pointBalance + '</td></tr>' +
18577							'</table>';
18578
18579		$rewardsApplied.html(rewardsRowTemplate({
18580			Status: status,
18581			RewardsNumber: data.Rewards.rewardsNumber,
18582			Name: data.Rewards.fullName,
18583			Email: data.Rewards.email,
18584			RewardsAvailable: data.Rewards.creditAmountFormatted.replace('.00', ''),
18585			RewardsExpirationDate: data.Rewards.creditExpirationDate || 'n/a',
18586			Message: message,
18587			AdditionalData: additionalData
18588		}));
18589		if ($rewardsAccordion.find('header .rewards-points').length) {
18590			$rewardsAccordion.find('header .rewards-points').text(data.Rewards.pointBalance);
18591		}
18592
18593		$rewardsAccordion.attr('rewards-found', 'true');
18594		$rewardsForm.find('.btn.reset-echo').removeAttr('disabled');
18595		if (!$('.accordion.rewards .accordion-head').hasClass('active')) {
18596			$('.accordion.rewards .accordion-head').addClass('active');
18597		}
18598	} else {
18599		$rewardsAccordion.removeAttr('rewards-found');
18600		$rewardsApplied.html('<p class="no-results">no rewards found</p>');
18601	}
18602}
18603
18604exports.init = function (optionsParameter) {
18605	options = optionsParameter;
18606
18607	if (options && options.Lob && options.Lob.AddressVerification.Enabled) {
18608		AddressVerificationDialog = require('../../../../app_storefront_core/cartridge/js/Lob/AddressVerification/AddressVerificationDialog');
18609	}
18610
18611	if (options && options.Lob && options.Lob.AddressAutocomplete.Enabled) {
18612		AddressAutocompleteDialog = require('../../../../app_storefront_core/cartridge/js/Lob/AddressAutocomplete/AddressAutocompleteDialog');
18613	}
18614
18615	GiftCard.initialize($.extend({ LoadingIndicator: {
18616		IsLabelShown: false,
18617		BackgroundColor: 'transparent'
18618	}}, options));
18619
18620	RewardsCredit.initialize($.extend({ LoadingIndicator: {
18621		IsLabelShown: false,
18622		BackgroundColor: 'transparent'
18623	}}, options));
18624
18625	ApplePay.initialize(options);
18626
18627    initialize(options);
18628};
18629},{"../../../../app_storefront_core/cartridge/js/Lob/AddressAutocomplete/AddressAutocompleteDialog":1,"../../../../app_storefront_core/cartridge/js/Lob/AddressVerification/AddressVerificationDialog":2,"../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper":112,"../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants":113,"../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../components/LoadingIndicator":25,"../popup":83,"../tooltip":101,"../util":102,"./checkout/ApplePay":53,"./checkout/GiftCardPaymentPanel":54,"./checkout/RewardsCredit":57,"lodash":133,"promise":134}],68:[function(require,module,exports){
18630'use strict';
18631
18632var addProductToCart = require('./product/addToCart'),
18633    ajax = require('../ajax'),
18634    page = require('../page'),
18635    productTile = require('../product-tile'),
18636    quickview = require('../quickview');
18637
18638/**
18639 * @private
18640 * @function
18641 * @description Binds the click events to the remove-link and quick-view button
18642 */
18643function initializeEvents() {
18644    $('#compare-table').on('click', '.remove-link', function (e) {
18645        e.preventDefault();
18646        ajax.getJson({
18647            url: this.href,
18648            callback: function () {
18649                page.refresh();
18650            }
18651        });
18652    })
18653    .on('click', '.open-quick-view', function (e) {
18654        e.preventDefault();
18655        var url = $(this).closest('.product').find('.thumb-link').attr('href');
18656        quickview.show({
18657            url: url,
18658            source: 'quickview'
18659        });
18660    });
18661
18662    $('#compare-category-list').on('change', function () {
18663        $(this).closest('form').submit();
18664    });
18665}
18666
18667exports.init = function () {
18668    productTile.init();
18669    initializeEvents();
18670    addProductToCart();
18671};
18672
18673},{"../ajax":3,"../page":46,"../product-tile":84,"../quickview":88,"./product/addToCart":72}],69:[function(require,module,exports){
18674'use strict';
18675
18676var	util = require('../util');
18677var page = require('../page');
18678var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
18679
18680function initializeEvents(){
18681};
18682
18683exports.init = function () {
18684    initializeEvents();    
18685};
18686
18687},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../page":46,"../util":102}],70:[function(require,module,exports){
18688'use strict';
18689
18690var quickview = require('../quickview');
18691var bonusProductsView = require('../bonus-products-view');
18692var TPromise = require('promise');
18693var util = require('../util');
18694var addresses = require('./checkout/opcaddress');
18695var formPrepare = require('./checkout/formPrepare');
18696var cart = require('./cart');
18697var opcpayment = require('./checkout/opcpayment');
18698var loadingIndicator = require('../components/LoadingIndicator');
18699var GiftCard = require('./checkout/GiftCardPaymentPanel');
18700var GoogleTagManagerClient = require('../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
18701var PaymentVoucherPaymentPanel;
18702var WorkforceVoucherPaymentPanel;
18703var recaptcha = require('../components/Recaptcha');
18704var RewardsCredit = require('./checkout/RewardsCredit');
18705var LoyaltyCredit = require('./checkout/LoyaltyCredit');
18706var CheckoutConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants');
18707var PaymentMethodConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
18708var BopisProductLineItemHelper = require('../bopis/components/ProductLineItemHelper');
18709var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
18710
18711var cartItemsFormSelector = 'form#cart-items-form';
18712var billingAddressFormSelector = '#billingAddressForm';
18713var $billingAddressForm = $(billingAddressFormSelector);
18714var shippingAddressFormSelector = '#shippingAddressForm';
18715var $shippingAddressForm = $(shippingAddressFormSelector);
18716var paymentSectionSelector = '.form-checkout.payment-section';
18717var $paymentSection = $(paymentSectionSelector);
18718var paymentFormSelector = '#paymentSection';
18719var $paymentForm = $(paymentFormSelector);
18720var creditCardSaveCheckboxContainerSelector = 'div[field="saveCard"]';
18721var $creditCardSaveCheckboxContainer = $(creditCardSaveCheckboxContainerSelector);
18722var creditCardSaveCheckboxSelector = '#dwfrm_billing_paymentMethods_credit
18722Card_saveCard';
18723var $creditCardSaveCheckbox = $(creditCardSaveCheckboxSelector);
18724var submitPaymentSelector = '#submitOPCPayment';
18725var $submitPayment = $(submitPaymentSelector);
18726var basketContainerSelector = '.basket-container';
18727var $basketContainer = $(basketContainerSelector);
18728
18729var placeOrderRecaptcha;
18730var options = {};
18731var loaded = {};
18732
18733// Credit Card inputs & formatting
18734var creditCardListSelector = '.payment-card-list select, .payment-card-list input[type="radio"]';
18735var $creditCardList = $(creditCardListSelector);
18736var savedCreditCardListSelector = '#creditCardList';
18737var $savedCreditCardList = $(savedCreditCardListSelector);
18738var privateLabelCardNumberInputSelector = 'input[name*="_privateLabelCredit_number"]';
18739var creditCardNumberInputSelector = 'input[name*="_creditCard_number"]';
18740var $creditCardNumberInput = $(creditCardNumberInputSelector);
18741var $creditCardImage = $('<div />', { 'class': 'cc-image' });
18742
18743/**
18744 * @description Events should be divided to modules. This allows reinitialize events by sections after partial reload 
18745 * @private
18746 * @function
18747 * @description Initializes events for the gift registration
18748 */
18749function initializeEvents(options) {
18750	initializeCartEvents(options);
18751	initializePaymentMethodsEvents();
18752	initializeCheckoutEvents();
18753	
18754	renderRecaptcha();
18755	$(document).on('Recaptcha.Loaded', function(event) {
18756		if (!loaded.Recaptcha) {
18757			loaded.Recaptcha = true;
18758	        renderRecaptcha();
18759	    }
18760	});
18761}
18762
18763function initializeCartEvents(options) {
18764	$(document).on('click', 'a.add-to-wishlist', function (e) {
18765    	/*remove next line to use default dw wishlist functionality*/
18766    	return false;
18767    	e.preventDefault();
18768    	
18769    	if (Customer.authenticated == true) {
18770    		// post form here
18771    	} else {
18772    		$('html, body').animate({ scrollTop: $('.login-form').eq(0).offset().top}, 1000);
18773    	}
18774    });
18775	
18776	if (SitePreferences.ALTERNATE_PICKUP_ENABLED) {
18777		renderAlternatePickup();
18778	}
18779
18780    $paymentForm.on('change', '[name$="_creditCard_type"]', function() {
18781    	$submitPayment.removeAttr('disabled');
18782    	$(this).closest('div.form-row').find('div.field-wrapper.error').remove();
18783    });
18784
18785    $(document).on('click', submitPaymentSelector, function(e) {
18786    	e.preventDefault();
18787
18788    	if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForPlaceOrder) {
18789			submitPayment();
18790		}
18791    });
18792    
18793    $(document).on('Checkout.SubmitPayment', function (event, data) {
18794		if ((options && options.Recaptcha && options.Recaptcha.EnabledForPlaceOrder) || (data && data.SubmitPaymentForBopis)) {
18795			submitPayment(data);
18796		}
18797	});
18798    
18799    var getCreditCardInfo = function (id) {
18800        return TPromise.resolve($.ajax({
18801            type: 'GET',
18802            url: util.ajaxUrl(Urls.billingGetCreditCard),
18803            data: { creditCardUUID: id }
18804        }));
18805    };
18806    
18807    $(document).on('change', savedCreditCardListSelector, function() {
18808    	var creditCardUUID = $(this).val();
18809    	
18810    	if (creditCardUUID.length <= 0) {
18811    		return;
18812    	}
18813    	
18814    	getCreditCardInfo(creditCardUUID).then(function (data) {
18815			if (data && data.hasOwnProperty('maskedNumber')) {
18816				var $paymentMethodGroup = $('[payment-method-group="' + PaymentMethodConstants.CreditCard + '"]');
18817				var $paymentMethod = $paymentMethodGroup.find('.payment-method[payment-method="' + data.paymentMethod + '"]');
18818
18819				var paymentMethodRadioToSelect = data.paymentMethod == PaymentMethodConstants.CreditCard ? data.type : data.paymentMethod;
18820				$paymentMethodGroup.find('input[type="radio"][value="' + paymentMethodRadioToSelect + '"]').first().prop('checked', true).trigger('change', { doReset: false });
18821				
18822				if ($paymentMethod.find('[id*="_creditCard_owner"]').length > 0 && data.hasOwnProperty('holder')) {
18823					$paymentMethod.find('[id*="_creditCard_owner"]').val(data.holder).valid();
18824				}
18825
18826				$('.pt_onepcheckout input[name*="_creditCard_type"]').val(data.type);
18827				$paymentMethod.find('[id*="_number"]').val(data.maskedNumber).valid();
18828				$paymentMethod.find(creditCardNumberInputSelector).val(data.maskedNumber);
18829				$creditCardNumberInput.data('isMaskedSavedCard', true);
18830				
18831				if ($paymentMethod.find('[id*="_creditCard_expiration_month"]').length > 0 && data.hasOwnProperty('expirationMonth')) {
18832					$paymentMethod.find('[id*="_creditCard_expiration_month"]').val(data.expirationMonth).trigger('change');
18833				}
18834				
18835				if ($paymentMethod.find('[id*="_creditCard_expiration_year"]').length > 0 && data.hasOwnProperty('expirationYear')) {
18836					$paymentMethod.find('[id*="_creditCard_expiration_year"]').val(data.expirationYear).trigger('change');
18837				}
18838				
18839				if ($paymentMethod.find('[id*="_creditCard_cvn"]').length > 0) {
18840					$paymentMethod.find('[id*="_creditCard_cvn"]').val('');
18841				}
18842
18843				$creditCardSaveCheckboxContainer.hide();
18844			}
18845		}.bind(this));	
18846    });
18847    
18848	if ($savedCreditCardList.length > 0 && ($('.pt_onepcheckout input[name*="_selectedPaymentMethodID"]').val() == PaymentMethodConstants.CreditCard || $('.pt_onepcheckout input[name*="_selectedPaymentMethodID"]').val() == PaymentMethodConstants.AllianceData)) {
18849		$savedCreditCardList.find('option').eq(1).prop('selected', true);
18850		$savedCreditCardList.trigger('change');
18851		$creditCardSaveCheckboxContainer.hide();
18852	} else {
18853		$creditCardSaveCheckboxContainer.show();
18854	}
18855	
18856    addresses.init(options);
18857    cart.init(options);
18858}
18859
18860function initializeCheckoutEvents() { // Events that are order-level or checkout-level without being specific to payments or cart
18861	
18862	// Transactional SMS
18863	if (SitePreferences.TRANSACTIONAL_SMS) {
18864		if (SitePreferences.JQUERY_MASK) {
18865			$('.order-transactional-sms-phone [id*="transactionalsms_smsPhone"]').mask('(000) 000-0000');
18866		}
18867
18868		var $smsPhoneField = $paymentForm.find('input[id*="transactionalsms_smsPhone"]');
18869		$smsPhoneField.rules('add', {
18870			required: {
18871				depends: function(element) {
18872					// Returns true if the checkbox is checked, making the mobile phone required.
18873					// Otherwise, returns false, and the field is not required.
18874					return $('.order-transactional-sms [id*="transactionalsms_smsOptin"]').is(":checked");
18875				}
18876			},
18877			messages: {
18878				required: "Please enter a valid phone number to receive order updates."
18879			}
18880		});
18881		$smsPhoneField.attr('inputmode','numeric');
18882
18883		// Use 'active' class to check/uncheck the opt-in box here since we can't use conditionals in XML files and checkbox is defaulted to fal
18883se
18884		if ($('.order-transactional-sms-container').hasClass('active')) {
18885			$('.order-transactional-sms input[id*="transactionalsms_smsOptin"]').prop('checked', true);
18886			getBillingPhoneForSMS();
18887		} else {
18888			$('.order-transactional-sms input[id*="transactionalsms_smsOptin"]').prop('checked', false);
18889		}
18890
18891		$(document).off('click', '.order-transactional-sms .transactional-sms-toggle').on('click', '.order-transactional-sms .transactional-sms-toggle', function (e) {
18892			$(this).closest('.order-transactional-sms').find('input[id*="transactionalsms_smsOptin"]').trigger('click');
18893		});
18894
18895		$(document).off('change', '.order-transactional-sms input[id*="transactionalsms_smsOptin"]').on('change', '.order-transactional-sms input[id*="transactionalsms_smsOptin"]', function (e) {
18896			if ($(this).is(':checked')) {
18897				$(this).closest('.order-transactional-sms-container').addClass('active');
18898				if ($('.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]').val().trim() <= 0) {
18899					getBillingPhoneForSMS();
18900				}
18901			} else {
18902				$(this).closest('.order-transactional-sms-container').removeClass('active');
18903				var $smsPhone = $('.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]');
18904				var $errorSpan = $smsPhone.next('span.error'); 
18905				$smsPhone.removeClass("error").val(''); // remove error and clear field if toggled off
18906				$errorSpan.remove();
18907			}
18908		});
18909
18910		$(document).off('change', '.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]').on('change', '.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]', function (e) {
18911			$paymentForm.validate().element('input[id*="transactionalsms_smsPhone"]');
18912		});
18913	}
18914	
18915}
18916
18917function getBillingPhoneForSMS() {
18918	var billingPhone = $('#billingAddressForm').find('[id*="_phone"]').val();
18919	if (billingPhone) {
18920		$('.order-transactional-sms-phone input[id*="transactionalsms_smsPhone"]').val(billingPhone).trigger('input');
18921	}
18922}
18923
18924function renderAlternatePickup() {
18925	$(document).off('click', 'button.alt-pickup-button').on('click', 'button.alt-pickup-button', function (e) {
18926    	e.preventDefault();
18927		$('#alt-pickup-modal').addClass('active-modal').show();
18928		$('fieldset span').removeClass('error-msg');
18929		$('.one-required').removeClass('error');
18930    });
18931    
18932    $(document).off('click', '#alt-pickup-modal .close-modal').on('click', '#alt-pickup-modal .close-modal', function (e) {
18933    	e.preventDefault();
18934		$('#alt-pickup-modal').removeClass('active-modal').hide();
18935    });
18936    
18937    $(document).on('focus', 'input[name="firstName"]', function () {
18938		$('.firstname-error').removeClass('error-msg');
18939    });
18940    
18941    $(document).on('focus', 'input[name="lastName"]', function () {
18942		$('.lastname-error').removeClass('error-msg');
18943    });
18944    
18945    $(document).on('focus', 'input[name="email"]', function () {
18946		$('.email-error').removeClass('error-msg');
18947		$('.one-required').removeClass('error');
18948    });
18949    
18950    $(document).on('focus', 'input[name="phone"]', function () {
18951		$('.phone-error').removeClass('error-msg');
18952		$('.one-required').removeClass('error');
18953    });
18954    
18955    $(document).off('click', 'button#altPickupSubmit').on('click', 'button#altPickupSubmit', function (e) {
18956    	e.preventDefault();
18957		
18958    	var isFirstNameValid = true;
18959		var firstName = $('input[name="firstName"]').val();
18960		var regexName = /^[a-z ,.'-]+$/i;
18961		if (firstName.length == 0 || firstName.match(regexName) == null) {
18962			isFirstNameValid = false;
18963			$('.firstname-error').addClass('error-msg');
18964		}
18965		
18966		var isLastNameValid = true;		
18967		var lastName =$('input[name="lastName"]').val();
18968		if (lastName.length == 0 || lastName.match(regexName) == null) {
18969			isLastNameValid = false;
18970			$('.lastname-error').addClass('error-msg');
18971		}
18972		
18973		var isEmailValid = true;		
18974		var email = $('input[name="email"]').val();
18975		var regexEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/g;
18976		if (email.length > 0 && email.match(regexEmail) == null) {
18977			isEmailValid = false;
18978			$('.email-error').addClass('error-msg');
18979		}
18980
18981		var isPhoneValid = true;
18982		var phone = $('input[name="phone"]').val().replace(/\D/g, '');
18983		var regexPhone = /^\+?\(?([0-9][0-8][0-9])\)?[\-\. ]?([2-9][0-9]{2})[\-\. ]?([0-9]{4})(\s*x[0-9]+)?$/g;
18984		if (phone.length > 0 && phone.match(regexPhone) == null) {
18985			isPhoneValid = false;
18986			$('.phone-error').addClass('error-msg');
18987		}
18988		
18989    	if (email.length == 0 && phone.length == 0) {
18990    		$('.one-required').addClass('error');
18991    		isEmailValid = false;
18992    		isPhoneValid = false;
18993    	}
18994    	
18995    	if (!isEmailValid || !isPhoneValid) {
18996    		return;
18997    	}
18998    	
18999		if (isFirstNameValid && isLastNameValid && (isEmailValid || isPhoneValid)) {
19000			var cleanPhone = phone.replace(/\D/g, '');
19001			var shipmentID = $('.alternate-pickup').closest("form").attr('shipment');
19002			
19003			$.ajax({
19004				url: Urls.setAlternatePickup,
19005				type: "get",
19006				data: {
19007					shipmentID: shipmentID,
19008					altPickupFirstName: firstName,
19009					altPickupLastName: lastName,
19010					altPickupEmail: email,
19011					altPickupPhone: cleanPhone
19012				},
19013				success: function (response) {
19014					var data = JSON.parse(response);
19015						
19016					if (data.status == 'success') {
19017						$('span.alt-person').attr('data-firstname', firstName).attr('data-lastname', lastName).attr('data-email', email).attr('data-phone', cleanPhone);
19018						displayAlternatePickup (firstName + ' ' + lastName)
19019						$('#alt-pickup-modal').removeClass('active-modal').hide();
19020					}
19021				},
19022				error: function(jqXHR, textStatus, errorThrown) {
19023					console.log(textStatus, errorThrown);
19024				}
19025			});
19026		}
19027    });
19028}
19029
19030function displayAlternatePickup (fullName) {
19031	$('.alternate-pickup span.alt-person').text(fullName);
19032	$('span.alt-header').show();
19033	$('button.alt-pickup-button').text('UPDATE');
19034}
19035
19036function renderRecaptcha() {
19037	if (loaded.Recaptcha && $('#submitOPCPayment').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForPlaceOrder && options.Recaptcha.GoogleSiteKey && placeOrderRecaptcha == undefined) {
19038		placeOrderRecaptcha = grecaptcha.render('submitOPCPayment', {
19039	        'sitekey': options.Recaptcha.GoogleSiteKey,
19040	        'callback': submitPayment,
19041	        'badge': 'bottomleft'
19042	    });
19043	}
19044}
19045
19046function submitPaymentRecaptchaError() {
19047	if (loaded.Recaptcha && $('#submitOPCPayment').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForPlaceOrder && options.Recaptcha.GoogleSiteKey && placeOrderRecaptcha != undefined) {
19048		grecaptcha.reset(placeOrderRecaptcha);
19049		renderRecaptcha();
19050	}
19051}
19052
19053function submitPayment(data) {
19054	var balanceDue = parseFloat($paymentSection.attr('balance-due'));
19055	$submitPayment.loading({
19056		IsImageShown: false,
19057		Label: Resources.Loading.Checkout.PlacingOrder
19058	});
19059	$submitPayment.attr('disabled', true);
19060	$('#errorBoxOPC').remove();
19061
19062	$billingAddressForm.validate().form();
19063	$shippingAddressForm.validate().form();
19064
19065	// Remove CC formatting right before order is placed so server-side doesn't reject it
19066	// Don't strip formatting if it's a saved credit card
19067	if (SitePreferences.CREDIT_CARD_FORMATTING_ENABLED && SitePreferences.JQUERY_MASK) {
19068		if (!$creditCardNumberInput.data('isMaskedSavedCard') && $creditCardNumberInput.data('mask')) {
19069			$creditCardNumberInput.val($creditCardNumberInput.cleanVal());
19070		}
19071	}
19072		
19073	var $creditCardType = $paymentForm.find('[name$="_creditCard_type"]:checked').val();
19074	if (balanceDue > 0) {
19075		$paymentForm.validate().form();
19076		
19077		if (typeof $creditCardType === 'undefined') {
19078    		$paymentForm.find('[name$="_creditCard_type"]').closest('div.form-row').find('div.field-wrapper.error').remove();
19079
19080    		setTimeout(function(){
19081    			$paymentForm.find('[name$="_creditCard_type"]').closest('div.form-row').append('<div class="field-wrapper error" style="height:0px;"><span class="error">' + Resources.REQUIRED_FIELD + '</span></div>');
19082    		}, 50);
19083		}
19084	}
19085	
19086	// check first if addresses are valid
19087	var isBillingAddressValid = false;
19088	var isShippingAddressValid = false;
19089	
19090	$billingAddressForm.validate().settings.ignore = [];
19091	$shippingAddressForm.validate().settings.ignore = [];
19092	
19093	if ($billingAddressForm.valid()) {
19094		isBillingAddressValid = true;
19095	} else {
19096		$('.billing-address-data.form-info').hide();
19097		
19098		$billingAddressForm.show();
19099		$billingAddressForm.find('input.required.field.error').eq(0).focus();
19100	}
19101	
19102	if ($shippingAddressForm.valid() || ($shippingAddressForm.find('.formfield.isStore input').prop('checked') && $shippingAddressForm.find('.formfield.myStoreID input').val())) {
19103		isShippingAddressValid = true;
19104	} else {
19105		$('.shipping-address-data.form-info').hide();
19106		
19107		$shippingAddressForm.show();
19108		$shippingAddressForm.find('input.required.field.error').eq(0).focus();
19109	}
19110	
19111	var isBopisValid = false;
19112	if (data && data.SubmitPaymentForBopis) {
19113		isBopisValid = data.IsBopisInventoryValid;
19114	} else {
19115		var $bopisProductLineItems = $(cartItemsFormSelector).find('.bopis-product-line-item[sku][store]');
19116		
19117		if ($bopisProductLineItems.length > 0) {
19118			BopisProductLineItemHelper.ValidateProductLineItems($(cartItemsFormSelector).find('.bopis-product-line-item[sku][store]'), { SuccessEvent: 'Checkout.SubmitPayment' });
19119		} else {
19120			isBopisValid = true;
19121		}
19122	}
19123
19124	// Transactional SMS
19125	if (SitePreferences.TRANSACTIONAL_SMS) {
19126		var $transactionalSMSPhone = $('.order-transactional-sms-phone [id*="transactionalsms_smsPhone"]');
19127		if ($transactionalSMSPhone.data('mask')) {
19128			$transactionalSMSPhone.val($transactionalSMSPhone.cleanVal());
19129		}
19130	}
19131	
19132	if (isBillingAddressValid && isShippingAddressValid && isBopisValid && $('#shipping-method-list input:checked').length && (balanceDue == 0 || $paymentForm.valid())) {
19133		var $paymentFormToSet = $paymentForm.clone();
19134		var actionName = $submitPayment.attr('name');
19135		
19136		var selectedPaymentMethodId = $paymentForm.find('input[name*="selectedPaymentMethodID"]').val();
19137		$paymentFormToSet.find('div[payment-method]').not('div[payment-method="' + selectedPaymentMethodId + '"]').remove();
19138		
19139		$paymentFormToSet.find('select').each(function (index) {
19140			$(this).val($paymentForm.find('#' + $(this).attr('id')).val());
19141		});
19142		
19143		setOPCPaymentData($paymentFormToSet, actionName).then(function(response) {
19144			var data;
19145			try {
19146				data = JSON.parse(response);
19147			} catch (e) {
19148				data = response;
19149			}
19150
19151			if (data.hasOwnProperty('status') && data.status === 'success') {
19152				
19153				var dataObject = {
19154					value: balanceDue,
19155					payment_type: selectedPaymentMethodId
19156				}
19157				GoogleTagManagerClient.PushDataLayerForDataLayerType('AddPaymentInfo', dataObject).then(function() {
19158					if (options.CheckoutMode == 'MobileApp') {
19159						window.location.href = Urls.confirmationPageForMobileApp;
19160					} else {
19161						window.location.href = Urls.confirmationPage;
19162					}
19163				}).catch(function() {
19164					if (options.CheckoutMode == 'MobileApp') {
19165						window.location.href = Urls.confirmationPageForMobileApp;
19166					} else {
19167						window.location.href = Urls.confirmationPage;
19168					}
19169				});
19170			} else if (data.hasOwnProperty('status') && data.status === 'affirmRedirect') {
19171				var _affirm_config = {
19172					public_api_key: data.affirm.PublicApiKey,
19173					script: data.affirm.Script
19174				};
19175				(function(l,g,m,e,a,f,b){var d,c=l[m]||{},h=document.createElement(f),n=document.getElementsByTagName(f)[0],k=function(a,b,c){return function(){a[b]._.push([c,arguments])}};c[e]=k(c,e,"set");d=c[e];c[a]={};c[a]._=[];d._=[];c[a][b]=k(c,a,b);a=0;for(b="set add save post open empty reset on off trigger ready setProduct".split(" ");a<b.length;a++)d[b[a]]=k(c,e,b[a]);a=0;for(b=["get","token","url","items"];a<b.length;a++)d[b[a]]=function(){};h.async=!0;h.src=g[f];n.parentNode.insertBefore(h,n);delete g[f];d(g);l[m]=c})(window,_affirm_config,"affirm","checkout","ui","script","ready");
19176				
19177				affirm.checkout(JSON.parse(data.affirm.AffirmCheckout));
19178				affirm.checkout.post();
19179				affirm.ui.ready(
19180					function() {
19181						affirm.ui.error.on("close", function(){
19182							// window.location.replace(data.affirm.RedirectUrl);
19183						});
19184					}
19185				);
19186			} else if (data.hasOwnProperty('status') && data.status === 'shippingRestrictionsError') {
19187				var popup = require('../popup');
19188				var payload = JSON.parse(data.payload);
19189
19190				var list = $.map(payload.list, function (el) {
19191					return '<li>' + el.name + ':<br>' + el.message + '</li>';
19192				}).join('');
19193				
19194				popup.init({
19195					title: payload.title,
19196					content: payload.message + '<ul class="shipping-restrictions-popup__list">' + list + '</ul>',
19197				}).showPopup();
19198			} else if (data.hasOwnProperty('status') && data.status === 'BillingAddressError') {
19199				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
19200				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2"><i class="fa fa-exclamation-triangle"></i> <b>Billing Address Error:</b> ' + message + '</div>');
19201			} else if (data.hasOwnProperty('status') && data.status === 'ShippingAddressError') {
19202				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
19203				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2"><i class="fa fa-exclamation-triangle"></i> <b>Shipping Address Error:</b> ' + message + '</div>');
19204			} else if (data.hasOwnProperty('status') && data.status === 'ValidationError') {
19205				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
19206				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>Validation Error:</b> ' + message + '</div>');
19207			} else {
19208				var message = data.hasOwnProperty('message') ? data.message : Resources.Checkout.Confirm.Error.Generic;
19209				$submitPayment.after('<div id="errorBoxOPC" class="payment-declined p-2 mt-2" role="alert" tabindex="0"><i class="fa fa-exclamation-triangle"></i> <b>Payment Declined:</b> ' + message + '</div>');
19210			}
19211			
19212			$submitPayment.loading('destroy');
19213			$submitPayment.attr('disabled', false);
19214			submitPaymentRecaptchaError();
19215		}).catch(function(response) {
19216			if (response.status == 302 && response.responseText) {
19217				location.href = response.responseText;
19218			}
19219		});
19220	} else {
19221		$submitPayment.loading('destroy');
19222		$submitPayment.attr('disabled', false);
19223
19224		// Adding ADA compliance error messages for submitPayment section
19225		$paymentForm.validate().form();
19226		if ($paymentForm.find('.field.error').length > 0) {
19227			$('.payment-declined').remove();
19228			var paymentFormErrorMessages = opcpayment.getErrorMessages($paymentForm);
19229			var paymentFormErrorBox = opcpayment.errorBox('Cannot Proceed', 'Please fix the following errors:', paymentFormErrorMessages);
19230			if (paymentFormErrorBox) {
19231				$submitPayment.after(paymentFormErrorBox);
19232			}
19233		}
19234
19235		submitPaymentRecaptchaError();
19236	}
19237}
19238
19239function initializePaymentMethodsEvents() {
19240	paymentMethodDomChanges();
19241
19242	$(document).on('change', creditCardListSelector, function (e, options) {	
19243		var $this = $(this);
19244		var hiddenSelectedValue = $('.pt_onepcheckout input[name*="_selectedPaymentMethodID"]').val();
19245
19246		options = options != undefined ? options : {};
19247		options.doReset = options.doReset != undefined ? options.doReset : true;
19248		
19249		var selectedCreditCardType = $this.val();
19250		
19251		if (selectedCreditCardType) {
19252			var selectedPaymentMethodId = $this.attr('payment-method') || $this.find(':selected').attr('payment-method');
19253			var selectedPaymentCardImageUrl = $this.attr('payment-card-image') || $this.find(':selected').attr('payment-card-image');
19254			
19255			$paymentForm.attr('selected-payment-method', selectedPaymentMethodId).attr('selected-card-type', selectedCreditCardType);
19256			$this.closest('[payment-method-group]').attr('payment-method', selectedPaymentMethodId);
19257			
19258			$('.pt_onepcheckout input[name*="_selectedPaymentMethodID"]').val(selectedPaymentMethodId);
19259
19260			if (!$('[payment-method="' + selectedPaymentMethodId + '"].payment-method').hasClass('payment-method-expanded')) {
19261				$('.payment-method').removeClass('payment-method-expanded').hide();
19262				$('[payment-method="' + selectedPaymentMethodId + '"].payment-method').addClass('payment-method-expanded').show();	
19263			}
19264			
19265			$('.payment-card-list.grid').find('[payment-card]').hide();
19266			$('.payment-card-list.grid').find('[payment-card="' + selectedCreditCardType + '"]').show();
19267			
19268			$(creditCardListSelector).filter('input[value="' + selectedCreditCardType + '"]').prop('checked', true);
19269			$(creditCardListSelector).find('option[value="' + selectedCreditCardType + '"]').prop('selected', true);
19270
19271			// Legacy code? Are we still trying to allow logged-in customer to save credit card info to their account at checkout?
19272			if (hiddenSelectedValue == PaymentMethodConstants.CreditCard || hiddenSelectedValue == PaymentMethodConstants.AllianceData) {
19273				$creditCardSaveCheckboxContainer.hide();
19274			} else {
19275				$creditCardSaveCheckboxContainer.show();
19276			}
19277
19278			$paymentForm.find('[payment-method-group="' + PaymentMethodConstants.CreditCard + '"] .cc-image').css('background-image', 'url(' + selectedPaymentCardImageUrl + ')');
19279
19280			if (options.doReset) {
19281				resetCreditCardFields($('[payment-method="' + selectedPaymentMethodId + '"].payment-method'));
19282				$savedCreditCardList.val('');
19283			}
19284		}
19285	});
19286	
19287	$(document).on('click', paymentFormSelector + ' .payment-method-group-selector', function (e) {
19288	    var paymentMethodGroupId = $(this).val();
19289	    
19290	    if (paymentMethodGroupId) {
19291	    	$paymentForm.attr('selected-payment-method-group', paymentMethodGroupId);
19292		    
19293		    $('.pt_onepcheckout input[name*="_selectedPaymentMethodID"]').val($('[payment-method-group="' + paymentMethodGroupId + '"]').attr('payment-method'));
19294		    
19295		    $('[payment-method-group]').hide().removeClass('expanded');
19296		    $('[payment-method-group="' + paymentMethodGroupId + '"]').show().addClass('expanded');
19297		    
19298		    if ($('[payment-method-group="' + paymentMethodGroupId + '"]').find('.payment-method-expanded').length < 1) {
19299				var $defaultPaymentMethod;
19300				if (paymentMethodGroupId == PaymentMethodConstants.CreditCard) {
19301					// Clear form error messages before re-drawing CC fields - not to be confused with jquery validation, which is separate
19302					$('[payment-method-group="' + paymentMethodGroupId + '"]').find('.error').removeClass('error');
19303					$('[payment-method-group="' + paymentMethodGroupId + '"]').find('.error-message').html('').removeClass('error-message');
19304					$defaultPaymentMethod = $('[payment-method="' + paymentMethodGroupId + '"].payment-method');
19305				} else {
19306		    		$defaultPaymentMethod = $('[payment-method-group="' + paymentMethodGroupId + '"]').find('.payment-method').first();
19307				}
19308		    	$defaultPaymentMethod.addClass('payment-method-expanded').show();
19309		    	if ($defaultPaymentMethod.attr('payment-card-image')) {
19310		    		$defaultPaymentMethod.find('.cc-image').css('background-image', 'url(' + $defaultPaymentMethod.attr('payment-card-image') + ')');
19311		    	}
19312		    }
19313		    
19314		    if (window.innerWidth <= 767) {
19315				$([document.documentElement, document.body]).animate({
19316					// scrollTop: $('.payment-method-expanded').closest('.payment-method-option').offset().top - $('.top-banner').height() + 20
19317					scrollTop: $('#paymentSection').offset().top - $('.top-banner').height() - 15
19318				}, 250);
19319		    }
19320		    
19321		    // addresses.updateSummary();
19322	    }
19323	});
19324
19325	$(document).on('input blur', creditCardNumberInputSelector, function(event) {
19326		var creditCardNumber = $creditCardNumberInput.val().replace(/\s/g, '');
19327		var selectedCard;
19328
19329		if (creditCardNumber.length > 0) {
19330			selectedCard = util.getCreditCardType(creditCardNumber);
19331			if (selectedCard) {
19332				var $selectedCardNumberInput = $('[payment-method-group="' + PaymentMethodConstants.CreditCard + '"] [payment-method="' + selectedCard.PaymentMethod + '"] [credit-card-number-input]');
19333				if ($selectedCardNumberInput.filter(':visible').length < 1) {
19334					$selectedCardNumberInput.val(creditCardNumber);
19335				}
19336				$('.pt_onepcheckout input[name*="_creditCard_type"]').val(selectedCard.PaymentCard);
19337				$creditCardList.filter('input[type="radio"][value="' + selectedCard.PaymentCardDisplayKey + '"]').first().prop('checked', true).trigger('change', { doReset: false });
19338			} else {
19339				$('.cc-cardlist-select :input').removeAttr('checked').removeAttr('selected').val('');
19340				$('.payment-card-list.grid').find('[payment-card]').show();
19341				$creditCardList.filter('input').prop('checked', false);
19342				$creditCardList.find('option').prop('selected', false);
19343				$paymentForm.find('[payment-method-group="' + PaymentMethodConstants.CreditCard + '"] .cc-image').css('background-image', '');
19344				
19345				var isMaskedSavedCard = $creditCardNumberInput.data('isMaskedSavedCard') || false;
19346				if (isMaskedSavedCard) {
19347					var newInput = event.originalEvent.data || '';
19348					creditCardNumber = newInput; // Update the variable with the new value 
19349					$creditCardNumberInput.data('isMaskedSavedCard', false).val(newInput); // Update credit card number field
19350				}
19351			}
19352			if (SitePreferences.CREDIT_CARD_FORMATTING_ENABLED && SitePreferences.JQUERY_MASK) {
19353				var newType = creditCardNumber.startsWith('34') || creditCardNumber.startsWith('37') ? 'amex' : 'standard';
19354				var currentType = $creditCardNumberInput.data('cardType');
19355				if (!currentType || currentType != newType) {
19356					$creditCardNumberInput.data('cardType', newType);
19357					$creditCardNumberInput.unmask();
19358
19359					if (newType === 'amex') {
19360						$creditCardNumberInput.mask('0000 000000 00000');
19361					} else {
19362						$creditCardNumberInput.mask('0000 0000 0000 0000');
19363					}
19364				}
19365			}
19366			$creditCardSaveCheckboxContainer.show();
19367			
19368		}
19369	});
19370	
19371	$(document).on('click', '.add-new-card', function(e) {
19372		e.preventDefault();
19373
19374		resetCreditCardFields($(this).closest('[payment-method]'));
19375		$('#opc-cc-list :input').removeAttr('checked');
19376		$('.cc-cardlist-select :input').removeAttr('checked').removeAttr('selected').val('');
19377		$('.cc-image').css('background-image', 'none');
19378		$creditCardNumberInput.val('');
19379		paymentMethodDomChanges();
19380	});
19381	
19382	$(document).on('click', '.payment-method-option.accordion .form-label', function () {
19383		$(this).closest('.accordion').toggleClass('open');
19384	});
19385
19386	$(document).off('Onepcheckout.PaymentFormFieldValidation').on('Onepcheckout.PaymentFormFieldValidation', function() {
19387		// Destroy any previous validators for the $paymentForm before reinitializing due to redraw of checkout
19388		$paymentForm.validate().destroy();
19389		// Don't use .form() or any other validator method here because it will auto-check the form immediately, but we want to just attach the listeners
19390		$paymentForm.validate(validator.settings);
19391    });
19392}
19393
19394function paymentMethodDomChanges() {
19395	var combinedSelectors = [creditCardNumberInputSelector, privateLabelCardNumberInputSelector].join();
19396	var $fieldContainer = $(combinedSelectors).parent();
19397
19398	if ($fieldContainer.find('.cc-image').length == 0) {
19399		$fieldContainer.append($creditCardImage);
19400	}
19401}
19402
19403function resetCreditCardFields($paymentMethod) {
19404	$paymentMethod.find('input[name*="_owner"]').val('');
19405	$paymentMethod.find('input[name*="_number"]').val('');
19406	$paymentMethod.find('input[name*="_cvn"]').val('');
19407	$paymentMethod.find('input[name*="_month"]').val('');
19408	$paymentMethod.find('input[name*="_year"]').val('');
19409	$paymentMethod.find('input[name="cc-number-mask"]').val('');
19410}
19411
19412/**
19413 * @description Make the AJAX request to set cart data
19414 * @param {Element} form The form element that present address object
19415 * @param {Element} Action name to apply to form
19416 * @returns {Promise}
19417 */
19418var setOPCPaymentData = function (formSelector, name) {
19419    var $form = $(formSelector);
19420
19421    return TPromise.resolve($.ajax({
19422        type: 'POST',
19423        url: util.ajaxUrl(Urls.setOpcAddress),
19424        data: $form.serialize() + '&' + name + '=' + name
19425    }));
19426};
19427
19428exports.init = function (optionsParameter) {
19429	options = optionsParameter;
19430	
19431	if (options && options.Recaptcha && (options.Recaptcha.EnabledForPlaceOrder || options.Recaptcha.EnabledForRe
19431wardsCredit || options.Recaptcha.EnabledForGiftCards)) {
19432    	recaptcha.initialize();
19433	}
19434	if (options.B2B && options.B2B.Enabled) {
19435		PaymentVoucherPaymentPanel = require('./checkout/PaymentVoucherPaymentPanel');
19436		PaymentVoucherPaymentPanel.initialize();
19437	}
19438	if (pageContext && pageContext.site && pageContext.site == SiteConstants.SiteIds.BootBarnWorkforceUS) {
19439		WorkforceVoucherPaymentPanel = require('./checkout/WorkforceVouchersPaymentPanelClient');
19440		WorkforceVoucherPaymentPanel.initialize();
19441	}
19442	GiftCard.initialize(options);
19443	RewardsCredit.initialize(options);
19444	LoyaltyCredit.initialize(options);
19445    initializeEvents(options);
19446};
19447},{"../../../../bc_sheplersbootbarn_core/cartridge/js/CheckoutConstants":113,"../../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../bonus-products-view":6,"../bopis/components/ProductLineItemHelper":11,"../components/LoadingIndicator":25,"../components/Recaptcha":29,"../popup":83,"../quickview":88,"../util":102,"./cart":50,"./checkout/GiftCardPaymentPanel":54,"./checkout/LoyaltyCredit":55,"./checkout/PaymentVoucherPaymentPanel":56,"./checkout/RewardsCredit":57,"./checkout/WorkforceVouchersPaymentPanelClient":58,"./checkout/formPrepare":61,"./checkout/opcaddress":64,"./checkout/opcpayment":65,"promise":134}],71:[function(require,module,exports){
19448'use strict';
19449
19450var tooltip = require('../tooltip');
19451var	page = require('../page');
19452
19453var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
19454
19455var options = {};
19456var buffer = {};
19457
19458function triggerReactWebviewEvent() {
19459	if ('ReactNativeWebView' in window) {
19460		window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'order-page' }));
19461	}
19462}
19463
19464function initializePriceSpiderOrderTracking() {
19465	$(document).on('DataLayer.OrderComplete', function (event, data) {
19466		if (data && data.dataLayer) {
19467			var dataLayer = data.dataLayer;
19468			var psrid = sessionStorage.getItem('psrid');
19469			if (psrid && dataLayer && 'event' in dataLayer && dataLayer.event == 'purchase') {	
19470				var itemsTitle = [];
19471				var itemsPrice = [];
19472				var itemsQuantity = [];
19473				var itemsSku = [];
19474				var itemsManufacturer = [];
19475				var itemElements = dataLayer.items;
19476				for (var i = 0; i < itemElements.length; i++) {
19477					itemsTitle.push(itemElements[i].item_name);
19478					itemsQuantity.push(itemElements[i].quantity);
19479					itemsPrice.push(itemElements[i].price);
19480					itemsSku.push(itemElements[i].item_id);
19481					itemsManufacturer.push(itemElements[i].item_brand);
19482				}
19483				var order = pswtb.purchaseTracking.createOrder();
19484				order.currencyCode = dataLayer.currency;
19485				order.purchaseTotal = dataLayer.value;
19486				var itemsDetails = [];
19487				for (var y = 0; y < itemsTitle.length; y++) {
19488					itemsDetails.push({
19489						"productName": itemsTitle[y],
19490						"quantity": itemsQuantity[y],
19491						"unitPrice": itemsPrice[y] / itemsQuantity[y],
19492						"manufacturer": itemsManufacturer[y],
19493						"sku": itemsSku[y]
19494					});
19495				}
19496				order.details = itemsDetails;
19497				order.redirectToken = psrid;
19498				order.submit();
19499			}
19500		}
19501	});
19502}
19503
19504function initializeVWOOrderTracking() {
19505	buffer['items'] = null;
19506	if (SitePreferences.VWO_ENABLED && SitePreferences.VWO_PURCHASE_TRACKING_ENABLED) {
19507		buffer['vwoLoadedInterval'] = setInterval(triggerVWOOrderTracking, 100);
19508
19509		$(document).on('DataLayer.OrderComplete', function (event, data) {
19510			var items = [];
19511			var paymentMethods = '';
19512			if (data && data.dataLayer) {
19513				var dataLayer = data.dataLayer;
19514				if (data && data.options && data.options.PaymentMethod) {
19515					paymentMethods = data.options.PaymentMethod;
19516				} else if (data && data.options && data.options.PaymentMethods) {
19517					paymentMethods = data.options.PaymentMethods.join(' | ');
19518				} else if (pageContext.PaymentMethods) {
19519					paymentMethods = pageContext.PaymentMethods.join(' | ');
19520				}
19521				for (var itemKey in dataLayer.items) {
19522					var item = dataLayer.items[itemKey];
19523					var revenue = item.price;
19524					var price = revenue;
19525					if (item.discount) {
19526						price += item.discount;
19527					}
19528					items.push({
19529						"price": price.toFixed(2),
19530						"item": item.item_id,
19531						"paymentMethod": paymentMethods,
19532						"registeredUser": Customer.registered,
19533						"revenue": revenue
19534					});
19535				}
19536			}
19537
19538			if (items && items.length > 0) {
19539				buffer['items'] = items;
19540				triggerVWOOrderTracking();
19541			}
19542		});
19543	}
19544}
19545
19546function triggerVWOOrderTracking() {
19547	if (buffer['items'] && window.VWO && window.VWO.event && typeof window.VWO.event == 'function') {
19548		clearInterval(buffer['vwoLoadedInterval']);
19549		for (var itemIndex = 0; itemIndex < buffer['items'].length; itemIndex++) {
19550			var item = buffer['items'][itemIndex];
19551			VWO.event("purchase", item);
19552		}
19553	}
19554}
19555
19556function initializeTatariOrderTracking() {
19557	if (SitePreferences.TATARI_ENABLED) {
19558		$(document).on('DataLayer.OrderComplete', function (event, data) {
19559			var maximumAttempts = 50;
19560			var retryDelay = 100;
19561			var attempts = 0;
19562			
19563			var items = [];
19564			if (data && data.dataLayer) {
19565				var orderData = data.dataLayer;
19566				var orderTotal = orderData.value;
19567				var orderCurrency = orderData.currency;
19568				var orderID = orderData.transaction_id;
19569				
19570				for (var itemKey in orderData.items) {
19571					var item = orderData.items[itemKey];
19572					var price = item.price;
19573
19574					items.push({
19575						"name": item.item_name,
19576						"price": price.toFixed(2),
19577						"quantity": item.quantity,
19578						"productID": item.item_id,
19579						"category": item.item_category + " " + item.item_category2
19580					});
19581				}
19582			}
19583
19584			if (items && items.length > 0) {
19585				function sendTatariPurchase() {
19586					if ( window.tatari && typeof window.tatari.purchase === 'function') {
19587						window.tatari.purchase({
19588							total: orderTotal,
19589							currency: orderCurrency,
19590							orderId: orderID,
19591							items: items,
19592							newPurchase: true
19593						});
19594	
19595						window.console.log(
19596							'Tatari purchase sent for order:',
19597							orderID
19598						);
19599	
19600						return;
19601					}
19602	
19603					attempts += 1;
19604	
19605					if (attempts < maximumAttempts) {
19606						window.setTimeout(sendTatariPurchase, retryDelay);
19607					} else {
19608						window.console.error(
19609							'Tatari purchase was not sent: ' +
19610							'tatari.purchase did not become available.'
19611						);
19612					}
19613				}
19614				
19615				sendTatariPurchase();
19616			}
19617		});
19618	}
19619}
19620
19621function initializeConfirmationRegistration() {
19622	var existingCustomer = $('.order-billing-name').attr('has-profile');
19623	var notRewardsCustomer = $('.order-billing-name').attr('has-rewards');
19624	var rewardsAdded = $('.rewards-added').data('rewards');
19625	var firstName = $('input[name="dwfrm_profile_customer_firstname"]').val();
19626	var lastName = $('input[name="dwfrm_profile_customer_lastname"]').val();
19627	var email = $('input[name="dwfrm_profile_customer_email"]').val();
19628	var zip = $('input[name="dwfrm_profile_customer_zip"]').val();
19629	var phone = $('input[name="dwfrm_profile_customer_phone"]').val();
19630	var cleanPhone = '';
19631	if (phone) {
19632		cleanPhone = phone.replace(/\D/g, '');
19633	}
19634	var city = $('.billing-info .city').text().replace(',','');
19635	var state = $('.billing-info .state').text();
19636	var country = $('.billing-info .order-billing-country').text();
19637	var orderNo = $('.orderdetail-section .sectionheader h3').text().replace("Order Number: ","");
19638	
19639	$(document).ready(function() {
19640		if(notRewardsCustomer === "false"){
19641			if (!rewardsAdded || rewardsAdded == '') {
19642				$.ajax({
19643					type: 'post',
19644					url: Urls.createRewardsAccount,
19645					contentType: 'application/json',
19646					data: JSON.stringify({
19647						firstName: firstName,
19648						lastName: lastName,
19649						emailAddress: email,
19650						phoneNumber: cleanPhone,
19651						zipCode: zip,
19652						birthDate: '01/01/1900',
19653						autoEnroll: true,
19654						existingCustomer: existingCustomer
19655					}),
19656					success: function (data) {
19657						console.log('Rewards account created.');
19658					}
19659				});
19660			}
19661		}
19662	});
19663	$(document).on('focus', 'input[name^="dwfrm_profile_login_password_"]', function () {
19664    	$('.register-errors span').addClass('hidden');
19665    	$('.password-holder').removeClass('error');
19666    });
19667	$(document).off('click', 'button.confirmation-register').on('click', 'button.confirmation-register', function (e) {
19668    	e.preventDefault();
19669		$('.register-errors span').addClass('hidden');
19670		$('.password-holder').removeClass('error');
19671		
19672    	var isPasswordValid = true;
19673		var password = $('input[name^="dwfrm_profile_login_password_"]').val();
19674		var regexPassword = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*()_+={}\[\]:;"'<>,.?\/\\~-])[A-Za-z\d!@#$%^&*()_+={}\[\]:;"'<>,.?\/\\~-]{8,20}$/g;
19675		if (password.length == 0 || password.match(regexPassword) == null) {
19676			isPasswordValid = false;
19677			$('.register-errors span.format-error').removeClass('hidden');
19678			$('.password-holder').addClass('error');
19679		}
19680    	
19681		if (isPasswordValid) {
19682			$.ajax({
19683				url: Urls.registerAccount,
19684				type: "get",
19685				data: {
19686					registerFirstName: firstName,
19687					registerLastName: lastName,
19688					registerEmail: email,
19689					registerZip: zip,
19690					registerPhone: cleanPhone,
19691					registerOrderNo: orderNo,
19692					registerPassword: password
19693				},
19694				success: function (response) {
19695					var data = JSON.parse(response);
19696						
19697					if (data.status == 'success') {		
19698						var auth_data = {
19699							email: email
19700						}
19701						var contact_data = {
19702						    'channels': {
19703						        'email': {
19704						            'address': email
19705						        }
19706						    },
19707							'first_name': firstName,
19708							'last_name': lastName,
19709							'phone': cleanPhone,
19710							'postal_code': zip,
19711							'source_create': 'confirmation',
19712							'geo_location': {
19713							    'postal_code': zip,
19714							    'country': country,
19715							    'state': state,
19716							    'city': city
19717							}
19718						};
19719						var loginProperties = {
19720						    "email": email
19721						};			
19722						if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
19723							crdl([				
19724								['contact', auth_data, contact_data],
19725								['event', 'account_create'],
19726								['event', 'login', loginProperties]
19727							]);
19728						}
19729					}
19730					if ($('body').attr('checkout-mode') == 'mobileapp') {
19731						window.location.href = "bootbarn://account";
19732					} else {
19733						window.location.href = Urls.account;
19734					}
19735				},
19736				error: function(jqXHR, textStatus, errorThrown) {
19737					console.log(textStatus, errorThrown);
19738				}
19739			});
19740		}
19741	});
19742}
19743
19744exports.init = function (optionsParameter) {
19745	options = optionsParameter;
19746
19747	tooltip.init({
19748		items: '.tooltip'
19749	});
19750	triggerReactWebviewEvent();
19751	initializePriceSpiderOrderTracking();
19752	initializeVWOOrderTracking();
19753	initializeTatariOrderTracking();
19754	if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
19755		initializeConfirmationRegistration();
19756	}
19757}
19758},{"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../page":46,"../tooltip":101}],72:[function(require,module,exports){
19759'use strict';
19760
19761var _ = require('lodash');
19762var ajax = require('../../ajax');
19763var AddressAutocompleteDialog;
19764var AddressVerificationDialog;
19765var AddressVerificationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper');
19766var dialog = require('../../dialog');
19767var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
19768var minicart = require('../../minicart');
19769var page = require('../../page');
19770var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
19771var TPromise = require('promise');
19772var util = require('../../util');
19773var ValidationHelper = require('../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper');
19774var ValidationRules = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
19775var ValidationSettings = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
19776
19777var options = {};
19778var dataBuffer = {};
19779
19780/**
19781 * Wraps the dialog close functionality in a function so that we can hook into the transitionEnd event
19782 * to fire the close event on the dialog.
19783 * This allows for an animation to complete before the dialog is closed and removed from the dom
19784 */
19785 var closeAddToCartModal = function () {
19786	// save overaly for class additions
19787	var $overlay = $('.add-to-cart-modal-wrapper').parent().find('.ui-widget-overlay');
19788	
19789	// hook into transition end
19790	if (pageContext.site === SiteConstants.SiteIds.BootBarnRspUS) {
19791		var currentModal = $('#dialog-container');
19792		if (currentModal.dialog('isOpen')) {
19793			currentModal.dialog('close');
19794		}
19795	} else {
19796		$('.add-to-cart-modal-wrapper').one('transitionend webkitTransitionEnd oTransitionEnd', function (e) {
19797			var currentModal = $(this).find('#dialog-container');
19798			if (currentModal.dialog('isOpen')) {
19799				currentModal.dialog('close');
19800			}
19801		});
19802	}
19803	
19804	$overlay.removeClass('show');
19805	$('.add-to-cart-modal-wrapper').removeClass('show');
19806}
19807
19808/**
19809 * @description Make the AJAX request to add an item to cart
19810 * @param {Element} form The form element that contains the item quantity and ID data
19811 * @returns {Promise}
19812 */
19813var addItemToCart = function (form) {
19814    var $form = $(form),
19815        $qty = $form.find('input[name="Quantity"]');
19816    if ($qty.length === 0 || isNaN($qty.val()) || parseInt($qty.val(), 10) === 0) {
19817        $qty.val('1');
19818    }
19819    return TPromise.resolve($.ajax({
19820        type: 'POST',
19821        url: util.ajaxUrl(Urls.addProduct),
19822        data: $form.serialize()
19823    }));
19824};
19825
19826/**
19827 * @description Handler to handle the add to cart event
19828 */
19829var addToCart = function(event, data) {
19830    event.preventDefault();
19831	var $html = $('html');
19832	var $container = $('#wrapper');
19833    var $form = $('.pdpForm');
19834    var sizeSelected = $('div.product-variations .size.size-options li.selected');
19835	var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
19836	var widthSelected = $('div.product-variations .width.width-options li.selected').length;
19837	var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
19838
19839    if (!isSizeSelected) {
19840    	dialog.open({
19841			html: 'Please select a size',
19842			options: {
19843				dialogClass: 'dialog-alert',
19844				draggable: false
19845			}
19846		});
19847    	return false;
19848    }
19849
19850    if (!$('div.product-variations .color.color-options li.selected').length) {
19851    	dialog.open({
19852			html: 'Please select a color',
19853			options: {
19854				dialogClass: 'dialog-alert',
19855				draggable: false
19856			}
19857		});
19858    	return false;
19859    }
19860
19861    var isBopis = false;
19862	var $bopisAddToCartOptions = $('.bopis-add-to-cart-options-container');
19863	var $bopisAddToCartOption;
19864	if ($bopisAddToCartOptions.length > 0) {
19865		if (pageContext.site === SiteConstants.SiteIds.BootBarnRspUS) {
19866			$bopisAddToCartOptions.find('input[name="AddToCartOption"]').prop('checked', false);
19867			$(event.currentTarget).closest('li').find('input[name="AddToCartOption"]').prop('checked', true);
19868			$bopisAddToCartOptions = $('.bopis-add-to-cart-options-container');
19869		}
19870		$bopisAddToCartOption = $bopisAddToCartOptions.find('input[name="AddToCartOption"]:checked');
19871		var $bopisAddToCartLabel = $bopisAddToCartOption.closest('li');
19872		var $bopisOptionalMessage = $bopisAddToCartLabel.find('.add-to-cart-optional-message');
19873		if ($bopisOptionalMessage.length > 0) {
19874			dataBuffer['OptionalMessage'] = $bopisOptionalMessage.first().html().trim();
19875		}
19876
19877		var $bopisAddToCartOptionsEnabled = $bopisAddToCartOptions.find('input[name="AddToCartOption"]:not([disabled])');
19878		if ($bopisAddToCartOptionsEnabled.length == 0) {
19879			dialog.open({
19880				html: 'This item is not available for your selected options.',
19881				options: {
19882					dialogClass: 'dialog-alert',
19883					draggable: false
19884				}
19885			});
19886			return false;
19887		}
19888	}
19889
19890    if ($(event.currentTarget).hasClass('bopis') || (data && data.bopis)) {
19891    	isBopis = true;
19892    } else if ($bopisAddToCartOptions.length > 0) {
19893		var bopisShippingOption = $bopisAddToCartOption.val();
19894		if (bopisShippingOption == 'delivery') {
19895			$(document).trigger('Bopis.LocalDelivery.Display', { OptionalMessage: dataBuffer['OptionalMessage'] });
19896			return false;
19897		} else if (bopisShippingOption == 'shiptostorepickup') {
19898			isBopis = true;
19899			data = data || {};
19900			data.bopisStore = $bopisAddToCartOptions.find('.selected-store[store-id]').attr('store-id');
19901			data.shippingOption = bopisShippingOption;
19902		} else if (bopisShippingOption == 'pickup') {
19903			isBopis = true;
19904			var storeData;
19905			var $storeData = $bopisAddToCartOptions.find('.selected-store[store-data]');
19906			if ($storeData.length > 0) {
19907				storeData = JSON.parse($storeData.attr('store-data'))
19908				data = data || {};
19909				data.bopisStore = storeData.location.code;
19910				data.quantityAvailable = storeData.detail.quantity;
19911				data.shippingOption = bopisShippingOption;
19912			}
19913		}
19914	}
19915
19916    var $bopisStore;
19917    if (isBopis) {
19918    	$bopisStore = $('input[name="Bopis_StoreSearch_StoreId"]:checked');
19919		$('.product-add-to-cart input[name="bopis_store"]').val(data.bopisStore ? data.bopisStore : $bopisStore.val());
19920    	$('.product-add-to-cart input[name="bopis_quantity_available"]').val(data.quantityAvailable != undefined ? data.quantityAvailable : $bopisStore.attr('quantity-available'));
19921    	$('.product-add-to-cart input[name="bopis_shipping_option"]').val(data.shippingOption ? data.shippingOption : '');
19922    	$('.product-add-to-cart input[name="bopis_delivery_address"]').val(data.deliveryAddress ? data.deliveryAddress : '');
19923    	$('.product-add-to-cart input[name="bopis_delivery_note"]').val(data.deliveryNote ? data.deliveryNote : '');
19924    	$('.product-add-to-cart input[name="bopis_delivery_gift_message"]').val(data.giftMessage ? data.giftMessage : '');
19925    	$('.option-dropdown select[name*="_Bopis"]').val('Yes');
19926    } else {
19927    	$('.product-add-to-cart input[name="bopis_store"]').val('');
19928    	$('.product-add-to-cart input[name="bopis_quantity_available"]').val('');
19929    	$('.product-add-to-cart input[name="bopis_shipping_option"]').val('');
19930    	$('.product-add-to-cart input[name="bopis_delivery_address"]').val('');
19931    	$('.option-dropdown select[name*="_Bopis"]').val('No');
19932	}
19933
19934	$('.product-add-to-cart input[name="channel"]').val(data && data.channel ? data.channel : '');
19935	if (data && 'csrf_token' in data && data.csrf_token) {
19936		$('#csrftoken').val(data.csrf_token);
19937	}
19938    
19939    if (sizeSelected.hasClass("tempselection")) {
19940    	sizeSelected.addClass('enterSelected');
19941    	sizeSelected.trigger('click');
19942    	sizeSelected.find('.swatchanchor').trigger('click');
19943    	setTimeout( function(){$("#add-to-cart").click()}, 100 );
19944    	return false;
19945    }
19946
19947    if (typeof dataLayer !== 'undefined') {
19948		var qty = $('.pt_product-details .pdpForm .product-add-to-cart select[id=Quantity]').val();
19949		var variationAttr = $('.product-variations').data('attributes');
19950		var sizeAttrValue = variationAttr.size.displayValue;
19951		if (sizeAttrValue == '' || sizeAttrValue == null ) {
19952			if (sizeSelected.hasClass("tempselection") && sizeSelected.find('a.swatchanchor').data() != null ) {
19953				sizeSelected.addClass('enterSelected');
19954				var sizeObjRef = sizeSelected.find('a.swatchanchor').data();
19955				sizeAttrValue = sizeObjRef.sizeName;
19956				sizeSelected.find('a.swatchanchor').trigger('click');
19957			}
19958		}
19959
19960		var googleTagManagerAddToCartDeliveryMethod = 'shiptoaddress';
19961		if (data && data.shippingOption) {
19962			switch (data.shippingOption) {
19963				case 'pickup':
19964					googleTagManagerAddToCartDeliveryMethod = 'storepickup';
19965					break;
19966				case 'delivery':
19967					googleTagManagerAddToCartDeliveryMethod = 'samedaydelivery';
19968					break;
19969				case 'ship':
19970					googleTagManagerAddToCartDeliveryMethod = 'shiptoaddress';
19971					break;
19972				default:
19973					googleTagManagerAddToCartDeliveryMethod = data.shippingOption;
19974					break;
19975			}
19976		}
19977		GoogleTagManagerClient.PushDataLayerForDataLayerType('AddToCart', { deliverymethod: googleTagManagerAddToCartDeliveryMethod});
19978
19979		for (var i=0; i < dataLayer.length; i++) {
19980			if (dataLayer[i].event == 'pdpDisplay') {
19981				var selectedItem = dataLayer[i].ecommerce.detail.products[0];
19982				var itemPrice = (selectedItem.silhouette == 'Gift Card') ?  parseInt((variationAttr.size.value).replace('$','')) : selectedItem.price;
19983				var skuID = $('#pid').val();
19984				var image = $('.product-primary-image [class*="-slide"] .primary-image.first').find('a').attr('href');
19985				if (!image || image == undefined || image == null) {
19986					image = $('.product-primary-image [class*="-slide"] .primary-image.first').find('img').attr('src');
19987				}
19988
19989				if (SitePreferences.CORDIAL_ENABLED) {
19990					var properties = {
19991						'productID': selectedItem.id
19992					};
19993					
19994					var cart_data = [{
19995					    'productID': selectedItem.id,
19996					    'sku': skuID,
19997					    'category': (selectedItem.category).replace(/\\/g, ""),
19998					    'name': (selectedItem.name).replace(/\\/g, ""),
19999					    'qty': qty, 
20000					    'itemPrice': itemPrice,
20001					    'images': [image],
20002					    'url':$(location).attr("href").split("?utm")[0].split("#utm")[0].split("&utm")[0],
20003					    'attr': {
20004					        'manufacturer': selectedItem.brand,
20005					    	'color': variationAttr.color.displayValue,
20006					        'size': variationAttr.size.displayValue
20007					    }
20008					}];
20009	
20010					if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
20011						crdl([
20012							['cart', 'update'],
20013							['cartitem', 'add' , cart_data], 
20014							['event', 'cart', properties]
20015						]);
20016					}
20017				}
20018				
20019				var dateTime = Math.floor(Date.now() / 1000);
20020				var eventID = 'addtocart-' + selectedItem.id + '-' + dateTime;
20021				
20022				if (pageContext.site === SiteConstants.SiteIds.BootBarnUS && SitePreferences.PINTEREST_ENABLED) {
20023					pintrk('track', 'addtocart', {
20024						event_id: eventID,
20025						value: itemPrice * qty,
20026						order_quantity: qty,
20027						currency: $('meta[itemprop="priceCurrency"]').attr('content'),
20028						line_items: [
20029							{
20030								product_name: (selectedItem.name).replace(/\\/g, ""),
20031								product_id: selectedItem.id,
20032								product_category: (selectedItem.category).replace(/\\/g, ""),
20033								product_variant_id: skuID,
20034								product_price: itemPrice,
20035								product_quantity: qty,
20036								product_brand: selectedItem.brand
20037							}
20038						]
20039					});
20040				}
20041				
20042				if (SitePreferences.PIXLEE_ENABLED) {
20043					if (typeof pixlee_analytics !== 'undefined') {
20044					    pixlee_analytics.events.trigger('add:to:cart', {
20045					        'product_sku': selectedItem.id,
20046					        'price': itemPrice,
20047					        'quantity': qty,
20048					        'currency': $('meta[itemprop="priceCurrency"]').attr('content')
20049					    });
20050					 }
20051				}
20052			}
20053		}
20054    }
20055
20056    addItemToCart($form).then(function (response) {
20057    	$.ajax({url : Urls.cartItemCount, method: 'POST', dataType: 'json', success : function(data) {
20058			if (data && data.ProductCount != undefined) {
20059    			$('.btn-minicart span').html(Math.round(data.ProductCount));
20060			}
20061    	}});
20062        var $uuid = $form.find('input[name="uuid"]');
20063        if ($uuid.length > 0 && $uuid.val().length > 0) {
20064            page.refresh();
20065        } else if(SitePreferences.REDIRECT_TO_CART && (!pageContext.options || pageContext.options.StoreUIVersion != '2024Redesign')){
20066    		var redirectUrl = (SitePreferences.HIDE_BASKET) ? Urls.onePage : Urls.cartShow;
20067    		window.location.href = redirectUrl;
20068    	} else {
20069			if (SitePreferences.ADD_TO_CART_MODAL || (pageContext.options && pageContext.options.StoreUIVersion == '2024Redesign')) {
20070				var qtyAdded = $('.pt_product-details .pdpForm .product-add-to-cart select[id=Quantity]').val();
20071				$.ajax({
20072					url: Urls.addToCartModalDisplay,
20073					data: {
20074						storeuiversion: pageContext && pageContext.options && pageContext.options.StoreUIVersion ? pageContext.options.StoreUIVersion : null,
20075						shippingoption: bopisShippingOption
20076					},
20077					dataType: 'html',
20078					success: function(modalContent) {
20079						if (modalContent.length > 0) {
20080							var $modalContent = $(modalContent);
20081							var $recsContent;
20082							if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
20083								$recsContent = $('#best_selling_products.accordion.product-recommendations').removeClass('accordion');
20084							} else if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
20085								$recsContent = $('.recomm-prod-pdp-a2c-slot .accordion.product-recommendations').removeClass('accordion');
20086							} else {
20087								$recsContent = $('.main .accordion.product-recommendations').removeClass('accordion');
20088							}
20089							$recsContent.find('.accordion-head').removeClass('accordion-head');
20090							$recsContent.find('.accordion-body').removeClass('accordion-body');
20091
20092							if ($recsContent.find('.slick-list').length > 0) {
20093								var $recommendationsContentHtml = $($recsContent[0].outerHTML);
20094								var $recommendationsSlides = $recommendationsContentHtml.find('.slide');
20095								var recommendationsSlidesHtml = '';
20096								$recommendationsSlides.each(function(index, element) {
20097									recommendationsSlidesHtml += $(this)[0].outerHTML;
20098								});
20099								$recommendationsContentHtml.find('.slides').html(recommendationsSlidesHtml);
20100								$recommendationsContentHtml.find('.slides').removeClass('slick-initialized slick-slider');
20101								$recommendationsContentHtml.find('.slide').removeClass('slick-slide slick-current slick-active slick-cloned').css('width', '');
20102								$modalContent.find('.product-recommendations-container').html($recommendationsContentHtml);
20103							}
20104
20105							if (dataBuffer['OptionalMessage']) {
20106								var optionalMessageHtml = '<div class="add-to-cart-modal-optional-message">' + dataBuffer['OptionalMessage'] + '</div>';
20107								$modalContent.find('.added-message').append(optionalMessageHtml);
20108							}
20109							$modalContent.attr('bopis-shipping-option', bopisShippingOption);
20110
20111							var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
20112
20113							dialog.open({
20114								html: $modalContent,
20115								options: {
20116									dialogClass: 'add-to-cart-modal',
20117									draggable: false,
20118									resizable: false,
20119									width: 1090,
20120									open: function(event, ui) {
20121										dataBuffer['wrapperScrollTop'] = scrollTop;
20122    									dataBuffer['wrapperMarginTop'] = $container.css('margin-top');
20123										$container.attr('buffer-scrollTop', scrollTop);
20124
20125    									$container.css('margin-top', parseFloat($container.css('margin-top')) - scrollTop);
20126    									document.body.scrollTop = document.documentElement.scrollTop = 0;
20127    									$html.addClass('scroll-lock');
20128										$('.add-to-cart-modal').wrap('<div class="add-to-cart-modal-wrapper"></div>');
20129
20130										// add wrapper to close button 
20131										$('.add-to-cart-modal-wrapper').off('click.Dialog.CloseButtons').on('click.Dialog.CloseButtons', '.ui-dialog-titlebar-close, .btn.continue-shopping', function(event) {
20132											closeAddToCartModal();
20133										});
20134									
20135										// add 'show' class to overlay for nice animate in
20136										var $overlay = $('.add-to-cart-modal-wrapper').parent().find('.ui-widget-overlay');
20137										$overlay.addClass('add-to-cart-overlay');
20138										$overlay.addClass('show');
20139
20140										$('.add-to-cart-modal-wrapper').off('click.Dialog.Close').on('click.Dialog.Close', function(event) {
20141											// only call this block if the ui-widget background is clicked
20142											if ($(event.target).closest('.add-to-cart-modal').length <= 0 && $(event.target).closest('.address-autocomplete-dialog').length <= 0) {
20143												closeAddToCartModal();
20144											}
20145										});
20146
20147										if (parseFloat($('.add-to-cart-modal').css('top')) < 0) {
20148											$('.add-to-cart-modal').css('top', '0');
20149										}
20150
20151										// move title from main content to top of the modal
20152										var $addMessage = $('.add-to-cart-modal-content .main-content .added-message');
20153										if ($addMessage.length > 0) {
20154											$('.add-to-cart-modal-content').prepend($addMessage);
20155										}
20156
20157										if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
20158											var fulfillmentType = $('.bopis-add-to-cart-options-container').find('input[name="AddToCartOption"]:checked').val();
20159											var isBopis = fulfillmentType && (fulfillmentType == 'shiptostorepickup' || fulfillmentType == 'pickup');
20160											var bopisStoreName = '';
20161											var bopisStoreAddress = '';
20162											if (isBopis) {
20163												bopisStoreName = $('.bopis-add-to-cart-options-store-selector .selected-store').attr('store-name');
20164												bopisStoreAddress = $('.bopis-add-to-cart-options-store-selector .selected-store').attr('store-address');
20165												if (bopisStoreAddress && bopisStoreAddress.length > 0) {
20166													$('.modal-mini-cart-store-message-container').removeClass('d-none');
20167													$('.modal-mini-cart-store-message-container').find('.modal-mini-cart-store-details').html('<strong>' +bopisStoreName+ '</strong><br />' +bopisStoreAddress);
20168												}
20169											}
20170										}
20171
20172										var $containers = $('.add-to-cart-modal').find('.accordion, .section-offers');
20173										if ($containers.length > 0) {
20174											$containers.each(function() {
20175												var $container = $(this);
20176												if ($container.find('.product-tile').length > 0) {
20177													var $containerHeader = $container.find('.accordion-head h5, .section-head h2').first();
20178													var $containerHeaderCheck = $containerHeader.find('span');
20179													if ($containerHeaderCheck.length > 0) {
20180														$containerHeader = $containerHeaderCheck;
20181													}
20182										
20183													if ($containerHeader.length > 0) {
20184														var dataObject = {
20185															promotion_id: 'addtocartmodal',
20186															promotion_name: 'Add to Cart Modal'
20187															
20188														}
20189										
20190														GoogleTagManagerClient.PushDataLayerForDataLayerType('ViewPromotion', dataObject, { Container: $container });
20191													}
20192												}
20193											});
20194										}
20195
20196										if (pageContext.site === SiteConstants.SiteIds.BootBarnRspUS) {
20197											$.validator.addMethod('requiredExcludingPunctuation', ValidationHelper.ValidateRequiredExcludingPunctuation, Resources.INVALID_VALUE);
20198											$.validator.addMethod('zipCode', ValidationHelper.ValidateZipCode, Resources.INVALID_ZIP_CODE);
20199											$.validator.addMethod('postalCode', ValidationHelper.ValidatePostalCode, Resources.INVALID_POSTAL_CODE);
20200											var $addToCartModalForm = $('.add-to-cart-modal-wrapper form');
20201											var addToCartModalFormValidator = $addToCartModalForm.validate();
20202											addToCartModalFormValidator.settings.onfocusout = ValidationSettings.Checkout.onfocusout;
20203											addToCartModalFormValidator.settings.errorPlacement = ValidationSettings.Checkout.errorPlacement;
20204											if ($addToCartModalForm.find('select[name$="_country"]').val() == 'US') {
20205												$addToCartModalForm.find('input[name$="_postalCode"]').rules('add', ValidationRules.ZipCode);
20206											} else {
20207												$addToCartModalForm.find('input[name$="_postalCode"]').rules('add', ValidationRules.PostalCode);
20208											}
20209
20210											if (pageContext.options && pageContext.options.Lob && pageContext.options.Lob.AddressAutocomplete.Enabled) {
20211												AddressAutocompleteDialog = require('../../Lob/AddressAutocomplete/AddressAutocompleteDialog');
20212												$(document).on('keyup.AddressAutocomplete', 'input[type="text"]', function (event) {
20213													var $this = $(this);
20214													var $form = $this.closest('form');
20215
20216													if (event.keyCode == 8 || event.keyCode == 46) {
20217														$this.parent().find('.address-autocomplete-dialog').remove();
20218													} else if ($this.attr('id').indexOf('_address1') >= 0 || $this.attr('id').indexOf('_city') >= 0 || $this.attr('id').indexOf('_postalCode') >= 0) {
20219														var addressAutocompleteHandler = function (addressAutocompleteData) {
20220															AddressAutocompleteDialog.initialize(pageContext.options, addressAutocompleteData);
20221														}
20222													
20223														AddressVerificationHelper.AutocompleteAddress($form, $this, pageContext.options).then(addressAutocompleteHandler);
20224													} else {
20225														$this.parent().find('.address-autocomplete-dialog').remove();
20226													}
20227												});
20228												
20229												$(document).on('SetAddressFromAddressAutocomplete', function (event, data) {
20230													AddressVerificationHelper.SetAddressFromAddressVerificationDialog(data.$form, data.Address);
20231												});
20232											}
20233
20234											// if (pageContext.options && pageContext.options.Lob && pageContext.options.Lob.AddressVerification.Enabled) {
20235											// 	AddressVerificationDialog = require('../../Lob/AddressVerification/AddressVerificationDialog');
20236											// 	$(document).off('click.AddToCartGoToCart').on('click.AddToCartGoToCart', '.add-to-cart-modal-wrapper .btn.go-to-cart', function (event) {
20237											// 		event.preventDefault();
20238											// 		event.stopImmediatePropagation();
20239
20240											// 		var $addToCartModalForm = $(this).closest('.add-to-cart-modal-wrapper form');
20241											// 		var addressDeliverable = function ($form) {
20242											// 			submitForm();
20243											// 		}
20244
20245											// 		var addressNotDeliverable = function (addressVerifyData) {
20246											// 			AddressVerificationDialog.initialize($.extend({
20247											// 				$form: $addToCartModalForm,
20248											// 				successCallback: submitForm,
20249											// 				continueCallback: submitForm
20250											// 			}, pageContext.options), 'shipping', addressVerifyData);
20251											// 		}
20252											// 		AddressVerificationHelper.VerifyAddress($addToCartModalForm, pageContext.options).then(addressDeliverable, addressNotDeliverable);
20253											// 	});
20254
20255											// 	$(document).off('SetAddressFromAddressVerification').on('SetAddressFromAddressVerification', function (event, data) {
20256											// 		AddressVerificationHelper.SetAddressFromAddressVerificationDialog(data.$form, data);
20257											// 	});
20258											// }
20259
20260											$(document).on('change', '.add-to-cart-modal-wrapper #dwfrm_shippingaddress_country', function (event) {
20261												var $this = $(this);
20262												var $country = $this.find('option:selected');
20263												var country = $country.val();
20264												var stateListData = $country.data('options');
20265												var stateList;
20266												if (stateListData) {
20267													try {
20268														stateList = JSON.parse(stateListData.replace(/\'/g, '\"'));
20269													} catch (exception) { }
20270
20271													if (stateList) {
20272														var stateListHtml;
20273														var postalCodeType;
20274														var postalCodePlaceholder;
20275														if (country == 'US') {
20276															stateListHtml = '<option class="select-option" label="State" selected disabled>State</option>';
20277															postalCodeType = 'number';
20278															postalCodePlaceholder = 'zip code';
20279															$addToCartModalForm.find('input[name$="_postalCode"]').rules('add', 'postalCode zipCode');
20280															$addToCartModalForm.find('input[name$="_postalCode"]').rules('remove', ValidationRules.ZipCode);
20281														} else if (country == 'CA') {
20282															stateListHtml = '<option class="select-option" label="Province" selected disabled>Province</option>';
20283															postalCodeType = 'text';
20284															postalCodePlaceholder = 'postal code';
20285															$addToCartModalForm.find('input[name$="_postalCode"]').rules('remove', 'postalCode zipCode');
20286															$addToCartModalForm.find('input[name$="_postalCode"]').rules('add', ValidationRules.PostalCode);
20287														} else {
20288															stateListHtml = '<option class="select-option" label="Province/State" selected disabled>Province/State</option>';
20289															postalCodeType = 'text';
20290															postalCodePlaceholder = 'postal code';
20291															$addToCartModalForm.find('input[name$="_postalCode"]').rules('remove', 'postalCode zipCode');
20292															$addToCartModalForm.find('input[name$="_postalCode"]').rules('add', ValidationRules.PostalCode);
20293														}
20294														for (var stateListKey in stateList) {
20295															var state = stateList[stateListKey];
20296															stateListHtml += '<option class="select-option" label="' + state.label + '" value="' + state.code + '">' + state.label + '</option>';
20297														}
20298														$('.add-to-cart-modal-wrapper #dwfrm_shippingaddress_states_state').html(stateListHtml);
20299														$('.add-to-cart-modal-wrapper #addtocartmodal_postalCode').attr('type', postalCodeType);
20300														$('.add-to-cart-modal-wrapper #addtocartmodal_postalCode').attr('placeholder', postalCodePlaceholder);
20301													}
20302												}
20303											});
20304
20305											$(document).off('click.AddToCartGoToCart').on('click.AddToCartGoToCart', '.add-to-cart-modal-wrapper .btn.go-to-cart', function (event) {
20306												event.preventDefault();
20307												event.stopImmediatePropagation();
20308
20309												if (addToCartModalFormValidator.valid()) {
20310													$addToCartModalForm.trigger('submit');
20311												} else {
20312													$addToCartModalForm.find('input.required.field.error').eq(0).focus();
20313												}
20314											});
20315										}
20316
20317										$('.add-to-cart-modal-wrapper').addClass('show');
20318						            },
20319						            close: function(event, ui) {
20320						            	var $html = $('html');
20321								    	var $container = $('#wrapper');
20322
20323								    	$html.removeClass('scroll-lock');
20324
20325						            	if (dataBuffer['wrapperScrollTop'] > 0) {
20326							    			document.body.scrollTop = document.documentElement.scrollTop = dataBuffer['wrapperScrollTop'];
20327							    		}
20328										$container.css('margin-top', parseFloat(dataBuffer['wrapperMarginTop']));
20329
20330										$('.add-to-cart-modal').unwrap();
20331						            }
20332								}
20333							});
20334
20335							$('.add-to-cart-modal .added-message .count').text('Item');
20336						}
20337					}
20338				});
20339			}
20340			if ( $('.banner-cart-holder .btn-minicart').length > 0 ) {
20341				$.ajax({
20342					url : Urls.cartMiniCart,
20343					data : {desktop: '1'},
20344					dataType: 'html',
20345					success : function(data) {
20346						$('.banner-cart-holder .btn-minicart').remove();
20347						$('.banner-cart-holder').append(data);
20348					}
20349				});
20350				$.ajax({
20351					url : Urls.cartMiniCart,
20352					dataType: 'html',
20353					success : function(data) {
20354						var parentMiniCart = $('.top-banner-inner-links #mini-cart');
20355						if (parentMiniCart.length > 0) {
20356							$('.top-banner-inner-links #mini-cart a.mini-cart-link').remove();
20357							parentMiniCart.append(data);
20358						}
20359					}
20360				});
20361			}
20362			// do not close quickview if adding individual item that is part of product set
20363			// @TODO should notify the user some other way that the add action has completed successfully
20364			if (!$(this).hasClass('sub-product-item')) {
20365				dialog.close();
20366			}
20367			if(SitePreferences.ADD_TO_CART_MODAL != true){
20368				minicart.show(response);
20369			}
20370        }
20371    }.bind(this));
20372
20373};
20374
20375/**
20376 * @description Handler to handle the add all items to cart event
20377 */
20378var addAllToCart = function (e) {
20379    e.preventDefault();
20380    var $productForms = $('#product-set-list').find('form').toArray();
20381    TPromise.all(_.map($productForms, addItemToCart))
20382        .then(function (responses) {
20383            dialog.close();
20384            // show the final response only, which would include all the other items
20385            minicart.show(responses[responses.length - 1]);
20386        });
20387};
20388
20389/**
20390 * @function
20391 * @description Binds the click event to a given target for the add-to-cart handling
20392 */
20393module.exports = function () {
20394    $('.add-to-cart[disabled]').attr('title', $('.availability-msg').text());
20395    $('.product-detail').on('click', '.add-to-cart:not([disabled]):not([loading])', addToCart);
20396	$(document).on('ProductDetailPage.Bopis.AddItemToCart', addItemToCart);
20397    $(document).on('ProductDetailPage.Bopis.AddToCart', addToCart);
20398	$('#add-all-to-cart').on('click', addAllToCart);
20399};
20400
20401},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/AddressVerification/AddressVerificationHelper":112,"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../../../../bc_sheplersbootbarn_core/cartridge/scripts/util/ValidationHelper":124,"../../Lob/AddressAutocomplete/AddressAutocompleteDialog":1,"../../ajax":3,"../../dialog":36,"../../minicart":45,"../../page":46,"../../util":102,"lodash":133,"promise":134}],73:[function(require,module,exports){
20402'use strict';
20403
20404var ajax =  require('../../ajax'),
20405    util = require('../../util');
20406
20407var updateContainer = function (data) {
20408    var $availabilityMsg = $('#pdpMain .availability .availability-msg');
20409    var message; // this should be lexically scoped, when `let` is supported (ES6)
20410    if (!data) {
20411        $availabilityMsg.html(Resources.ITEM_STATUS_NOTAVAILABLE);
20412        return;
20413    }
20414    $availabilityMsg.empty();
20415    // Look through levels ... if msg is not empty, then create span el
20416    if (data.levels.IN_STOCK > 0) {
20417        if (data.levels.PREORDER === 0 && data.levels.BACKORDER === 0 && data.levels.NOT_AVAILABLE === 0) {
20418            // Just in stock
20419            message = Resources.IN_STOCK;
20420        } else {
20421            // In stock with conditions ...
20422            message = data.inStockMsg;
20423        }
20424        $availabilityMsg.append('<p class="in-stock-msg">' + message + '</p>');
20425    }
20426    if (data.levels.PREORDER > 0) {
20427        if (data.levels.IN_STOCK === 0 && data.levels.BACKORDER === 0 && data.levels.NOT_AVAILABLE === 0) {
20428            message = Resources.PREORDER;
20429        } else {
20430            message = data.preOrderMsg;
20431        }
20432        $availabilityMsg.append('<p class="preorder-msg">' + message + '</p>');
20433    }
20434    if (data.levels.BACKORDER > 0) {
20435        if (data.levels.IN_STOCK === 0 && data.levels.PREORDER === 0 && data.levels.NOT_AVAILABLE === 0) {
20436            message = Resources.BACKORDER;
20437        } else {
20438            message = data.backOrderMsg;
20439        }
20440        $availabilityMsg.append('<p class="backorder-msg">' + message + '</p>');
20441    }
20442    if (data.inStockDate !== '') {
20443        $availabilityMsg.append('<p class="in-stock-date-msg">' + String.format(Resources.IN_STOCK_DATE, data.inStockDate) + '</p>');
20444    }
20445    if (data.levels.NOT_AVAILABLE > 0) {
20446        if (data.levels.PREORDER === 0 && data.levels.BACKORDER === 0 && data.levels.IN_STOCK === 0) {
20447            message = Resources.NOT_AVAILABLE;
20448        } else {
20449            message = Resources.REMAIN_NOT_AVAILABLE;
20450        }
20451        $availabilityMsg.append('<p class="not-available-msg">' + message + '</p>');
20452    }
20453};
20454
20455var getAvailability = function () {
20456    ajax.getJson({
20457        url: util.appendParamsToUrl(Urls.getAvailability, {
20458            pid: $('#pid').val(),
20459            Quantity: $(this).val()
20460        }),
20461        //callback: updateContainer
20462    });
20463};
20464
20465module.exports = function () {
20466    $('#pdpMain').on('change', '.pdpForm input[name="Quantity"]', getAvailability);
20467};
20468
20469},{"../../ajax":3,"../../util":102}],74:[function(require,module,exports){
20470'use strict';
20471var dialog = require('../../dialog');
20472var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
20473var util = require('../../util');
20474
20475var zoomMediaQuery = matchMedia('(min-width: 960px)');
20476
20477/**
20478 * @description Enables the zoom viewer on the product detail page
20479 * @param zmq {Media Query List}
20480 */
20481var loadZoom = function (zmq) {
20482    var $imgZoom = $('#pdpMain .main-image'),
20483        hiresUrl;
20484    if (!zmq) {
20485        zmq = zoomMediaQuery;
20486    }
20487    if ($imgZoom.length === 0 || dialog.isActive() || util.isMobile() || !zoomMediaQuery.matches) {
20488        // remove zoom
20489        $imgZoom.trigger('zoom.destroy');
20490        return;
20491    }
20492    hiresUrl = $imgZoom.attr('href');
20493
20494    if (hiresUrl && hiresUrl !== 'null' && hiresUrl.indexOf('noimagelarge') === -1 && zoomMediaQuery.matches) {
20495        $imgZoom.zoom({
20496            url: hiresUrl
20497        });
20498    }
20499};
20500
20501zoomMediaQuery.addListener(loadZoom);
20502
20503/**
20504 * @description Sets the main image attributes and the href for the surrounding <a> tag
20505 * @param {Object} atts Object with url, alt, title and hires properties
20506 */
20507var setMainImage = function (atts) {
20508    $('#pdpMain .primary-image').attr({
20509        src: atts.url,
20510        alt: atts.alt,
20511        title: atts.title
20512    });
20513    if (!dialog.isActive() && !util.isMobile()) {
20514        $('#pdpMain .main-image').attr('href', atts.hires);
20515    }
20516    loadZoom();
20517};
20518
20519/**
20520 * @description Replaces the images in the image container, for eg. when a different color was clicked.
20521 */
20522
20523var replaceImages = function () {
20524	
20525    var $newImages = $('#update-images'),
20526        $win = $(window),
20527        $imageContainer,
20528        $header,
20529        $promosClone,
20530        $promosCloneMobile,
20531        newImage = new Image();
20532    
20533    if ($win.width() <= 1024) {
20534    	$header =  $('#pdpMain .product-image-container').find('header').clone();
20535    }
20536    if ($newImages.length === 0) { return; }
20537
20538    // product container classes to handle styles while image is loading
20539    $('#pdpMain .product-image-container').addClass('loading-image');
20540    newImage.src = $newImages.find('.product-primary-image [class*="slide"]').first().find('img').attr('src');
20541    newImage.onload = function() {
20542        $('#pdpMain .product-image-container').removeClass('loading-image');
20543    }
20544    
20545    var attachPromosMobile = function() {
20546        if (pageContext.site === SiteConstants.SiteIds.SheplersUS || pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
20547            var $imageContainerMobile = $('#pdpMain .product-image-container');
20548        } else {
20549            var $imageContainerMobile = $('#pdpMain .product-image-container-mobile');
20550        }
20551        var $promoMessagesMobile = $('#pdpMain .product-image-container-mobile .promo-messages'),
20552            $promosCloneMobile = $promoMessagesMobile.find('.promo-messages').clone();
20553        
20554        if ($promoMessagesMobile.is(':empty') && $promosCloneMobile.length > 0) {
20555            $imageContainerMobile.html($newImages.html());
20556            $("<div class='promo-messages'></div>").prependTo($imageContainerMobile);
20557            $promosCloneMobile.prependTo($promoMessagesMobile);
20558        }
20559        else {
20560            $imageContainerMobile.html($newImages.html());
20561            $promosCloneMobile.prependTo($imageContainerMobile);
20562        }
20563    };
20564
20565    var attachPromosDesktop = function() {
20566        var $imageContainer = $('#pdpMain .product-image-container'),
20567            $promoMessages = $('#pdpMain .product-image-container .promo-messages'),
20568            $promosClone = $promoMessages.clone();
20569        
20570        //window.console.log("$promoMessages: ",$promoMessages.length, " $promosClone.length: ", $promosClone.length);
20571        
20572        if ($promoMessages.is(':empty') && $promosClone.length > 0) {
20573            $imageContainer.html($newImages.html());
20574            $("<div class='promo-messages'></div>").prependTo($imageContainer);
20575            $promosClone.prependTo($promoMessages);
20576        }
20577        else {
20578            $imageContainer.html($newImages.html());
20579            $promosClone.prependTo($imageContainer);
20580        }
20581    };
20582
20583    if ($win.width() <= 767 ) {
20584        attachPromosMobile();
20585    }
20586    else {
20587        attachPromosDesktop();
20588    }
20589    
20590    $newImages.remove();
20591    loadZoom();
20592};
20593
20594/* @module image
20595 * @description this module handles the primary image viewer on PDP
20596 **/
20597
20598/**
20599 * @description by default, this function sets up zoom and event handler for thumbnail click
20600 **/
20601module.exports = function () {
20602    if (dialog.isActive() || util.isMobile()) {
20603        $('#pdpMain .main-image').removeAttr('href');
20604    }
20605    loadZoom();
20606    // handle product thumbnail click event
20607    $('#pdpMain').on('click', '.productthumbnail', function () {
20608        // switch indicator
20609        $(this).closest('.product-thumbnails').find('.thumb.selected').removeClass('selected');
20610        $(this).closest('.thumb').addClass('selected');
20611
20612        setMainImage($(this).data('lgimg'));
20613    });
20614};
20615module.exports.loadZoom = loadZoom;
20616module.exports.setMainImage = setMainImage;
20617module.exports.replaceImages = replaceImages;
20618
20619},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../dialog":36,"../../util":102}],75:[function(require,module,exports){
20620'use strict';
20621
20622var ApplePay = require('../checkout/ApplePay');
20623var dialog = require('../../dialog');
20624var productStoreInventory = require('../../storeinventory/product');
20625var tooltip = require('../../tooltip');
20626var util = require('../../util');
20627var addToCart = require('./addToCart');
20628var availability = require('./availability');
20629var image = require('./image');
20630var video = require('./video');
20631var productSet = require('./productSet');
20632var variant = require('./variant');
20633var recaptcha = require('../../components/Recaptcha');
20634var recentlyViewedProducts = require('../../components/RecentlyViewedProducts');
20635var relevantProductRecommendations = require('../../components/RelevantProductRecommendations');
20636var sameDayShippingCountdown = require('../../components/SameDayShippingCountdown');
20637var ShopPay = require('../../../../../int_shoppay_sfra/cartridge/client/default/js/cart/initShopPayCart');
20638var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
20639
20640var options = {};
20641var loaded = {};
20642var dataBuffer = {};
20643
20644/**
20645 * @description Initialize product detail page with reviews, recommendation and product navigation.
20646 */
20647function initializeDom(options) {
20648	tooltip.init();
20649
20650	var $sameDayShippingCountdownContainer = $('.same-day-shipping-countdown');
20651	if ($sameDayShippingCountdownContainer.length > 0) {
20652		sameDayShippingCountdown.initialize($sameDayShippingCountdownContainer, options.SameDayShipping);
20653	}
20654
20655	$('.accordion').each(function() {
20656		var $this = $(this);
20657		if (!$this.hasClass('product-features') || window.innerWidth <= 768) {
20658			$this.find('.accordion-head').addClass('active');
20659			$this.find('.accordion-body').css('display', 'block');
20660		}
20661	});
20662}
20663
20664/**
20665 * @description Initialize event handlers on product detail page
20666 */
20667function initializeEvents(options) {
20668    var $pdpMain = $('#pdpMain');
20669
20670    addToCart();
20671    availability();
20672    image();
20673    video();
20674    productSet();
20675
20676    if (SitePreferences.STORE_PICKUP) {
20677        productStoreInventory.init();
20678    }
20679
20680	$(document).on('click', '.product-swatches-accordion-button', function () {
20681        var $accordion = $(this).closest('.accordion');
20682
20683        if ($accordion.length > 0) {
20684            $accordion.toggleClass('active');
20685        }
20686    });
20687
20688    // Add to Wishlist and Add to Gift Registry links behaviors
20689    $pdpMain.on('click', '[data-action="gift-registry"]', function () {
20690        var data = util.getQueryStringParams($('.pdpForm').serialize());
20691        if (data.cartAction) {
20692            delete data.cartAction;
20693        }
20694        var url = util.appendParamsToUrl(this.href, data);
20695        this.setAttribute('href', url);
20696    });
20697
20698    // product options
20699    $pdpMain.on('change', '.product-options select', function () {
20700        var salesPrice = $pdpMain.find('.product-add-to-cart .price-sales');
20701        var selectedItem = $(this).children().filter(':selected').first();
20702        salesPrice.text(selectedItem.data('combined'));
20703    });
20704
20705    // prevent default behavior of thumbnail link and add this Button
20706    $pdpMain.on('click', '.thumbnail-link, .unselectable a', function (e) {
20707        e.preventDefault();
20708    });
20709
20710    $pdpMain.on('click', 'a.size-chart-link', function (e) {
20711        e.preventDefault();
20712        dialog.open({
20713            url: $(e.target).attr('href')
20714        });
20715    });
20716
20717    // Technology Features Tooltip
20718    // $('.list-product-info figure img').each(function() {
20719    // 	var $img = $(this),
20720    // 		$target = $img.parents('li').first().find('.tooltip-content img');
20721
20722    // 	$target.attr("src", $img.data('hover')).attr("alt", $img.attr("alt")).attr("width", "236px");
20723    // });
20724
20725    // $('.list-product-info').tooltip({
20726    //     items: 'li',
20727    //     tooltipClass: 'techfeature-tooltip',
20728    //     track: true,
20729    //     content: function () {
20730    //         return $(this).find('.tooltip-content').html();
20731    //     }
20732    // });
20733
20734
20735	$(document).on('click','.searchagain-btn',function() {
20736		$('#dialog-container .findStore-result').hide();
20737		$('#dialog-container .ui-dialog-footer').show();
20738		$('.ui-dialog').css({'height' : '230px !important','top':'260px !important'});
20739	});
20740
20741	$(document).on('click','.find-store-btn',function() {
20742		dialog.open({
20743			html:($('.findStoreDialog').html()).replace(/select-holder/g,''),
20744			callback: function() {
20745				$(document).on('click','.findstore-formContainer .findStore-btn',function() {
20746					var zipcode    = $('#dialog-container input.zipcode').val(),
20747						radius     = $('#dialog-container select.radius').val(),
20748						postalcode = $('#dialog-container .postalcode select').val(),
20749						statecode = $('#dialog-container .statecode select').val(),
20750						pid        = $('#dialog-container #pid').val(),
20751						values 	   = {
20752										pid : pid,
20753										zipCode : zipcode,
20754										radius : radius,
20755										statecode : statecode,
20756										postalcode : postalcode
20757										};
20758
20759					$.ajax({
20760						url: Urls.storeavailability,
20761						type: "GET",
20762						data: values ,
20763						success: function (response) {
20764							$('#dialog-container .findStore-result').show();
20765							$('#dialog-container .ui-dialog-footer').hide();
20766							$('#dialog-container .findStore-result').html(response).text();
20767
20768						},
20769						error: function(jqXHR, textStatus, errorThrown) {
20770							console.log(textStatus, errorThrown);
20771						}
20772					});
20773				});
20774			},
20775			options: {
20776				title: Resources.FINDSTORE_TITLE
20777			},
20778		});
20779		return false;
20780	});
20781
20782	$(document).off('RecentlyViewedProducts.Done').on('RecentlyViewedProducts.Done', function(event) {
20783		loaded.RecentlyViewedProducts = true;
20784		addPromotionDataLayers();
20785	});
20786
20787	$(document).off('EinsteinProductRecommendations.Done').on('EinsteinProductRecommendations.Done', function(event) {
20788		loaded.EinsteinProductRecommendations = true;
20789		addPromotionDataLayers();
20790	});
20791
20792	$(document).off('RelevantProductRecommendations.Done').on('RelevantProductRecommendations.Done', function(event) {
20793		loaded.RelevantProductRecommendations += 1;
20794		addPromotionDataLayers();
20795	});
20796
20797	$(document).off('SFCCProductRecommendations.Done').on('SFCCProductRecommendations.Done', function(event) {
20798		loaded.SFCCProductRecommendations = true;
20799		addPromotionDataLayers();
20800	});
20801}
20802
20803function addPromotionDataLayers() {
20804	if (loaded.RecentlyViewedProducts && loaded.EinsteinProductRecommendations && loaded.SFCCProductRecommendations && loaded.RelevantProductRecommendations == dataBuffer['RelevantProductRecommendationsCount']) {
20805		$(document).trigger('GoogleTagManager.AddPromotionDataLayers');
20806	}
20807}
20808
20809var product = {
20810    initializeEvents: initializeEvents,
20811    init: function (optionsParameter) {
20812    	options = optionsParameter;
20813		loaded = {
20814			RecentlyViewedProducts: false,
20815			EinsteinProductRecommendations: false,
20816			RelevantProductRecommendations: 0
20817		};
20818		dataBuffer = {
20819			RelevantProductRecommendationsCount: 0
20820		};
20821
20822		if (options && options.RecentlyViewedProducts) {
20823    		recentlyViewedProducts.initialize(options.RecentlyViewedProducts);
20824    	} else {
20825			loaded.RecentlyViewedProducts = true;
20826		}
20827
20828		if (!options || !options.EinsteinProductRecommendations) {
20829			loaded.EinsteinProductRecommendations = true;
20830		}
20831
20832		if (options && options.RelevantProductRecommendations) {
20833			dataBuffer['RelevantProductRecommendationsCount'] = options.RelevantProductRecommendations.Containers.length;
20834			relevantProductRecommendations.initialize(options.RelevantProductRecommendations);
20835		}
20836
20837		variant.initialize(options);
20838
20839		initializeDom(options);
20840		initializeEvents(options);
20841		ApplePay.initialize(options);
20842	}
20843};
20844
20845module.exports = product;
20846
20847},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../int_shoppay_sfra/cartridge/client/default/js/cart/initShopPayCart":126,"../../components/Recaptcha":29,"../../components/RecentlyViewedProducts":30,"../../components/RelevantProductRecommendations":31,"../../components/SameDayShippingCountdown":32,"../../dialog":36,"../../storeinventory/product":98,"../../tooltip":101,"../../util":102,"../checkout/ApplePay":53,"./addToCart":72,"./availability":73,"./image":74,"./productSet":76,"./variant":77,"./video":78}],76:[function(require,module,exports){
20848'use strict';
20849
20850var ajax = require('../../ajax'),
20851    tooltip = require('../../tooltip'),
20852    util = require('../../util');
20853
20854module.exports = function () {
20855    var $addToCart = $('#add-to-cart'),
20856        $addAllToCart = $('#add-all-to-cart'),
20857        $productSetList = $('#product-set-list');
20858
20859    var updateAddToCartButtons = function () {
20860        if ($productSetList.find('.add-to-cart[disabled]').length > 0) {
20861            $addAllToCart.attr('disabled', 'disabled');
20862            // product set does not have an add-to-cart button, but product bundle does
20863            $addToCart.attr('disabled', 'disabled');
20864        } else {
20865            $addAllToCart.removeAttr('disabled');
20866            $addToCart.removeAttr('disabled');
20867        }
20868    };
20869
20870    if ($productSetList.length > 0) {
20871        updateAddToCartButtons();
20872    }
20873    // click on swatch for product set
20874    $productSetList.on('click', '.product-set-item .swatchanchor', function (e) {
20875        e.preventDefault();
20876        if ($(this).parents('li').hasClass('unselectable')) { return; }
20877        var url = Urls.getSetItem + this.search;
20878        var $container = $(this).closest('.product-set-item');
20879        var qty = $container.find('form input[name="Quantity"]').first().val();
20880
20881        ajax.load({
20882            url: util.appendParamToURL(url, 'Quantity', isNaN(qty) ? '1' : qty),
20883            target: $container,
20884            callback: function () {
20885                updateAddToCartButtons();
20886                tooltip.init();
20887            }
20888        });
20889    });
20890};
20891
20892},{"../../ajax":3,"../../tooltip":101,"../../util":102}],77:[function(require,module,exports){
20893'use strict';
20894
20895var _ = require('lodash');
20896var ajax = require('../../ajax');
20897var image = require('./image');
20898var GoogleTagManagerClient = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
20899var productStoreInventory = require('../../storeinventory/product');
20900var rating = require('../../rating');
20901var tooltip = require('../../tooltip');
20902var util = require('../../util');
20903var bopisAddToCartOptions = require('../../bopis/components/AddToCartOptions');
20904var bopisStoreSearch = require('../../bopis/components/StoreSearch');
20905var wishlist = require('../../wishlist');
20906var sliders = require('../../sliders');
20907var imageGallery;
20908
20909var SiteConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
20910var TemplateConstants = require('../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
20911
20912var html = {};
20913var data = {};
20914var loaded = {};
20915var options;
20916
20917function initializeDom() {
20918	loaded['content'] = true;
20919	
20920	updateCSRFToken();
20921
20922	var selectorPayload = util.getQueryStringParams($('.pdpForm').serialize());
20923	
20924	var queryStringParameters = util.getQueryStringParams(util.getQueryString(util.appendParamsToUrl(window.location.toString(), selectorPayload)));
20925	for (var parameter in queryStringParameters) {
20926		selectorPayload[parameter] = queryStringParameters[parameter];
20927	}
20928
20929	var sizeValue = util.getPreferenceValueFromWindowLocation('size');
20930	if (sizeValue) {
20931		selectorPayload.setSize = sizeValue;
20932	}
20933	
20934    buildSelectors(selectorPayload);
20935	$('.btn.add-to-cart').removeAttr('disabled').removeAttr('loading').find('.loading-indicator-container').remove();
20936}
20937
20938function updateProductInfo() {
20939	$('li.selected-value').each(function() {
20940		var $selectedItem = $(this);
20941		$selectedItem.parents('.attribute').first().find('.label .current, .select-dropdown .select-text').html($selectedItem.html());
20942	});
20943}
20944
20945function reloadSubdimensionsWidget(subdimensionHTML) {
20946	$('#tt-subdimension-teaser').html(subdimensionHTML);
20947}
20948
20949function updateCSRFToken() {
20950    $.ajax({
20951    	method: 'get',
20952    	url: Urls.CSRFToken.Get,
20953		dataType: 'json',
20954    	success: function(response) {
20955    		$('#csrftoken').val(response.token);
20956    	}
20957    });
20958}
20959
20960function updatePid() {
20961	if (data['pid']) {
20962		$('#pid').val(data['pid']);
20963	}
20964}
20965
20966function buildSelectors(payload) {
20967	loaded['selectors'] = false;
20968	
20969	if (!payload) {
20970		payload = util.getQueryStringParams($('.pdpForm').serialize());
20971	}
20972    
20973    var templates = [];
20974    templates['variations'] = _.template($('#template-variations').html(), TemplateConstants.Lodash.Settings);
20975    templates['variationSelector'] = _.template($('#template-variations-selector').html(), TemplateConstants.Lodash.Settings);
20976	templates['variationSelectorAttributeGuide'] = _.template($('#template-variations-attribute-guide').html(), TemplateConstants.Lodash.Settings);
20977    templates['variationSelectorDropdown'] = _.template($('#template-variations-selector-dropdown').html(), TemplateConstants.Lodash.Settings);
20978    templates['variationSelectorDropdownOption'] = _.template($('#template-variations-selector-dropdown-option').html(), TemplateConstants.Lodash.Settings);
20979    templates['variationSelectorDropdownOptionSelected'] = _.template($('#template-variations-selector-dropdown-option-selected').html(), TemplateConstants.Lodash.Settings);
20980    templates['variationSelectorSwatch'] = _.template($('#template-variations-selector-swatch').html(), TemplateConstants.Lodash.Settings);
20981    templates['variationSelectorSwatchOption'] = _.template($('#template-variations-selector-swatch-option').html(), TemplateConstants.Lodash.Settings);
20982    templates['variationSelectorSwatchOptionUnselectable'] = _.template($('#template-variations-selector-swatch-option-unselectable').html(), TemplateConstants.Lodash.Settings);
20983    
20984	data['attributeGuides'] = {};
20985
20986    $.ajax({
20987    	method: 'post',
20988    	url: Urls.getVariationAttributes,
20989    	data: payload,
20990		dataType: 'json',
20991    	success: function(response) {
20992			html['variationSelectors'] = '';
20993			data['IsRestrictedByGlobale'] = response.IsRestrictedByGlobale;
20994
20995			// Color Swatch Length Check
20996			var isMultiSwatch;
20997			if (response && 'variations' in response && response.variations.length) {
20998				isMultiSwatch = response.variations.filter(function(variantGroup) {
20999					if (variantGroup && 'type' in variantGroup && 'values' in variantGroup) {
21000						return variantGroup.type == 'swatch' && variantGroup.values.length > 1; // Check for multiple color swatches
21001					}
21002				});
21003			}
21004			var hasSizeAttribute = false;
21005			if (response && 'variations' in response && response.variations.length) {
21006				hasSizeAttribute = response.variations.filter(function(variantGroup) {
21007					if (variantGroup && 'productVariationAttribute' in variantGroup && 'values' in variantGroup) {
21008						return variantGroup.productVariationAttribute == 'size' && variantGroup.values.length > 1;
21009					}
21010				});
21011			}
21012
21013			var variations;
21014			if (hasSizeAttribute) {
21015				variations = response.variations;
21016			} else {
21017				variations = response.variations;
21018			}
21019			for (var variation in response.variations) {
21020				var variationData = response.variations[variation];
21021				var variationDataValueSelected = null;
21022				
21023				html['variationSelectorWidgetOptions'] = '';
21024								
21025				for (var variationValue in variationData.values) {
21026					var variationDataValue = variationData.values[variationValue];
21027					var variationDataValueTemplate;
21028					var variationText = (variationDataValue.attribute === 'size') ? variationDataValue.text.replace(' 1/2', '\u00BD') : variationDataValue.text;
21029					var variationReviewsWidget = (variationDataValue.attribute === 'size' && SitePreferences.TURNTO_ENABLED && SitePreferences.TURNTO_SUBDIMENSIONS_ENABLED) ? '<div id="tt-subdimension-teaser"></div>' : '';
21030					var widthDescription = variationDataValue.widthDescription ? '<span class="width-description">' + variationDataValue.widthDescription + '</span>' : '';
21031					var optionAriaLabel = variationDataValue.value;
21032					if (variationDataValue.shipping) {
21033						optionAriaLabel += + ' - ' + variationDataValue.shipping;
21034					}
21035
21036					var variationDataValuePayload = {
21037						attribute: variationDataValue.attribute,
21038						valueId: variationDataValue.valueId,
21039						value: variationDataValue.value,
21040						title: variationDataValue.title,
21041						optionAriaLabel: optionAriaLabel,
21042						text: variationText,
21043						widthDescription: widthDescription,
21044						stock: variationDataValue.stock,
21045						shipping: variationDataValue.shipping,
21046						isSameDayShippingWindow: variationDataValue.isSameDayShippingWindow,
21047						quantitySourceType: variationDataValue.quantitySourceType,
21048						href: variationDataValue.href,
21049						selectable: variationDataValue.selectable ? 'selectable' : 'unselectable',
21050						selected: variationDataValue.selected ? 'selected' : '',
21051						swatchImageUrl: variationDataValue.images.swatch.url,
21052						largeSwatchImageUrl: variationDataValue.images.largeSwatch.url,
21053						hrefAttribute: variationDataValue.selectable ? 'href' : 'xhref'
21054					};
21055					
21056					if (variationData.type == 'swatch') {
21057						if (variationDataValue.selectable) {
21058							variationDataValueTemplate = templates['variationSelectorSwatchOption'];
21059						
21060							variationDataValuePayload.imageLarge = JSON.stringify({
21061								url: variationDataValue.images.large.url,
21062								title: variationDataValue.images.large.title,
21063								alt: variationDataValue.images.large.alt,
21064								hires: variationDataValue.images.hires.url
21065							});
21066						} else {
21067							variationDataValueTemplate = templates['variationSelectorSwatchOptionUnselectable'];
21068						}
21069					} else {
21070						variationDataValueTemplate = templates['variationSelectorDropdownOption'];
21071						
21072						if (variationDataValue.selected) {
21073							variationDataValueSelected = variationDataValue;
21074						}
21075					}
21076				
21077					html['variationSelectorWidgetOptions'] += variationDataValueTemplate(variationDataValuePayload);	
21078				}	
21079				
21080				// Add color swatch length boolean to template data
21081				if (variationData.type == 'swatch') {
21082					html['variationSelectorWidget'] = templates['variationSelectorSwatch']({
21083						attribute: variationData.productVariationAttribute,
21084						options: html['variationSelectorWidgetOptions'],
21085						multiSwatchDisplay: isMultiSwatch.length > 0 ? 'd-block' : 'd-none'
21086					});
21087				} else {
21088					var placeholder = variationData.label.placeholder,
21089						ariaLabel = variationData.label.placeholder;
21090
21091					if (pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS && variationData.productVariationAttribute === 'size' && variationDataValueSelected === null) {
21092						placeholder += ' (' + variationData.label.message + ')';
21093					}
21094
21095					if (variationDataValueSelected) {
21096						var variationSelectedText = (variationData.productVariationAttribute === 'size') ? variationDataValueSelected.text.replace(' 1/2', '\u00BD') : variationDataValueSelected.text;
21097						var widthDescription = variationDataValueSelected.widthDescription ? '<span class="width-description">' + variationDataValueSelected.widthDescription + '</span>' : '';
21098
21099						placeholder = templates['variationSelectorDropdownOptionSelected']({
21100							text: variationSelectedText,
21101							widthDescription: widthDescription,
21102							stock: variationDataValueSelected.stock,
21103							shipping: variationDataValueSelected.shipping,
21104							isSameDayShippingWindow: variationDataValueSelected.isSameDayShippingWindow,
21105							quantitySourceType: variationDataValueSelected.quantitySourceType
21106						});
21107
21108						ariaLabel = 'Currently Selected: ' + variationDataValueSelected.value; 
21109						if (variationDataValueSelected.shipping) {
21110							ariaLabel += ' - ' + variationDataValueSelected.shipping;
21111						}
21112					}
21113
21114					// Add color swatch length boolean to template data
21115					html['variationSelectorWidget'] = templates['variationSelectorDropdown']({
21116						attribute: variationData.productVariationAttribute,
21117						text: placeholder,
21118						ariaLabel: ariaLabel,
21119						options: html['variationSelectorWidgetOptions'],
21120						multiSwatchDisplay: isMultiSwatch.length > 0 ? 'd-block' : 'd-none'
21121					});
21122				}
21123
21124				html['attributeGuide'] = '';
21125				if (variationData.attributeGuide) {
21126					data['attributeGuides'][variationData.productVariationAttribute] = variationData.attributeGuide;
21127					html['attributeGuide'] = templates['variationSelectorAttributeGuide'](_.extend(variationData.attributeGuide, { Attribute: variationData.productVariationAttribute }));
21128				}
21129				
21130				html['variationSelector'] = templates['variationSelector']({
21131					attribute: variationData.productVariationAttribute,
21132					label: {
21133						text: variationData.label.text,
21134						message: variationData.label.message
21135					},
21136					selector: html['variationSelectorWidget'],
21137					reviewsWidget: variationReviewsWidget,
21138					guide: html['attributeGuide']
21139				});
21140
21141				html['variationSelectors'] += html['variationSelector'];
21142			}
21143			
21144			html['selectors'] = templates['variations']({
21145				selectors: html['variationSelectors']
21146			});
21147			
21148			data['selected'] = response.selected;
21149			data['pid'] = response.pid;
21150			
21151			loaded['selectors'] = true;
21152			renderVariationUpdates();
21153    	}
21154	});
21155}
21156
21157function setAlternates() {
21158	$('ul.alternate-items li').sort(function(a,b) { return $(a).data('id') - $(b).data('id'); }).appendTo('.alternate-items');
21159	var altType = $('ul.alternate-items').data('alternate');
21160	var altDefault = $('ul.alternate-items').data('default');
21161	var alternates = [];
21162
21163	var isSubscriptionProduct = $('.pdp-main').hasClass('subscription-product-template');
21164	var colorSelected = $('.color-options .selected a');
21165	var sizeSelected = $('.size-options .selected a').data('size-id');
21166
21167	if (colorSelected && isSubscriptionProduct) {
21168		alternates.push({'altID': $(colorSelected).parent('.selected').data('id'), 'altValue': $(colorSelected).attr('data-color-name')});
21169	}
21170	$('ul.alternate-items li').each(function(i, elem) {
21171		alternates.push({'altID': $(elem).data('id'), 'altValue': $(elem).text().split('-')[1]});
21172	});
21173
21174	if (sizeSelected && sizeSelected.length > 0 && $('.alternate').length > 0){
21175		updateAlternates(sizeSelected);
21176	} else if (alternates && alternates.length > 0){
21177		$.ajax({
21178			url: Urls.getItemAlternates,
21179			dataType: 'JSON',
21180			method: 'POST',
21181			data: {
21182				'alternates': JSON.stringify(alternates)
21183			},
21184			success : function(data) {
21185				var altSwatches = '';
21186				var alternate = data.result;
21187				
21188				$.each(alternate, function(i, e){
21189					var id = alternate[i].id;
21190					var type = altType ? altType : 'Color';
21191					var value = alternate[i].value;
21192					var image = alternate[i].image;
21193					var url = $('.alternate-items li[data-id='+id+']').data('url');
21194					var altHidden = 'show-content';
21195					if ( i >= 11 ){
21196						altHidden = 'hidden-content';
21197					}
21198					if ( isSubscriptionProduct && id == $(colorSelected).parent('.selected').data('id') ){
21199						$(colorSelected).css('background', 'url('+image+')');
21200					} else {
21201						altSwatches += '<li class="unselectable alternate ' + altHidden + '" data-id="'+id+'" data-type="'+altType+'" data-value="'+value+'"><a class="swatchanchor" href="'+url+'" title="Select '+altType+': '+value+'" value="'+value+'" style="background: url('+image+'?sw=70&amp;sh=70&amp;sm=fit); background-repeat: no-repeat; background-size: contain; background-position: center;"><span class="visually-hidden">Select '+altType+':'+value+'</span></a></li>';
21202					}
21203				});
21204				
21205				if (altSwatches.length > 0 ){
21206					$('.color.color-options').removeClass('color-d-none').append(altSwatches).find('li').sort(function(a,b) { return $(a).data('id') - $(b).data('id'); }).appendTo('.color-options');
21207					if (altType && altDefault){
21208						$('.color-options .selected').attr('data-type',altType).attr('data-value',altDefault).find('a.swatchanchor').attr('title','Select '+altType+': '+altDefault);
21209						var newText = '<span class="text">'+altType+'<span class="separator">:</span> <span class="current color-current">'+altDefault+'</span></span>';
21210						$('.attribute-color .text').html(newText);
21211					}
21212				}
21213				
21214				if (sizeSelected && sizeSelected.length > 0){
21215					updateAlternates(sizeSelected);
21216				}
21217				
21218				var altHiddenCount = $('.hidden-content').length;
21219				var altHiddenToggle = '+ ' + altHiddenCount;
21220				if (altHiddenCount > 0){
21221					$('.hidden-content').hide();
21222					$('.color.color-options').append('<button class="altButton">' + altHiddenToggle + '</button>');
21223				}
21224
21225				$('button.altButton').on('click', function(e){
21226					e.preventDefault();
21227					$(this).closest('.color-options').find('.hidden-content').toggle();
21228					$('.altButton').hide();
21229				})
21230				
21231				$(".color-options li").hover(function(){
21232					var altTitle = $(this).data('type');
21233					var altValue = $(this).data('value');
21234					var newText = '<span class="text">'+altType+'<span class="separator">:</span> <span class="current color-current">'+altValue+'</span></span>';
21235					$('.attribute-color .text').html(newText);
21236				}, function(){
21237					var altTitle = $('.color-options .selected').data('type');
21238					var altValue = $('.color-options .selected').data('value');
21239					var newText = '<span class="text">'+altType+'<span class="separator">:</span> <span class="current color-current">'+altValue+'</span></span>';
21240					$('.attribute-color .text').html(newText);
21241				});
21242			}
21243		});
21244	}
21245}
21246
21247function updateAlternates(sizeSelected) {
21248	var altupdates = [];
21249	$('ul.color-options li').each(function(i, elem) {
21250		altupdates.push(String($(elem).data('id')));
21251	});
21252	
21253	if(altupdates && altupdates.length > 0){
21254		$.ajax({
21255			url: Urls.updateItemAlternates,
21256			dataType: 'JSON',
21257			method: 'POST',
21258			data: {
21259				'altupdates': JSON.stringify(altupdates),
21260				'size': sizeSelected
21261			},
21262			success : function(data) {
21263				var alternate = data.result;
21264				
21265				$.each(alternate, function(i, e){
21266					var id = alternate[i].id;
21267					var appendURL = alternate[i].appendURL;
21268					var sizeAvailable = !alternate[i].sizeAvailable;
21269					var oldURL = $('ul.color-options li[data-id='+id+'] a').attr('href');
21270					var newURL = oldURL + appendURL;
21271					$('ul.color-options li.alternate[data-id="'+id+'"] a').attr('href',newURL);
21272					if(appendURL == null || (typeof appendURL === "string" && appendURL.trim().length === 0)){
21273						$('ul.color-options li[data-id='+id+'] a').addClass('no-size');
21274					}
21275				});
21276			}
21277		});
21278	}
21279}
21280
21281function renderVariationUpdates() {
21282	if (loaded['content'] && loaded['selectors']) {
21283		renderSelectors();
21284		updatePid();
21285		setAlternates();
21286		
21287		$(document).trigger('SameDayShippingCountdown.Update');
21288		
21289		var sizeSelected = $('div.product-variations .size.size-options li.selected');
21290		var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
21291		var widthSelected = $('div.product-variations .width.width-options li.selected').length;
21292		var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
21293		var isColorSelected = $('div.product-variations .color.color-options li.selected').length;
21294		
21295		updateWishlistButton(sizeSelected);
21296
21297		if (isSizeSelected) {
21298			$('.attribute-size').addClass('size-selected');
21299		} else {
21300			$('.attribute-size').removeClass('size-selected');
21301		}
21302	    if (isSizeSelected && isColorSelected) {
21303			$('.btn.store-search').removeAttr('disabled');
21304
21305	    	$(document).trigger('ProductDetailPage.Variant.Selected');
21306			$(document).trigger('ProductDetailPage.Variant.AddToCartOptions');
21307			$(document).trigger('ProductDetailPage.Variant.StoreSearch');
21308			$(document).trigger('Currency.Update', $('#product-content'));
21309	    }
21310	    
21311    	var $selectableCount = 0;
21312    	$('.product-options .attribute-size .select-dropdown ul.size-options li, .product-options .attribute-size .select-dropdown ul.length-options li').each(function() {
21313        	var $selectClass = $(this).attr('class').replace('selected','').trim();
21314        	if( $selectClass == 'selectable'){
21315        		$selectableCount++;
21316        	}        
21317        });
21318
21319		if (data['IsRestrictedByGlobale'] || $selectableCount == 0) {
21320			$('.same-day-shipping-countdown').addClass('hidden');
21321			$('.product-options .attribute-size .select-dropdown').css({"pointer-events": "none", "cursor": "default"});
21322			$('.product-variation').addClass('not-available');
21323			if ($('.color-options .selectable').length > 1 || $('.alternate-items li').length > 1) {
21324				$('.product-variation').addClass('colors-selectable');
21325			}
21326			if (data['IsRestrictedByGlobale']) {
21327				$('.product-globale-unavailable').removeClass('hidden');
21328			} else if ($selectableCount == 0) {
21329				$('.product-out-of-stock').removeClass('hidden');
21330			}
21331		}
21332
21333		$(document).trigger('ProductDetailPage.Variant.Updated', { ProductId: data['pid'] });
21334	}
21335}
21336
21337function renderSelectors() {
21338	$('.product-variations').html(html['selectors']);
21339	$('.product-variations').attr('data-attributes', JSON.stringify(data['selected']));
21340	checkForSelectedSwatch($('.product-variations'));
21341
21342	$('.attribute-guide-link').fancybox();
21343}
21344
21345function checkForSelectedSwatch($container) {
21346	var swatchIsSelected = $container.find('.color-options .selectable.selected').length > 0;
21347	if (!swatchIsSelected) {
21348		$container.find('.color-options .selectable:first-child>.swatchanchor').mouseenter();
21349	}
21350}
21351
21352/**
21353 * @description update product content with new variant from href, load new content to #product-content panel
21354 * @param {String} href - url of the new product variant
21355 **/
21356var updateContent = function (href) {
21357	loaded['content'] = false;
21358	$('.btn.add-to-cart').attr('disabled', 'disabled').loading();
21359	var bopisStoreSearchPostalCode = $('.bopis-store-search input[name="Bopis_StoreSearch_postalCode"]').val();
21360    var $pdpForm = $('.pdpForm');
21361    var qty = $pdpForm.find('select[name="Quantity"]').first().val();
21362    var subdimensionHTML = $('#tt-subdimension-teaser').html();
21363    var reloadSubdimension = $('.tt-w-dim-teaser__item').length > 0 ? true : false;
21364    var params = {
21365        Quantity: isNaN(qty) ? '1' : qty,
21366        format: 'ajax',
21367        productlistid: $pdpForm.find('input[name="pid"]').first().val(),
21368        BopisEnabled: $('.bopis-store-search').length > 0 ? true : false,
21369		Token: $('.bopis-store-search input[name="csrf_token"]').val()
21370    };
21371
21372    buildSelectors(util.getQueryStringParams(util.getQueryString(util.appendParamsToUrl(href, params))));
21373	$.ajax({
21374    	method: 'GET',
21375    	url: util.appendParamsToUrl(href, params),
21376    	complete: function(html) {
21377			loaded['content'] = true;
21378			$('#product-content').html(html.responseText);
21379			renderVariationUpdates();
21380
21381			if (bopisStoreSearchPostalCode) {
21382        		$('.bopis-store-search input[name="Bopis_StoreSearch_postalCode"]').val(bopisStoreSearchPostalCode);
21383        	}
21384        	
21385            if (SitePreferences.STORE_PICKUP) {
21386                productStoreInventory.init();
21387            }
21388            image.replaceImages();
21389            tooltip.init();
21390            // rating.init();
21391            updateProductInfo();
21392            if (reloadSubdimension){
21393            	reloadSubdimensionsWidget(subdimensionHTML);
21394            }
21395            initializeImageGallery();            
21396			updateCSRFToken();
21397			// GoogleTagManagerClient.PushDataLayerForDataLayerType('ViewItem');
21398            
21399            setTimeout(function(){$(document).trigger('update-review-text');}, 500);
21400
21401            if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui) {
21402			    affirm.ui.refresh();
21403			}
21404            
21405			var variantSelected = isVariantSelected();
21406            if ($('.bopis-store-search').length > 0 || $('.bopis-add-to-cart-options-container').length > 0) {
21407            	if (variantSelected) {
21408            		options.Bopis.IsVariantSelected = true;
21409            	}
21410				initializeBopis();
21411			}
21412
21413			if (pageContext.site === SiteConstants.SiteIds.BootBarnUS) {
21414				updateAddToCartRecommendations();
21415			}
21416			$('.btn.add-to-cart').removeAttr('disabled').removeAttr('loading').find('.loading-indicator-container').remove();    		
21417    	}
21418    });
21419};
21420
21421function isVariantSelected() {
21422	var sizeSelected = $('div.product-variations .size.size-options li.selected').length;
21423	var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
21424	var widthSelected = $('div.product-variations .width.width-options li.selected').length;
21425	var colorSelected = $('div.product-variations .color.color-options li.selected').length;
21426
21427	return (sizeSelected || (lengthSelected && widthSelected)) && colorSelected;
21428}
21429
21430function updateAddToCartRecommendations() {
21431	var intervalCount = 0;
21432	var addToCartRecommendationsCheck = setInterval(function() {
21433		if ( $('.recomm-prod-pdp-a2c-slot .accordion.product-recommendations').length > 0 ) {
21434			clearInterval(addToCartRecommendationsCheck);
21435			initSlider();
21436			return;
21437		}
21438
21439		if (intervalCount === 50) {
21440			clearInterval(addToCartRecommendationsCheck);
21441			return;
21442		}
21443
21444		intervalCount++;
21445	}, 200);
21446
21447	function initSlider() {
21448		sliders.init('.recomm-prod-pdp-a2c-slot .accordion.product-recommendations .slides', {breakpoints: {0: {slidesToShow: 1, arrows: true, dots: true}, 768: {slidesToShow: 3, arrows: true, dots: false}, 1024: {slidesToShow: 4, arrows: true, dots: false}}}, true);
21449	}
21450}
21451
21452function sizeMenuOpenClose(event, el, open) {
21453	var $this = el;
21454	var $container = $this.closest('.select-dropdown');
21455	var $menu = $container.find('.select-dropdown-inner');
21456	
21457	if (open) {
21458		$this.addClass('active');
21459		$menu.addClass('expand');
21460		$menu.find('li.selectable a').removeAttr('tabindex');
21461	} else {
21462		$this.removeClass('active');
21463		$menu.removeClass('expand');
21464		$menu.find('li.selectable a').attr('tabindex', '-1');
21465		if ($menu.find('.tempselection').length > 0) {
21466			$menu.find('.tempselection').removeClass('selected tempselection');
21467		}
21468		if (event.keyCode) {
21469			$container.addClass('focus-visible-indicator').focus();
21470		}
21471	}
21472
21473	if ($menu.hasClass('expand')) {
21474		scrollToCurrentSize();
21475		setTimeout(function(){
21476			$menu.addClass('expand-close');
21477		}, 100);
21478	} else {
21479		$menu.removeClass('expand-close');
21480	}
21481}
21482
21483function selectSizeOption(el) {
21484	var $selected = $(el);
21485	
21486	$selected
21487		.addClass('selected')
21488		.siblings()
21489		.removeClass('selected');
21490
21491	$selected
21492		.closest('.select-dropdown-inner')
21493		.removeClass('expand')
21494		.removeClass('expand-close')
21495		.prev('.select-text')
21496		.removeClass('active');
21497}
21498
21499function updateWishlistButton(sizeSelected) {
21500	var variantId = $('#pid').val();
21501	var isWishlistItem = wishlist.checkWishlistForItem(variantId);
21502	var sizeText = '';
21503
21504	if (sizeSelected) {
21505		sizeText = sizeSelected.find('.size').text();
21506	}
21507	if (isWishlistItem) { 
21508		wishlist.prepareWishlistRemoveUI($('.pdp-main .wishlist-item-status-button'), sizeText);
21509	} else {
21510		wishlist.prepareWishlistAddUI($('.pdp-main .wishlist-item-status-button'), null, sizeText);
21511	}
21512}
21513
21514function initializeEvents () {
21515	var $pdpMain = $('#pdpMain');
21516
21517    // hover on swatch - should update main image with swatch image.
21518//    $pdpMain.off('mouseenter.Variant.SwatchAnchor').on('mouseenter.Variant.SwatchAnchor', '.color>li>.swatchanchor', function (e) {
21519//        e.preventDefault();
21520//        if ($(this).parents('li').hasClass('unselectable')) { return; }
21521//        if ($(this).parents('li').hasClass('selected')) { return; }
21522//        updateContent(this.href);
21523//    });
21524
21525    // click on swatch - should update main image with swatch image.
21526    $pdpMain.off('click.Variant.SwatchAnchor').on('click.Variant.SwatchAnchor', '.product-detail .swatchanchor', function (e) {
21527		e.preventDefault();
21528        var didReload = false;
21529        if ($(this).parents('li').hasClass('unselectable')) {
21530        	if ($(this).parents('li').hasClass('alternate')) {
21531        		var altUrlLoad = $(this).attr('href');    		
21532        		window.location.href = altUrlLoad;
21533        	}
21534        	
21535        	$('li.drop-down-select-value').each(function() {
21536    			var $selectedItem = $(this);
21537    			$selectedItem.siblings('li.selected-value').html($selectedItem.html());
21538        		$selectedItem.parents('.attribute').first().find('.label .current, .select-dropdown .select-text').html($selectedItem.html());
21539        		if ( $selectedItem.parents('.select-dropdown-inner') != null ) {
21540        			$selectedItem.parents('.select-dropdown-inner').find( 'li.selected' ).removeClass('selected');
21541        		}
21542        	});
21543    		if ( $(this).attr('xhref') != null ) {
21544    			updateContent($(this).attr('xhref'));
21545    			didReload = true;
21546    		} else { return; }
21547    	}
21548        if ( !didReload ) {
21549	        var link = this;
21550	        if (!link.href) {
21551	        	link = $(this).parent()[0];
21552	        }
21553	        updateContent(link.href);
21554        }
21555    });
21556
21557    // change drop down variation attribute - should replace product content with new variant
21558    $pdpMain.off('change.Variant.VariationSelect').on('change.Variant.VariationSelect', '.variation-select', function () {
21559        if ($(this).val().length === 0) { return; }
21560        updateContent($(this).val());
21561    });
21562
21563	// Custom Select Dropdown
21564    $pdpMain.off('click.Variant.Selector.Expand').on('click.Variant.Selector.Expand', '.select-dropdown .select-text', function (event) {
21565		event.preventDefault();
21566		var $this = $(this);
21567		var toggleOpen = !$this.hasClass('active');
21568		sizeMenuOpenClose(event, $this, toggleOpen);
21569	});
21570
21571    $(document).off('click.Variant.General.Close').on('click.Variant.General.Close', function(event) {
21572    	$('div.product-variations ul.select-dropdown-inner.expand-close').each(function () {
21573    		$(this).siblings('.select-text').trigger('click');
21574    	});
21575	});
21576
21577	$pdpMain.off('click.Variant.Selector.Select').on('click.Variant.Selector.Select', '.select-dropdown .selectable', function(event) {
21578		event.preventDefault();
21579		selectSizeOption(this);
21580	});
21581
21582	// Color Options
21583    $pdpMain.off('click.Variant.Color.Select').on('click.Variant.Color.Select', '.color-options a', function (event) {
21584		event.preventDefault();
21585
21586		var nameColor = $(this).data('color-name');
21587
21588		$(this)
21589			.parent()
21590			.addClass('selected')
21591			.siblings()
21592			.removeClass('selected');
21593
21594		$(this)
21595			.closest('.attribute')
21596			.find('.color-current')
21597			.text(nameColor);
21598	});
21599    
21600    $(document).on('keyup', 'div.product-variations div.select-dropdown', function(e) {
21601		var $listContainer = $(this),
21602			$listParent = $listContainer.children('ul.select-dropdown-inner'),
21603			$defaultTextElement = $listContainer.children('.select-text'),
21604			$sizeList = $listParent.find('li.selectable'),
21605			$selected = $sizeList.filter('.selected'),
21606			isMenuOpen = $listParent.hasClass('expand');
21607
21608		antiHover.init($listParent.width(), $listParent.height());
21609
21610		if (e.shiftKey && e.keyCode == 9 && isMenuOpen) { // Tab backwards
21611			selectOptionKeydown(e, 'up', isMenuOpen);
21612		} else {
21613			switch(e.keyCode) {
21614				case 38: // Up Arrow
21615					selectOptionKeydown(e, 'up', isMenuOpen);
21616					break;
21617				case 40: // Down Arrow
21618					if (!isMenuOpen) {
21619						sizeMenuOpenClose(e, $defaultTextElement, true);
21620					}
21621					selectOptionKeydown(e, 'down', isMenuOpen);
21622					break;
21623				case 9: // Tab forwards
21624					if (isMenuOpen) {
21625						selectOptionKeydown(e, 'down', isMenuOpen);
21626					}
21627					break;
21628				case 27: // Escape key
21629					if (isMenuOpen) {
21630						sizeMenuOpenClose(e, $defaultTextElement, false);
21631					}
21632					break;
21633				case 13: // Enter key
21634					$listContainer.find('.select-text').toggleClass('active');
21635					$listParent.toggleClass('expand').toggleClass('expand-close').toggleClass('size-d-none');
21636					$sizeList.find('a[tabindex]').length > 0 ? $sizeList.find('a').removeAttr('tabindex') : $sizeList.find('a').attr('tabindex', '-1');	
21637					if (!$selected) {
21638						$defaultTextElement.trigger('click');
21639					} else {
21640						$selected.find('a.swatchanchor').trigger('click');
21641					}	
21642					break;
21643			}
21644		}
21645	});
21646
21647    $(document).off('mousemove.Variant.AntiHover').on('mousemove.Variant.AntiHover', '#anti-hover', function(e) {
21648    	antiHover.moveremove(parseInt(e.clientX), parseInt(e.clientY));
21649    });
21650
21651    var swatches = $('div.product-variation ul.color-options li.selectable');
21652	if (swatches.length && !swatches.is('.selected')) {
21653		setColor();
21654	}
21655	
21656	updateProductInfo();
21657    initializeImageGallery();
21658};
21659
21660function setColor(){
21661	var d = new $.Deferred();
21662
21663	setTimeout(function(){
21664    	var swatches = $('div.product-variation ul.color-options li.selectable');
21665    	if (swatches.length && !swatches.is('.selected')) {
21666    		swatches.first().find('a').trigger('click');
21667    		setTimeout(function(){d.resolve()}, 150);
21668    	}
21669    	
21670    }, 50);
21671	
21672	return d.promise()
21673}
21674
21675var antiHover = {
21676	$antiHover: null,
21677	posX: 0,
21678	posY: 0,
21679	remove: function() {
21680		if (this.$antiHover) this.$antiHover.remove();
21681		this.posX = 0;
21682		this.posY = 0;
21683	},
21684	moveremove: function(x, y) {
21685		this.posX = (this.posX == 0) ? x : this.posX;
21686		this.posY = (this.posY == 0) ? y : this.posY;
21687		if (this.posX != x || this.posY != y) {
21688			$('div.product-variations ul.select-dropdown-inner li.selectable').removeClass('selected');
21689			this.remove();
21690		}
21691	},
21692	check: function() {
21693		return (this.$antiHover && this.$antiHover.length);
21694	},
21695	init: function(w, h) {
21696		this.remove();
21697		$('.value.select-dropdown').append('<div style="position: absolute;" id="anti-hover"></div>');
21698		this.$antiHover = $('#anti-hover');
21699		this.$antiHover
21700			.css('z-index', 1000)
21701			.css('height', h)
21702			.css('width', w);
21703	}
21704
21705};
21706
21707function selectOptionKeydown(e, action, isMenuOpen) {
21708	e.preventDefault();
21709
21710	var $listParent = $('div.product-variations ul.select-dropdown-inner'),
21711		$listContainer = $listParent.closest('.select-dropdown'),
21712		$defaultTextElement = $listContainer.children('.select-text'),
21713		$sizeList = $listParent.find('li.selectable'),
21714		$selected = $sizeList.filter('.selected'),
21715		height = parseInt($sizeList.first().css('height').replace(/px/, '')),
21716		$current;
21717
21718		if ($selected.length <= 0) {
21719			$selected = $listParent.find('.focus-visible-indicator').closest('li.selectable') ? $selected : null;
21720		}
21721		if (!$selected || !$selected.length) {
21722			$current = $sizeList.eq(0);
21723		} else if (action == 'up') {
21724			if ($selected.is(':first-child')) {
21725				$current = $sizeList.eq(0);
21726				// If this is first list item, and user pressed Up Arrow, then we need to close the menu
21727				sizeMenuOpenClose(e, $defaultTextElement, false);
21728			} else {
21729				$current = $selected.prev();
21730			}
21731		} else {
21732			if (!$selected.next().hasClass('selectable')) {
21733				$current = $sizeList.last();
21734				// If this is last list item, and user pressed Down Arrow, then we need to close the menu
21735				sizeMenuOpenClose(e, $defaultTextElement, false);
21736			} else {
21737				$current = $selected.next();
21738			}
21739		}
21740
21741		var listParentTop = 0;
21742		var listParentBottom = listParentTop + $listParent.height();
21743		var currentTop = $current.position().top;
21744		var currentBottom = currentTop + $current.height();
21745		var faultPx = 10;
21746		if (currentBottom + faultPx > listParentBottom) {
21747			$listParent.scrollTop(parseInt($listParent.scrollTop()) + height);
21748		} else if (currentTop < listParentTop) {
21749			$listParent.scrollTop(parseInt($listParent.scrollTop()) - height);
21750		}	
21751
21752		//$current.parents('.attribute').first().find('.label .current, .select-dropdown .select-text').html($current.find('a').html());
21753		$sizeList.removeClass('selected tempselection');
21754		$current.addClass('selected tempselection').find('a').addClass('focus-visible-indicator').focus();
21755		// Needed to combat auto-scroll behavior that happens in previous line when focus added to <a>
21756		if ($current.is(':first-child')) {
21757			$listParent.scrollTop(0);
21758		}
21759}
21760
21761function scrollToCurrentSize() {
21762	var $listParent = $('div.product-variations ul.select-dropdown-inner'),
21763		$sizeList = $listParent.find('li.selectable'),
21764		$selected = $sizeList.filter('.selected'),
21765		height = $sizeList.first().css('height').replace(/px/, '');
21766
21767	if ($selected) {
21768		$listParent.scrollTop(height * $sizeList.index($selected));
21769	}
21770}
21771
21772function initializeImageGallery() {
21773	var params;
21774	if (SitePreferences.PRIMARY_PRODUCT_SLIDER == 'swiperJS') { 
21775		imageGallery = require('../../components/ImageGallery/Swiper');
21776		if (SitePreferences.PRIMARY_PRODUCT_SLIDER_CONFIG) {
21777			params = JSON.parse(SitePreferences.PRIMARY_PRODUCT_SLIDER_CONFIG);
21778		}
21779	} else {
21780		imageGallery = require('../../components/ImageGallery/SlickAndImgViewer2');
21781	}
21782	imageGallery.initialize(params);
21783}
21784
21785function initializeBopis() {
21786	if ($('.bopis-add-to-cart-options-container').length > 0) {
21787		bopisAddToCartOptions.initialize($('.bopis-add-to-cart-options-container'), $.extend(options.Bopis, { Channel: options.Channel, ProductId: options.ProductId, StoreUIVersion: options.StoreUIVersion }));
21788	}
21789	
21790	if ($('.bopis-store-search').length > 0) {
21791		bopisStoreSearch.initialize($('.bopis-store-search'), $.extend(options.Bopis, { Channel: options.Channel }));
21792	}
21793}
21794
21795function initialize(optionsParameter) {
21796	options = optionsParameter;
21797	initializeDom();
21798	initializeEvents();
21799	initializeBopis();
21800}
21801
21802var variant = {
21803	initialize: initialize,
21804	initializeDom: initializeDom,
21805	initializeEvents: initializeEvents
21806};
21807
21808module.exports = variant;
21809},{"../../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../../ajax":3,"../../bopis/components/AddToCartOptions":7,"../../bopis/components/StoreSearch":13,"../../components/ImageGallery/SlickAndImgViewer2":23,"../../components/ImageGallery/Swiper":24,"../../rating":89,"../../sliders":95,"../../storeinventory/product":98,"../../tooltip":101,"../../util":102,"../../wishlist":105,"./image":74,"lodash":133}],78:[function(require,module,exports){
21810'use strict';
21811var direction, videoCount, width, height, videoCounter = 0, posValue = 0, lastCounter = 0, arrowPos;
21812
21813var setCarousel = function(){
21814	videoCount = $('.video-container ul li').length;
21815	width = $('.video-container').width();
21816	height = width * 315 / 420;
21817	$('.youtube-video iframe').css({'width': width + 'px','height': height + 'px'});
21818	width += 40;
21819	arrowPos = height / 2 - 20;
21820	$('.video-container .arrows .arrow').css('top', arrowPos + 'px');
21821};
21822
21823var arrowClick = function(arrow){
21824	direction = arrow.hasClass('left') ? 'left' : 'right';
21825	lastCounter = videoCounter;
21826	if(direction == 'right'){
21827		if(videoCounter == (videoCount - 1)){
21828			posValue = 0;
21829			videoCounter = 0;
21830		}
21831		else{
21832			videoCounter++;
21833			posValue = videoCounter * width;
21834		}
21835	}
21836	else{
21837		if(videoCounter == 0){
21838			posValue = (videoCount - 1) * width;
21839			videoCounter = videoCount - 1;
21840		}
21841		else{
21842			videoCounter--;
21843			posValue = videoCounter * width;
21844		}
21845	}
21846	$('.video-container ul').animate({'left':'-' + posValue + 'px'}, 'fast');
21847	var iframe = $('.youtube-video iframe:eq('+ lastCounter +')');
21848	$(iframe).attr('src', $(iframe).attr('src'));
21849};
21850
21851module.exports = function () {
21852	setCarousel();
21853    $('.video-container .arrows .arrow').on('click', function(){
21854    	arrowClick($(this));
21855    });
21856};
21857},{}],79:[function(require,module,exports){
21858'use strict';
21859
21860var addProductToCart = require('./product/addToCart'),
21861    ajax = require('../ajax'),
21862    quickview = require('../quickview'),
21863    util = require('../util');
21864
21865/**
21866 * @function
21867 * @description Loads address details to a given address and fills the address form
21868 * @param {String} addressID The ID of the address to which data will be loaded
21869 */
21870function populateForm(addressID, $form) {
21871    // load address details
21872    var url = Urls.giftRegAdd + addressID;
21873    ajax.getJson({
21874        url: url,
21875        callback: function (data) {
21876            if (!data || !data.address) {
21877                window.alert(Resources.REG_ADDR_ERROR);
21878                return false;
21879            }
21880            // fill the form
21881            $form.find('[name$="_addressid"]').val(data.address.ID);
21882            $form.find('[name$="_firstname"]').val(data.address.firstName);
21883            $form.find('[name$="_lastname"]').val(data.address.lastName);
21884            $form.find('[name$="_address1"]').val(data.address.address1);
21885            $form.find('[name$="_address2"]').val(data.address.address2);
21886            $form.find('[name$="_city"]').val(data.address.city);
21887            $form.find('[name$="_country"]').val(data.address.countryCode).trigger('change');
21888            $form.find('[name$="_postal"]').val(data.address.postalCode);
21889            $form.find('[name$="_state"]').val(data.address.stateCode);
21890            $form.find('[name$="_phone"]').val(data.address.phone);
21891            // $form.parent('form').validate().form();
21892        }
21893    });
21894}
21895
21896/**
21897 * @private
21898 * @function
21899 * @description Initializes events for the gift registration
21900 */
21901function initializeEvents() {
21902    var $eventAddressForm = $('form[name$="_giftregistry"]'),
21903        $beforeAddress = $eventAddressForm.find('fieldset[name="address-before"]'),
21904        $afterAddress = $eventAddressForm.find('fieldset[name="address-after"]');
21905
21906    $('.usepreevent').on('click', function () {
21907        // filter out storefront toolkit
21908        $(':input', $beforeAddress).not('[id^="ext"]').not('select[name$="_addressBeforeList"]').each(function () {
21909            var fieldName = $(this).attr('name'),
21910                $afterField = $afterAddress.find('[name="' + fieldName.replace('Before', 'After') + '"]');
21911            $afterField.val($(this).val()).trigger('change');
21912        });
21913    });
21914    $eventAddressForm.on('change', 'select[name$="_addressBeforeList"]', function () {
21915        var addressID = $(this).val();
21916        if (addressID.length === 0) { return; }
21917        populateForm(addressID, $beforeAddress);
21918    })
21919    .on('change', 'select[name$="_addressAfterList"]', function () {
21920        var addressID = $(this).val();
21921        if (addressID.length === 0) { return; }
21922        populateForm(addressID, $afterAddress);
21923    });
21924
21925    $('form[name$="_giftregistry_items"]').on('click', '.item-details a', function (e) {
21926        e.preventDefault();
21927        var productListID = $('input[name=productListID]').val();
21928        quickview.show({
21929            url: e.target.href,
21930            source: 'giftregistry',
21931            productlistid: productListID
21932        });
21933    });
21934
21935}
21936
21937exports.init = function () {
21938    initializeEvents();
21939    addProductToCart();
21940
21941    util.setDeleteConfirmation('.item-list', String.format(Resources.CONFIRM_DELETE, Resources.TITLE_GIFTREGISTRY));
21942};
21943
21944},{"../ajax":3,"../quickview":88,"../util":102,"./product/addToCart":72}],80:[function(require,module,exports){
21945'use strict';
21946
21947var compareWidget = require('../compare-widget');
21948var dialog = require('../dialog');
21949var GoogleTagManagerClient = require('../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
21950var loadingIndicator = require('../components/LoadingIndicator');
21951var productTile = require('../product-tile');
21952var page = require('../page');
21953var sizeRefinements = require('../size-refinements');
21954var styleCounts = require('../style-counts');
21955var util = require('../util');
21956var wishlist = require('../wishlist');
21957var sliders = require('../sliders');
21958
21959var ImageConstants;
21960
21961var RefinementUIVersionConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/RefinementConstants').RefinementsUIVersions;
21962var SiteConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
21963var ValidationSettings = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Settings;
21964var ValidationRules = require('../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants').Rules;
21965
21966var options;
21967var dataBuffer = [];
21968var $html = $('html');
21969var $refinementsInner = $('.refinements');
21970var refinementsExpanded;
21971var refinementsViewAll;
21972var loadingIndicatorSettings;
21973
21974var $bootFinderEmailForm;
21975var bootFinderEmailValidator;
21976
21977var previousSortApplied;
21978
21979/**
21980 * @private
21981 * @function
21982 * @description replaces breadcrumbs, lefthand nav and product listing with ajax and puts a loading indicator over the product listing
21983 */
21984function updateProductListing(url, callback, isLoadMore) {
21985    if (!url || url === window.location.href) {
21986    	if ($('body').attr('loading')) {
21987    		$('body').loading('destroy');
21988    	}
21989		if ($('.apply-refinements').hasClass('loading')) {
21990    		$('.apply-refinements').loading('destroy');
21991    	}
21992		if (typeof callback == 'function') {
21993			callback();
21994		}
21995        return;
21996    }
21997
21998	if (options.StoreUIVersion == '2024Redesign' || (options && options.Pagination && options.Pagination.Mode == 'LoadMore')) {
21999		$('.btn.load-more').loading({ IsImageShown: false });
22000	}
22001
22002    updateInventoryMessages(url);
22003	saveExpandedRefinements();
22004	
22005	$.ajax({
22006		url: util.appendParamToURL(url, 'format', 'ajax'),
22007		type: 'GET',
22008		success: function(data) {
22009			$('body').removeClass('refinements-mobile-scroll-lock').css('top', '');
22010			if ($('body').attr('loading') == 'loading') {
22011				$('body').loading('destroy');
22012			}
22013
22014			history.pushState({}, '', url);
22015			var $content = $(data);
22016			var $productGrid = $content.find('#search-result-items');
22017			var productTileCount = $productGrid.find('.grid-tile').length + $('#search-result-items').find('.grid-tile').length;
22018			var productTotalCount = parseInt($content.find('.search-result-options.bottom .count').text().replace(/\D/g, ''), 10);
22019			dataBuffer['PaginationCount'] = productTotalCount;
22020			if (isLoadMore) {
22021				dataBuffer['PaginationIndex'] += options.Pagination.Size;
22022				$('#search-result-items').append($productGrid.html());
22023				$('.search-result-options .display-count').text(productTileCount);
22024
22025				if (productTileCount >= dataBuffer['PaginationCount']) {
22026					$('.btn.load-more').hide();
22027				} else {
22028					$('.btn.load-more').show();
22029				}
22030
22031				if (options.StoreUIVersion == '2024Redesign' || (options && options.Pagination && options.Pagination.Mode == 'LoadMore')) {
22032					$('.btn.load-more').loading('destroy');
22033				}
22034			} else {
22035				dataBuffer['PaginationIndex'] = 0;
22036				$('#main').html($content);
22037				
22038				if (pageContext.site === SiteConstants.SiteIds.BootBarnUS || pageContext.site === SiteConstants.SiteIds.BootBarnWorkforceUS) {
22039					$('#grid-sort-header').selectmenu({
22040						'width': 205
22041					});
22042				}
22043
22044				if ('variantInventoryMessagingUrl' in page && page.variantInventoryMessagingUrl) {
22045					showVariantInventoryMessaging();
22046				}
22047
22048				if ($('#primary').length > 0) {
22049					$('html,body').animate({
22050						scrollTop: $('#primary').offset().top
22051					}, 1000);
22052				}
22053			}
22054
22055			if (options.Pagination && options.Pagination.Mode == 'EndlessScroll') {
22056				if (productTileCount >= dataBuffer['PaginationCount']) {
22057					$(document).off('scroll.EndlessScroll');
22058					dataBuffer['EndlessScrollInitialized'] = false;
22059				} else if (!dataBuffer['EndlessScrollInitialized']) {
22060					initializeEndlessScroll();
22061				}
22062			}
22063
22064			$refinementsInner = $('.refinements');
22065			redrawUpdatedRefinementHandling($content, url);				
22066			compareWidget.init();
22067			productTile.init();
22068			sizeRefinements.init();
22069			initializeRefinementControls();			
22070			redrawShopMyStore();
22071			sliders.initializeCategorySliders('.main-content .category-landing-slider:not(.slick-initialized)');
22072			wishlist.renderAllWishlistButtons();
22073
22074			if ('affirm' in window && 'ui' in affirm && 'refresh' in affirm.ui) {
22075				affirm.ui.refresh();
22076			}
22077
22078			$(document).trigger('CategoryHeaderSliders.Initialize');
22079			$(document).trigger('EinsteinProductRecommendations.Initialize');
22080			if ($('#categoryTitle').length > 0) {
22081				setTimeout(function(){window.top.document.title = $('#categoryTitle').data('title');}, 3000);
22082			}
22083	
22084			initializeThumbDataLayer(url);
22085
22086			if (options.StyleCountCache) {
22087				styleCounts.render(options.StyleCountCache);
22088			}
22089			
22090			if (callback && typeof callback == 'function') {
22091				callback();
22092			}
22093		},
22094		complete: function(data, status, xhr) {
22095			if ((data.status == 301 || data.status == 302) && data.responseText) {
22096				location.href = data.responseText;
22097			}
22098		}
22099	});
22100}
22101
22102function updateInventoryMessages(url, callback) {
22103	var urlParams = util.getQueryStringParams(url);
22104
22105	var inventoryLinkUrl = Urls.getInventoryLink;
22106	for (var urlParameter in urlParams) {
22107		if (urlParams[urlParameter]) {
22108			inventoryLinkUrl = util.appendParamToURL(inventoryLinkUrl, urlParameter, urlParams[urlParameter]);
22109		}
22110	}
22111
22112	$.post(inventoryLinkUrl, function(data) {
22113		var dataObj = JSON.parse(data);
22114		
22115		if (dataObj.status && dataObj.url.length) {
22116			var newUrl = dataObj.url;
22117			
22118			if (urlParams.hasOwnProperty('start') && urlParams.start > 0) {
22119				newUrl = util.appendParamToURL(newUrl, 'start', urlParams.start);
22120			}
22121			
22122			if (urlParams.hasOwnProperty('sz')) {
22123				newUrl = util.appendParamToURL(newUrl, 'sz', urlParams.sz);
22124			}
22125			
22126			page.variantInventoryMessagingUrl = newUrl;
22127		}
22128		 
22129		typeof callback == 'function' && callback();
22130	});
22131}
22132
22133function getUrlParams(url) {
22134	var vars = {};
22135
22136	var parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
22137		vars[key] = value;
22138    });
22139
22140	return vars;
22141}
22142
22143function initializeThumbDataLayer(url) {
22144	var data = {};
22145	
22146	var currentCategoryId = $('.currCGID').val();
22147	if (currentCategoryId && currentCategoryId != 'null') {
22148		data.cgid = currentCategoryId;
22149	}
22150	
22151	if (getUrlParams(url)['sz']) {
22152		data.sz = getUrlParams(url)['sz'];
22153	}
22154	
22155	if (getUrlParams(url)['start']) {
22156		data.start = getUrlParams(url)['start'];
22157	}
22158	
22159	if (getUrlParams(url)['q']) {
22160		data.q = decodeURIComponent(getUrlParams(url)['q']);
22161	}
22162	
22163	if (getUrlParams(url)['srule']) {
22164		data.srule = decodeURIComponent(getUrlParams(url)['srule']);
22165	}
22166	
22167	for (var preferenceIndex = 1; preferenceIndex < 9; preferenceIndex++) {
22168		if (getUrlParams(url)['prefn' + preferenceIndex]) {
22169			data['prefn' + preferenceIndex] = decodeURIComponent(getUrlParams(url)['prefn' + preferenceIndex]);
22170		}
22171		
22172		if (getUrlParams(url)['prefv' + preferenceIndex]) {
22173			data['prefv' + preferenceIndex] = decodeURIComponent(getUrlParams(url)['prefv' + preferenceIndex]);
22174		}
22175	}
22176	
22177	GoogleTagManagerClient.PushDataLayerForPage(pageContext);
22178}
22179
22180function filterRefinements(container, filterText) {
22181	container.find('.checks label').each(function() {
22182		var $filter = $(this);
22183		if (filterText === "") {
22184			// Reset filter
22185			$filter.parent().show();
22186		}
22187		else {
22188			var labelText = $filter.html().trim().toUpperCase();
22189			if (labelText.indexOf(filterText) > -1) {
22190				$filter.parent().show();
22191			}
22192			else {
22193				$filter.parent().hide();
22194			}
22195		}
22196	});
22197}
22198
22199/**
22200 * @private
22201 * @function
22202 * @description Initializes the following search refinement controls:<br/>
22203 * <p>scroll bar</p>
22204 * <p>section dropdowns</p>
22205 * <p>range slider</p>
22206 * <p>textbox filter</p>
22207 */
22208function initializeRefinementControls() {
22209	if ($('.refinements .refinement:visible').length > 2) {
22210		$('.refinements .show-all-refinements').addClass('active');
22211	}
22212	
22213	if ($('.refinement input:checked, .refinement [refinement-value].modified:not(:checked)').length > 0) {
22214		$('.refinements').addClass('refinements-applied');
22215	}
22216	
22217	// handle toggle refinement blocks
22218	$(document).off('click.RefinementToggle').on('click.RefinementToggle', '.refinement:not(.disabled) .toggle', function (event) {
22219		var $this = $(this);
22220		var $refinementBody = $this.parent().find('.refinement-body');
22221
22222		if (!$this.hasClass('expanded')) {
22223			$this.attr('aria-expanded', 'true').closest('.refinement').addClass('expanded');
22224		} else {
22225			$this.attr('aria-expanded', 'false').closest('.refinement').removeClass('expanded');
22226			
22227		}
22228		$this.toggleClass('expanded');
22229		if ($this.hasClass('expanded')) {
22230		    $refinementBody.stop().show();
22231        } else {
22232			$refinementBody.stop().hide();
22233        }
22234		
22235		if ($('.refinement.expanded').length > 0) {
22236			$('.refinements').addClass('refinement-expanded');
22237		} else {
22238			$('.refinements').removeClass('refinement-expanded');
22239		}
22240		
22241		if ($this.closest('.show-all-refinements').length > 0) {
22242			$this.parent().toggleClass('expanded');
22243			return;
22244		}
22245
22246		saveExpandedRefinements();
22247		if (options.StoreUIVersion == '2024Redesign') {
22248			updateSelectedRefinements();
22249		}
22250		$(document).trigger('StickyRefinements.Refresh');
22251		
22252		setTimeout(function(){
22253			var cssClass = $this.parent().attr('class');
22254			var refinementGroup = $.trim(cssClass.replace(/refinement/, ''));
22255			refGroupsStates.saveState(refinementGroup, $this.hasClass('expanded'));
22256		}.bind(this), 30);
22257	});
22258
22259	$('.refinement .form-row input[type=text]').on('input', function() {
22260		var $input = $(this);
22261
22262	    window.clearTimeout($input.data("timeout"));
22263	    $input.data("timeout", setTimeout(function () {
22264	    	filterRefinements($input.parents('.refinement').first(), $input.val().trim().toUpperCase());
22265	    }, 1000));
22266	});
22267
22268	// ADA compliance attribute
22269	$('li.more-link-container').closest('ul').attr('aria-live', 'polite');
22270
22271	priceSlider();
22272}
22273
22274/**
22275 * @private
22276 * @function
22277 * @description Initializes events for the following elements:<br/>
22278 * <p>refinement blocks</p>
22279 * <p>updating grid: refinements, pagination, breadcrumb</p>
22280 * <p>item click</p>
22281 * <p>sorting changes</p>
22282 */
22283function initializeEvents() {
22284	/*
22285	 * Added a JS for search result page to prevent refinement from having a margin-top even the header is not showing
22286	 */
22287	if ((options.RefinementsUIVersion != RefinementUIVersionConstants.Updated) && window.innerWidth < 767) {
22288		if($(".search-phrase-container").length) {
22289			$('.pt_product-search-result #secondary .refinements-inner').css({'margin-top': '60px'});
22290		}
22291	}
22292
22293	$(window).on('resize', function() {
22294		if ((options.RefinementsUIVersion == RefinementUIVersionConstants.Updated) && window.innerWidth > 767) {
22295			$('.modified').removeClass('modified').prop('checked', !$('.modified').prop('checked'));
22296		}
22297	});
22298
22299	$(document).ready(function() {
22300		$("body").delegate(".product-swatches-all", "click", function() {
22301			$(this).parent().find(".swatch-list li").removeClass('hide');
22302		});
22303
22304		previousSortApplied = $('#grid-sort-header').find('option:selected').text();
22305	});
22306
22307	var $main = $('#main');
22308    // compare checked
22309    $main.on('click', 'input[type="checkbox"].compare-check', function () {
22310        var cb = $(this);
22311        var tile = cb.closest('.product-tile');
22312
22313        var func = this.checked ? compareWidget.addProduct : compareWidget.removeProduct;
22314        var itemImg = tile.find('.product-image a img').first();
22315        func({
22316            itemid: tile.data('itemid'),
22317            uuid: tile[0].id,
22318            img: itemImg,
22319            cb: cb
22320        });
22321    });
22322
22323    $(document).off('click.Toggle.Refinements').on('click.Toggle.Refinements', '.toggle-refinements', function(event) {
22324    	event.preventDefault();
22325    	toggleRefinements();
22326    });
22327    
22328    $main.off('click.Toggle.Sorting').on('click.Toggle.Sorting', '.toggle-sort', function(event) {
22329    	event.preventDefault();
22330    	
22331		$('.sort-by.modal').toggle();
22332		$('.sort-by.modal').toggleClass('open');
22333		$('#search-result').toggleClass('active');
22334		
22335    });
22336    
22337    $main.off('click.Select.Sorting').on('click.Select.Sorting', '.sort-by.modal input', function(event) {
22338    	event.stopImmediatePropagation();
22339    	
22340    	var $this = $(this);
22341		$('.sort-by.modal').toggle();
22342		$('.sort-by.modal').toggleClass('open');
22343		$('body').loading(loadingIndicatorSettings);
22344		updateProductListing($this.val());
22345    });
22346    
22347    // handle events for updating grid
22348    $main.on('click', '.refinements-head a, .refinements-inner a:not(.skip-to-next), .pagination-bar a, .breadcrumb-refinement-value a', function (e) {
22349		var $this = $(this);
22350
22351    	if (!$this.hasClass('ref-filter') && checkForUpdatedRefinementHandling()) {
22352    		e.preventDefault();
22353    		var refinementValue = $this.attr('value');
22354    		
22355    		$this.closest('.refinement-body-inner').find('li[refinement-value="' + refinementValue + '"], a[value="' + refinementValue + '"]').toggleClass('modified');
22356    		redrawUpdatedRefinementHandling();
22357    		return;	
22358    	}
22359    	
22360        // don't intercept for category and folder refinements, as well as unselectable
22361        if ($this.parents('.category-refinement').length > 0 || $this.parents('.folder-refinement').length > 0 || $this.parent().hasClass('unselectable')) {
22362            return;
22363        }
22364        e.preventDefault();
22365
22366		var activeRefinementsCount = $('[refinement-value].selected').length - $('.swatches[class*="popular-"] [refinement-value].selected').length;
22367		if ($this.parent().hasClass('selected') && activeRefinementsCount == 1 && options.CategoryLandingPageContent && options.CategoryLandingPageContent.Url) {
22368			location.href = options.CategoryLandingPageContent.Url;
22369    		return;
22370		}
22371
22372        if ($this.hasClass('breadcrumb-element') && $this.attr('href') != null) {
22373    		location.href = this.href;
22374    		return;
22375        }
22376
22377        // hide or show/expand all refinement items
22378        if ($this.hasClass('ref-filter')) {
22379        	var ref = '[refinement-id="' + $(this).data('ref') + '"]';
22380        	$(ref).find('.toggleable').toggleClass('hidden');
22381        	if ($this.hasClass('more-link')) {
22382        		$this.parents('li').hide().closest('.refinement').addClass('more-expanded');
22383				$this.parents('li').nextAll(':visible').first().find('label').addClass('focus-visible-indicator').focus();
22384        	} else {
22385        		$(ref).find('.more-link').parents('li').show().closest('.refinement').removeClass('more-expanded');
22386				$(ref).find('.more-link').parents('li').prevAll(":visible").first().find('label').addClass('focus-visible-indicator').focus();
22387        	}
22388			saveExpandedRefinements();
22389        	return;
22390        }
22391
22392		if ($this.length > 0 && !$('html').hasClass('size-mobile')) {
22393			$('body').loading(loadingIndicatorSettings);
22394		}
22395
22396		if (SitePreferences.GA_NEW_ACTIONS_ENABLED) {
22397			var refinementId = $this.closest('[refinement-id]').attr('refinement-id') || null;
22398			var refinementValue = $this.attr('value') ? $this.attr('value').replace('"'," INCH") : null;
22399			if (refinementId && refinementValue) {
22400				var refinementAnalyticsTaggingData =  { 
22401					'action_name': 'filter',
22402					'action_page_type': 'plp',
22403					'action_value': refinementId, 
22404					'action_items': refinementValue
22405				};
22406				GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', refinementAnalyticsTaggingData);
22407			}
22408		}
22409
22410        updateProductListing(this.href);
22411
22412		var selection = $('div.product-selections ul li');
22413		var refGroup = $(selection[selection.length-2]).find('span:first').data('refgroup-id');
22414		if (refGroup && refGroup.length > 0 && refGroupsStates.checkState(refGroup)) {
22415			$('#refinements-inner .refinement.'+refGroup+' button')[0].click();
22416		}
22417    });
22418
22419    // handle events item click. append params.
22420    $main.on('click', '.product-tile a:not("#quickviewbutton")', function () {
22421        var a = $(this);
22422        // get current page refinement values
22423        var wl = window.location;
22424
22425        var qsParams = (wl.search.length > 1) ? util.getQueryStringParams(wl.search.substr(1)) : {};
22426        var hashParams = (wl.hash.length > 1) ? util.getQueryStringParams(wl.hash.substr(1)) : {};
22427
22428        // merge hash params with querystring params
22429        var params = $.extend(hashParams, qsParams);
22430        if (!params.start) {
22431            params.start = 0;
22432        }
22433        // get the index of the selected item and save as start parameter
22434        var tile = a.closest('.product-tile');
22435        var idx = tile.data('idx') ? + tile.data('idx') : 0;
22436
22437        // convert params.start to integer and add index
22438        params.start = (+params.start) + (idx + 1);
22439        // set the hash and allow normal action to continue
22440        a[0].hash = $.param(params);
22441    });
22442
22443    // handle sorting change
22444    $main.on('change', '.sort-by select', function (e) {
22445        e.preventDefault();
22446        
22447		if ($('html').hasClass('size-mobile')) {
22448			var isSortingChanged = false;
22449			var $sortingRule = $('.modal.open .sort-by option:checked');
22450			if ($sortingRule && $sortingRule.length > 0) {
22451				isSortingChanged = !$sortingRule.attr('selected');
22452			}
22453
22454			if (isSortingChanged || $refinementsInner.find('[refinement-value].modified, .list-selections .value.pending-clear').length > 0) {
22455				$refinementsInner.addClass('refinements-modified');
22456				$('.refinements-inner-container-footer .apply-refinements').removeClass('disabled');
22457			} else {
22458				$refinementsInner.removeClass('refinements-modified');
22459				$('.refinements-inner-container-footer .apply-refinements').addClass('disabled');
22460			}
22461		} else {
22462			$('body').loading(loadingIndicatorSettings);		
22463			var selectedSort = $(this).find('option:selected').val();
22464
22465			if (SitePreferences.GA_NEW_ACTIONS_ENABLED) {
22466				var newSortValue = $(this).find('option:selected').text();
22467				var oldSortValue = previousSortApplied ? previousSortApplied : 'Top Sellers';
22468				if (newSortValue && oldSortValue) {
22469					var sortAnalyticsTaggingData =  { 
22470						'action_name': 'sort',
22471						'action_page_type': 'plp',
22472						'action_value': newSortValue, 
22473						'action_items': 'from ' + oldSortValue
22474					};
22475					GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', sortAnalyticsTaggingData);
22476				}
22477				previousSortApplied = newSortValue;
22478			}
22479
22480			if ($(this).attr('id') == 'mobile-sort'){
22481				updateProductListing(selectedSort);
22482			} else {
22483				if (checkForUpdatedRefinementHandling()) {
22484					updateProductListing(selectedSort);
22485				} else {
22486					document.location.href = selectedSort;
22487				}
22488			}
22489		}
22490    });
22491
22492	$('.search-result-page').on('click', '#grid-sort-header-menu li', function () {
22493		$('.sort-by select').prop('selectedIndex', $(this).index()).first().trigger('change');
22494	});
22495
22496    $main.on('change', '.items-per-page select', function () {
22497        var refineUrl = $(this).find('option:selected').val();
22498        if (refineUrl === 'INFINITE_SCROLL') {
22499            $('html').addClass('infinite-scroll').removeClass('disable-infinite-scroll');
22500        } else {
22501            $('html').addClass('disable-infinite-scroll').removeClass('infinite-scroll');
22502            updateProductListing(refineUrl);
22503        }
22504	});
22505
22506	$main.on('click', '.mobile-refinements .toggle-button', function() {
22507		if (pageContext.site === SiteConstants.SiteIds.SheplersUS && options.RefinementsUIVersion != RefinementUIVersionConstants.Updated) {
22508			if ($(this).hasClass('sort-by-toggle')) {
22509				
22510				//Ensure both sort-by and refinements aren't open simultaneously 
22511				$('#secondary.refinements').removeClass('active');
22512				$('.mobile-refinements .refine-toggle').removeClass('active');
22513
22514				//container for new sorting options
22515				var sortByOptionsContainer = $('.mobile-refinements .sort-by-options');
22516				if(sortByOptionsContainer.html().length < 1) {
22517
22518					//sort-by form for reference and pulling options
22519					var currentSortByOptions = $('.mobile-refinements #sort-by #grid-sort-header option');
22520
22521					sortByOptionsContainer.append("<ul class='sort-by-list'></ul>");
22522
22523					//create new list with data linked to form
22524					currentSortByOptions.each(function() {
22525						var selectedValue = $(this).prop('selected');
22526
22527						if (typeof selectedValue !== typeof undefined && selectedValue !== false) {
22528							var newOption = '<li class="selectable-option selected" data-option-for="'  + $(this).attr('value') + '">' + $(this).text() + '</li>';
22529							sortByOptionsContainer.find('ul').append(newOption);
22530						} else  {
22531							var newOption = '<li class="selectable-option" data-option-for="'  + $(this).attr('value') + '">' + $(this).text() + '</li>';
22532							sortByOptionsContainer.find('ul').append(newOption);
22533						}
22534					});
22535					
22536				}
22537				//toggle sort-by options to active state
22538				$('.mobile-refinements .sort-by-options').toggleClass('active');
22539				$(this).toggleClass('active');
22540			} else if ($(this).hasClass('refine-toggle')) {
22541				//Ensure both sort-by and refinements aren't open simultaneously
22542				$('.mobile-refinements .sort-by-options').removeClass('active');
22543				$('.mobile-refinements .sort-by-toggle').removeClass('active');
22544				//toggle refinements options to active state
22545				$('#secondary.refinements').toggleClass('active');
22546				$(this).toggleClass('active');
22547			}
22548		}	
22549	});
22550	$main.on('click', '.sort-by-options .sort-by-list .selectable-option', function() {
22551		if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
22552			//set correct sort-by form option as selected
22553			var optionValue = $(this).attr('data-option-for');
22554			$('.mobile-refinements .sort-by#sort-by form #grid-sort-header option[value="'+optionValue+'"]').prop('selected', true);
22555			//Trigger form change to fire sort-by change watcher
22556			$('.mobile-refinements .sort-by#sort-by form #grid-sort-header option').change();
22557		}
22558	});
22559
22560    // refinement checkboxes
22561    $main.on('click', '.refinement .checks input', function (event) {
22562		var $this = $(this);
22563		var $refinement = $this.closest('[refinement-value]');
22564		if ($refinement.length > 0 && !$('html').hasClass('size-mobile')) {
22565			$('body').loading(loadingIndicatorSettings);
22566		}
22567
22568		if ($this.closest('.refinement.sort-by').length > 0) {
22569			$this.closest('.refinement-body').find('[refinement-value] input').not($this).removeClass('modified').prop('checked', false);
22570			$this.toggleClass('modified');
22571		}
22572    	if (checkForUpdatedRefinementHandling()) {
22573			var refinementValue = $this.val().replace(' INCH','\\"');
22574    		if ($this.closest('[is-price-refinement="true"]').length > 0) {
22575				$('[is-price-refinement="true"] input').not($this).prop('checked', false).removeAttr('checked').removeClass('modified');
22576				$refinement.toggleClass('modified');
22577			} else {
22578				var $equalValueInputs = $this.closest('.refinement-body').find('[refinement-value="' + refinementValue + '"]');
22579	    		$equalValueInputs.toggleClass('modified');
22580	    		$equalValueInputs.not($this).prop('checked', $this.prop('checked'));
22581    		}
22582    		redrawUpdatedRefinementHandling();
22583    		return;	
22584    	}
22585
22586		if (SitePreferences.GA_NEW_ACTIONS_ENABLED && $this.is(':checked')) {
22587			var refinementId = $this.closest('[refinement-id]').attr('refinement-id') || null;
22588			var refinementValue = $this.val() ? $this.val().replace('"'," INCH") : null;
22589			if (refinementId && refinementValue) {
22590				var refinementAnalyticsTaggingData =  { 
22591					'action_name': 'filter',
22592					'action_page_type': 'plp',
22593					'action_value': refinementId, 
22594					'action_items': refinementValue
22595				};
22596				GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', refinementAnalyticsTaggingData);
22597			}
22598		}
22599
22600		var url = $(this).parents('li').first().find('.hidden-refinement-link-container > a').first().attr('href');
22601
22602		updateProductListing(url);
22603    });
22604
22605	$main.on('keyup', '.refinement .checks label', function (event) {
22606		if (event.keyCode == 13) {
22607			$(this).siblings('input').trigger('click');
22608		}
22609	});
22610
22611    $main.on('click', '.result-options .switch input[name="field-switch"]', function () {
22612    	if (checkForUpdatedRefinementHandling()) {
22613    		updateProductListing($(this).parents('.switch').find('.visually-hidden input[name="field-true"] + a').first().attr('href'));
22614    	} else {
22615    		document.location = $(this).parents('.switch').find('.visually-hidden input[name="field-true"] + a').first().attr('href');
22616    	}
22617    });
22618
22619    $(document).on('click', '.list-selections li:not(.pending-selections) a.close-btn, .clear-all .close-btn', function (e) {
22620    	e.preventDefault();
22621
22622    	var $this = $(this);
22623    	if ($this.closest('.value').hasClass('applied') && $this.closest('.modal').length > 0) {
22624    		$this.closest('.value').toggleClass('pending-clear');
22625    		redrawUpdatedRefinementHandling();
22626    		return;	
22627    	}
22628
22629		$('body').loading(loadingIndicatorSettings);
22630		var activeRefinementsCount = $('[refinement-value].selected').length - $('.swatches[class*="popular-"] [refinement-value].selected').length;
22631		if (activeRefinementsCount == 1 && options.CategoryLandingPageContent && options.CategoryLandingPageContent.Url) {
22632			location.href = options.CategoryLandingPageContent.Url;
22633    		return;
22634		}
22635
22636    	var href = $this.attr('href');
22637    	if (href == null || href == 'null') {
22638    		var search = document.location.search;
22639    		updateProductListing(document.location.pathname + search.substr(0, search.indexOf('&')));
22640    	} else {
22641    		updateProductListing(href);
22642    	}
22643	});
22644    
22645    $(document).on('click', '.list-selections li.pending-selections a.close-btn', function (e) {
22646    	e.preventDefault();
22647
22648    	var pendingRefinementId = $(this).closest('[data-refgroup-id]').attr('data-refgroup-id');
22649    	var pendingRefinementValue = $(this).closest('span').attr('refinement-value');
22650		var selector = '[refinement-id="' + pendingRefinementId + '"] ul [refinement-value="' + pendingRefinementValue + '"]';
22651
22652    	var $pendingRefinementInput = $refinementsInner.find(selector);    	
22653    	if ($pendingRefinementInput.find('input').length > 0) {
22654    		$pendingRefinementInput.find('input').click();
22655    	} else if ($pendingRefinementInput.find('a').length > 0) {
22656    		$pendingRefinementInput.find('a').click();
22657    	}
22658	});
22659    
22660    $main.on('click', '.product-swatches-accordion-button', function () {
22661    	var $accordion = $(this).closest('.accordion');
22662    	
22663    	if ($accordion.length > 0) {
22664    		$accordion.toggleClass('active');
22665    	}
22666    });
22667
22668	$(document).off('click.ShopMyStore', '.shop-my-store .checkbox-toggle input').on('click.ShopMyStore', '.shop-my-store .checkbox-toggle input', function(event) {
22669		var $this = $(this);
22670
22671		var url;
22672		if ($this.prop('checked'))  {
22673			url = $this.attr('href-refine');
22674		} else {
22675			url = $this.attr('href-relax');
22676			$('body').attr('is-shop-my-store-active', false);
22677		}
22678
22679		if (url) {
22680			$('.shop-my-store .checkbox-toggle').loading({ IsLabelShown: false, IsImageShown: false });
22681			$('body').loading(loadingIndicatorSettings);
22682
22683			GoogleTagManagerClient.PushDataLayerForDataLayerType('ShopYourStore', { enabled: $this.prop('checked') });
22684
22685			url = util.appendParamToURL(url, 'shopmystore', $this.prop('checked'));
22686			updateProductListing(url);
22687		} else {
22688			event.preventDefault();
22689		}
22690    });
22691
22692	$(document).off('ShopMyStore.Update').on('ShopMyStore.Update', function(event, storeId) {
22693		if ($('.shop-my-store .checkbox-toggle input').prop('checked')) {
22694			var url = location.protocol + '//' + location.hostname + location.pathname;
22695			var queryStringParameters = util.getQueryStringParams(location.search);
22696			for (var queryStringParameterKey in queryStringParameters) {
22697				var queryStringParameterValue = queryStringParameters[queryStringParameterKey];
22698				if (queryStringParameterValue && queryStringParameterValue.indexOf('availableStores') > -1) {
22699					var refinementIndex;
22700					var refinementIndexMatch = queryStringParameterKey.match(/prefn([0-9])+/);
22701					if (refinementIndexMatch && refinementIndexMatch.length > 1) {
22702						refinementIndex = refinementIndexMatch[1];
22703					}				
22704				}
22705			}
22706	
22707			if (refinementIndex && storeId) {
22708				queryStringParameters['prefv' + refinementIndex] = storeId;
22709			}
22710	
22711			for (var queryStringParameterKey in queryStringParameters) {
22712				var queryStringParameterValue = queryStringParameters[queryStringParameterKey];
22713				if (queryStringParameterKey != 'start' && queryStringParameterKey != 'sz') {
22714					url = util.appendParamToURL(url, queryStringParameterKey, queryStringParameterValue);
22715				}
22716			}
22717
22718			$('body').loading({
22719				IsImageShown: false,
22720				Content: '<h6>Hold Your Horses!</h6><p>Your selections are loading...</p>',
22721				CssClass: 'shopyourstore',
22722				BackgroundColor: 'transparent'
22723			});
22724
22725			updateProductListing(url);
22726		} else {
22727			if (pageContext.site != SiteConstants.SiteIds.BootBarnUS || !SitePreferences.MOBILE_NAV_REDESIGN || window.innerWidth > 1024) {
22728				redrawShopMyStore();
22729			}
22730		}
22731	});
22732
22733    searchStores();
22734    initializeRefinementControls();
22735	if (pageContext.site === SiteConstants.SiteIds.BootBarnUS || pageContext.site === SiteConstants.SiteIds.BootBarnWorkforceUS) {
22736		$('#grid-sort-header').selectmenu({
22737			'width': 205
22738		});
22739	}
22740    
22741    $main.on('click', '.apply-refinements:not(.disabled)', function(event) {
22742    	event.preventDefault();
22743		$(this).attr('data-apply-refinements','true');
22744    	
22745		var pendingRefinementsDelta = $('#secondary [refinement-value]:not(.selected):not([class*="popular"]).modified').length - $('#secondary [refinement-value].selected:not([class*="popular"]).modified').length;
22746		var activeRefinementsCount = $('#secondary [refinement-value].selected').length - $('#secondary .swatches[class*="popular-"] [refinement-value].selected').length - $('#secondary .list-selections .pending-clear').length + pendingRefinementsDelta;
22747		if (activeRefinementsCount == 0 && options.CategoryLandingPageContent && options.CategoryLandingPageContent.Url) {
22748			location.href = options.CategoryLandingPageContent.Url;
22749    		return;
22750		}
22751
22752		if (SitePreferences.GA_NEW_ACTIONS_ENABLED) {
22753			var $refinement =  $('.refinements').find('[refinement-id]:not(.sort-by)');
22754			var $selectedRefinements = $refinement.find('ul:not([class*="popular"]) li[refinement-value]:not(.selected).modified, ul:not([class*="popular"]) li[refinement-value].selected:not(.modified)');
22755			if ($selectedRefinements.length > 0) {
22756				var refinementId, refinementValue;
22757				$selectedRefinements.each(function () {
22758					refinementId = $(this).closest('[refinement-id]').attr('refinement-id');
22759					refinementValue = refinementId.toUpperCase() == 'PRICE' ? $(this).find('.refinement-label').text().replace(/\s+/g, ' ').trim() : $(this).attr('refinement-value').replace(' INCH','"');
22760					if (refinementId && refinementValue) {
22761						var refinementAnalyticsTaggingData =  { 
22762							'action_name': 'filter',
22763							'action_page_type': 'plp',
22764							'action_value': refinementId, 
22765							'action_items': refinementValue
22766						};
22767						GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', refinementAnalyticsTaggingData);
22768					}
22769				});
22770			}	
22771			
22772			// The newSortValue will only be populated if a change to sort has occurred, and GA tagging only gets fired if newSortValue has data
22773			var newSortValue = $('#mobile-sort option:checked').length > 0 ? $('#mobile-sort option:checked')[0].label : null;
22774			var oldSortValue = previousSortApplied ? previousSortApplied : 'Top Sellers';
22775			if (newSortValue && oldSortValue) {
22776				var sortAnalyticsTaggingData =  { 
22777					'action_name': 'sort',
22778					'action_page_type': 'plp',
22779					'action_value': newSortValue, 
22780					'action_items': 'from ' + oldSortValue
22781				};
22782				GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', sortAnalyticsTaggingData);
22783			}
22784			previousSortApplied = newSortValue;
22785		}
22786		
22787    	$('body').loading(loadingIndicatorSettings);
22788    	updateProductListing(buildUrlForSelectedRefinements(null, null, false, true));
22789    });
22790    
22791    $main.on('click', '.clear-refinements:not(.disabled)', function(event) {
22792    	event.preventDefault();
22793    	
22794    	var $this = $(this);
22795    	var href = $this.attr('href');
22796    	var $refinementsModalProductSelections = $('.refinements-inner-container-header .product-selections');
22797    	
22798    	if ($refinementsModalProductSelections.find('.list-selections .value.applied').length > 0) {
22799    		if (checkForUpdatedRefinementHandling()) {
22800    			$('.clear-refinements').loading({
22801    				IsImageShown: false	
22802    			});
22803    			updateProductListing($(this).attr('href'));
22804    		} else {
22805    			document.location = $(this).attr('href');
22806    		}
22807    	} else {
22808    		$('.refinement[uuid]').find('li[refinement-value]:not(.selected).modified').removeClass('modified');
22809    		$('.refinement[uuid]').find('li[refinement-value]:not(.selected)').find('input
22809:checked').prop('checked', false);
22810    		redrawUpdatedRefinementHandling();
22811    	}
22812    });
22813
22814	$(document).on('click', '#main .btn.load-more:not(.disabled)', function(event) {
22815		event.preventDefault();
22816		var $loadMoreButton = $(this);
22817
22818		// Let's disable the button after first click, so that functionality can complete before being re-enabled
22819		$loadMoreButton.addClass('disabled');
22820
22821		loadMore(function() {
22822			// Use the callback function to re-enable the Load More button
22823			$loadMoreButton.removeClass('disabled');
22824		});
22825	});
22826
22827	$(document).on('click', '[refinement-open], [filter-open]', function(event) {
22828		var $this = $(this);
22829		var refinementId = $this.attr('refinement-open') || $this.attr('filter-open');
22830		if (refinementId && $('#attribute-refinement-control-button-' + refinementId).length > 0) {
22831			toggleRefinements();
22832			$('#attribute-refinement-control-button-' + refinementId).click();
22833		}
22834	});
22835}
22836
22837function loadMore(callback) {
22838	if (!dataBuffer['PaginationIndex']) {
22839		dataBuffer['PaginationIndex'] = options.Pagination.Start;
22840	}
22841	if (!dataBuffer['PaginationCount']) {
22842		dataBuffer['PaginationCount'] = options.Pagination.Count;
22843	}
22844	
22845	var url = document.location.href;
22846	var currentQueryStringParameters = util.getQueryStringParams(util.getQueryString(url));
22847	if (currentQueryStringParameters['start']) {
22848		url = util.removeParamFromURL(url, 'start');
22849	}
22850	url = util.appendParamToURL(url, 'start', dataBuffer['PaginationIndex'] + options.Pagination.Size);
22851
22852	updateProductListing(url, callback, true);
22853}
22854
22855function toggleBodyScrollLock() {
22856	var $body = $('body');
22857	var bodyScrollLockClass = 'refinements-mobile-scroll-lock';
22858
22859	if ($body.hasClass(bodyScrollLockClass)) {
22860		$body.removeClass(bodyScrollLockClass);
22861		$body.css('top', '');
22862		document.body.scrollTop = document.documentElement.scrollTop = dataBuffer['scrollTop'];
22863	} else {
22864		var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
22865    	dataBuffer['scrollTop'] = scrollTop;
22866    	$body.css('top', 0 - scrollTop);
22867    	$body.addClass(bodyScrollLockClass);
22868	}
22869}
22870
22871function buildUrlForSelectedRefinements(baseUrl, urlForParameters, includeCategory, includeSort) {
22872	var refinementValuesBuffer = {};
22873	
22874	$refinementsInner.find('[refinement-id]:not(.price-refinement):not(.sort-by)').each(function() {
22875		var $refinement = $(this);
22876		var $selectedRefinements = $refinement.find('li[refinement-value]:not(.selected).modified, li[refinement-value].selected:not(.modified)');
22877		
22878		if ($selectedRefinements.length > 0) {
22879    		var refinementId = $refinement.attr('refinement-id');
22880    		var mappedRefinementId;
22881			if (refinementId != 'availableStores' && refinementId.indexOf('availableStores') == 0) {
22882				mappedRefinementId = 'availableStores';	
22883			} else {
22884				mappedRefinementId = refinementId;
22885			}
22886    		
22887    		if (!refinementValuesBuffer[mappedRefinementId]) {
22888    			refinementValuesBuffer[mappedRefinementId] = [];
22889    		}
22890    		
22891    		$selectedRefinements.each(function () {
22892    			var value = $(this).attr('refinement-value').replace(' INCH','"');
22893    			var isNewValue = true;
22894    			for (var index = 0; index < refinementValuesBuffer[mappedRefinementId].length; index++) {
22895    				if (refinementValuesBuffer[mappedRefinementId][index] == value) {
22896    					isNewValue = false;
22897    				}
22898				}
22899				
22900    			if (isNewValue && $refinementsInner.find('.list-selections [data-refgroup-id="' + refinementId + '"] [value="' + $(this).attr('refinement-value').replace(/["']/g, "") + '"].pending-clear').length == 0) {
22901					refinementValuesBuffer[mappedRefinementId].push(value);
22902    			}
22903    		});
22904		}
22905	});
22906	
22907	var url = baseUrl || options.SearchUrl;
22908	var currentCategoryId = $('.currCGID').val();
22909	if (includeCategory && currentCategoryId && currentCategoryId != 'null') {
22910		url = util.removeParamFromURL(url, 'cgid');
22911		url = util.appendParamToURL(url, 'cgid', $('.currCGID').val());
22912	}
22913	
22914	var currentQueryString = util.getQueryString(urlForParameters || document.location.href);
22915	var currentQueryStringParameters = util.getQueryStringParams(currentQueryString);
22916	if (currentQueryStringParameters['q']) {
22917		url = util.removeParamFromURL(url, 'q');
22918		url = util.appendParamToURL(url, 'q', options.SearchPhrase);
22919	}
22920	
22921	if (currentQueryStringParameters['srule']) {
22922		url = util.removeParamFromURL(url, 'srule');
22923		url = util.appendParamToURL(url, 'srule', currentQueryStringParameters['srule']);
22924	}
22925
22926	var availableStoresValue;
22927	var activeRefinementIndex = 1;
22928	while (url.indexOf('prefn' + activeRefinementIndex) >= 0) {
22929		if (currentQueryStringParameters['prefn' + activeRefinementIndex] == 'availableStores') {
22930			availableStoresValue = currentQueryStringParameters['prefv' + activeRefinementIndex];
22931		}
22932
22933		url = util.removeParamFromURL(url, 'prefn' + activeRefinementIndex);
22934		url = util.removeParamFromURL(url, 'prefv' + activeRefinementIndex);
22935		activeRefinementIndex++;
22936	}
22937
22938	var availableStoresRefinementIndex;
22939	for (var currentQueryStringParameterKey in currentQueryStringParameters) {
22940		var currentQueryStringParameterValue = currentQueryStringParameters[currentQueryStringParameterKey];
22941		if (currentQueryStringParameterValue && currentQueryStringParameterValue.indexOf('availableStores') > -1) {
22942			var refinementIndexMatch = currentQueryStringParameterKey.match(/prefn([0-9])+/);
22943			if (refinementIndexMatch && refinementIndexMatch.length > 1) {
22944				availableStoresRefinementIndex = refinementIndexMatch[1];
22945			}
22946		}
22947	}
22948
22949	if (!availableStoresValue && availableStoresRefinementIndex) {
22950		availableStoresValue = currentQueryStringParameters['prefv' + availableStoresRefinementIndex];
22951	}
22952
22953	if (availableStoresValue && !refinementValuesBuffer['availableStores']) {
22954		refinementValuesBuffer['availableStores'] = [];
22955		refinementValuesBuffer['availableStores'].push(availableStoresValue);
22956	}
22957	
22958	var activeRefinementIndex = 1;
22959	for (var refinementId in refinementValuesBuffer) {
22960		if (refinementId && refinementValuesBuffer[refinementId].length > 0) {
22961			url = util.appendParamToURL(url, 'prefn' + activeRefinementIndex, refinementId);
22962			url = util.appendParamToURL(url, 'prefv' + activeRefinementIndex, refinementValuesBuffer[refinementId].join('|'));
22963			activeRefinementIndex++;
22964		}
22965	}
22966	
22967	if ($('[is-price-refinement="true"] input:checked').length > 0 || SitePreferences.PRICE_SLIDER_ENABLED) {
22968		var currentWindowUrlQS = util.getQueryString(window.location.href);
22969		var urlParams = util.getQueryStringParams(currentWindowUrlQS); // Get url parameters in object format
22970		var $refinementPrice = $('[is-price-refinement="true"] input:checked').closest('[is-price-refinement="true"]');
22971
22972		url = util.removeParamFromURL(url, 'pmin');
22973		url = util.removeParamFromURL(url, 'pmax');
22974
22975		if (SitePreferences.PRICE_SLIDER_ENABLED) { 
22976			if ($('html').hasClass('size-mobile')) {
22977				url = initMobilePriceSlider(url);
22978			} else {
22979				if (urlParams.pmin) {
22980					url = util.appendParamToURL(url, 'pmin', urlParams.pmin);
22981				}
22982				if (urlParams.pmax) {
22983					url = util.appendParamToURL(url, 'pmax', urlParams.pmax);
22984				}
22985			}
22986		} else {
22987			if ($refinementPrice.attr('pmin')) {
22988				url = util.appendParamToURL(url, 'pmin', $refinementPrice.attr('pmin'));
22989			}
22990			if ($refinementPrice.attr('pmax')) {
22991				url = util.appendParamToURL(url, 'pmax', $refinementPrice.attr('pmax'));
22992			}
22993		}
22994	}
22995
22996	if (includeSort && checkForUpdatedRefinementHandling()) {
22997		url = util.removeParamFromURL(url, 'srule');
22998		
22999		var $sortingRule = $refinementsInner.find('.sort-by option:checked, .sort-by input:checked');
23000		if ($sortingRule && $sortingRule.length > 0) {
23001			var sortingRule = $sortingRule.attr('sorting-rule');
23002			if ($sortingRule.attr('default') != 'true') {
23003				url = util.appendParamToURL(url, 'srule', sortingRule);
23004			}
23005		}
23006	}
23007
23008	return url;
23009}
23010
23011function checkForUpdatedRefinementHandling() {
23012	if ((options.RefinementsUIVersion == RefinementUIVersionConstants.Updated) && $html.hasClass('size-mobile') && (pageContext.site != SiteConstants.SiteIds.BootBarnRspUS || options.StoreUIVersion == '2024Redesign')) {
23013    	return true;
23014	}
23015	
23016	return false;
23017}
23018
23019function redrawUpdatedRefinementHandling($contentParameter, urlForParameters) {
23020	var $content;
23021	if ($contentParameter && $contentParameter.length > 0) {
23022		$content = $contentParameter.find('.refinements');
23023	}
23024	if (!$content || $content.length == 0) {
23025		$content = $refinementsInner;
23026	}
23027
23028	//updateMultiPriceRefinements($content, urlForParameters);
23029	updateRefinementCounts($content, urlForParameters);
23030	if (options.StoreUIVersion == '2024Redesign') {
23031		updateSelectedRefinements();
23032	}
23033	updatePendingRefinements();
23034
23035	var isSortingChanged = false;
23036	var $sortingRule = $('.modal.open .sort-by option:checked, .modal.open .sort-by input:checked');
23037	if ($sortingRule && $sortingRule.length > 0) {
23038		isSortingChanged = !$sortingRule.attr('selected');
23039	}
23040
23041	if (isSortingChanged || $content.find('[refinement-value].modified, .list-selections .value.pending-clear').length > 0) {
23042		$content.addClass('refinements-modified');
23043		$('.refinements-inner-container-footer .apply-refinements').removeClass('disabled');
23044	} else {
23045		$content.removeClass('refinements-modified');
23046		$('.refinements-inner-container-footer .apply-refinements').addClass('disabled');
23047	}
23048	
23049	if ($('[is-price-refinement="true"] input:checked').length > 0) {
23050		$content.addClass('price-selected');
23051	} else {
23052		$content.removeClass('price-selected');
23053	}
23054	
23055	var $refinementsModalProductSelections = $('.refinements-inner-container-header .product-selections');	
23056	if (($refinementsModalProductSelections.find('.list-selections .value.applied').length + $refinementsModalProductSelections.find('.list-selections > .pending-selections > span').length) > 0) {
23057		$refinementsModalProductSelections.show();
23058		$('.refinements-inner-container-footer .clear-refinements').removeClass('disabled');
23059	} else {
23060		$refinementsModalProductSelections.hide();
23061		$('.refinements-inner-container-footer .clear-refinements').addClass('disabled');
23062	}
23063	var $productSelectionContainer;
23064	if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
23065		$productSelectionContainer = $('.primary-content > .sort-and-refine > .product-selections');
23066	} else {
23067		$productSelectionContainer = $('.primary-content > .product-selections');
23068	}
23069
23070	if ($productSelectionContainer.find('.list-selections .value.applied').length > 0) {
23071		$productSelectionContainer.show();
23072	} else {
23073		$productSelectionContainer.hide();
23074	}
23075}
23076
23077function updateSelectedRefinements() {
23078	var $refinements = $refinementsInner.find('.refinement[uuid].expanded');
23079	$refinements.each(function() {
23080		var $refinement = $(this);
23081
23082		var $selectedRefinementsContainer = $refinement.find('.toggle').find('.list-selections');
23083		if (!$selectedRefinementsContainer || !$selectedRefinementsContainer.length) {
23084			$refinement.find('.toggle').append('<div class="product-selections"><ul class="list-selections"/></div>');
23085			$selectedRefinementsContainer = $refinement.find('.toggle .list-selections');
23086		}
23087
23088		var $selectedRefinementsValues;
23089		if (options.StoreUIVersion == '2024Redesign') {
23090			$selectedRefinementsValues = $refinement.find('ul:not(.popular)').find('li[refinement-value].selected');
23091		} else {
23092			$selectedRefinementsValues = $refinement.find('ul:not(.popular)').find('li[refinement-value].selected, input:checked:not(.modified)');
23093		}
23094		var selectedRefinementsBuffer = [];
23095		$selectedRefinementsValues.each(function(index, element) {
23096			var $this = $(this);
23097			var selectedRefinementId = $this.closest('[refinement-id]').attr('refinement-id');
23098			var selectedRefinementValue = $this.closest('[refinement-value]').attr('refinement-value');
23099			var selectedRefinementText = $this.closest('[refinement-value]').attr('display-value') || selectedRefinementValue;
23100			
23101			if (!selectedRefinementsBuffer[selectedRefinementId]) {
23102				selectedRefinementsBuffer[selectedRefinementId] = [];
23103			}
23104			
23105			var isNewValue = true;
23106			for (var index = 0; index < selectedRefinementsBuffer[selectedRefinementId].length; index++) {
23107				if (selectedRefinementsBuffer[selectedRefinementId][index].Value == selectedRefinementValue) {
23108					isNewValue = false;
23109				}
23110			}
23111			
23112			if (isNewValue) {
23113				selectedRefinementsBuffer[selectedRefinementId].push({
23114					Text: selectedRefinementText,
23115					Value: selectedRefinementValue
23116				});
23117			}
23118		});
23119
23120		var selectedRefinementsHtml = '';
23121		for (var selectedRefinementElement in selectedRefinementsBuffer) {
23122			var selectedRefinement = selectedRefinementsBuffer[selectedRefinementElement];
23123			
23124			var selectedRefinementValuesHtml = '';
23125			for (var selectedRefinementDataElement in selectedRefinement) {
23126				var selectedRefinementData = selectedRefinement[selectedRefinementDataElement];
23127				var selectedRefinementText = selectedRefinementData.Text;
23128				var selectedRefinementValue = selectedRefinementData.Value;
23129				
23130				if (selectedRefinementValuesHtml) {
23131					selectedRefinementValuesHtml += '<span class="delimiter">,</span>';
23132				}
23133				
23134				var selectedRefinementDisplayValue = selectedRefinementValue;
23135				if (selectedRefinementElement == 'Price') {
23136					selectedRefinementDisplayValue = $refinementsInner.find('[refinement-id="' + selectedRefinementElement + '"]').attr('display-value');
23137				} else if (selectedRefinementElement == 'size') {
23138					selectedRefinementDisplayValue = 'Size ' + selectedRefinementDisplayValue;
23139				}
23140				
23141				selectedRefinementValuesHtml += '<span class="value" refinement-value="' + selectedRefinementValue + '">' +
23142													selectedRefinementText +
23143													'<a class="close-btn"><i class="fa fa-close"></i></a>' +
23144												'</span>';
23145			}
23146			
23147			selectedRefinementsHtml += '<span data-refgroup-id="' + selectedRefinementElement + '">' +
23148											'<span class="key">' + selectedRefinementElement + '</span>' +
23149											'<span class="colon">:</span>' +
23150											selectedRefinementValuesHtml +
23151										'</span>';
23152		}
23153		
23154		$selectedRefinementsContainer.html(selectedRefinementsHtml);
23155	});
23156}
23157
23158function updatePendingRefinements() {
23159	var $pendingRefinementsContainer;
23160	if (options.StoreUIVersion == '2024Redesign') {
23161		var $selectedRefinementsContainer = $refinementsInner.find('.refinement.expanded .toggle').find('.product-selections');
23162		if (!$selectedRefinementsContainer || !$selectedRefinementsContainer.length) {
23163			$refinementsInner.find('.refinement.expanded .toggle').append('<div class="product-selections"><ul class="list-selections"/></div>');
23164		}
23165
23166		$pendingRefinementsContainer = $refinementsInner.find('.refinement.expanded .toggle').find('.pending-selections');
23167		if (!$pendingRefinementsContainer || !$pendingRefinementsContainer.length) {
23168			$refinementsInner.find('.refinement.expanded .toggle .list-selections').append('<li class="pending-selections"/>');
23169			$pendingRefinementsContainer = $refinementsInner.find('.refinement.expanded .toggle .list-selections .pending-selections');
23170		}
23171	} else {
23172		$pendingRefinementsContainer = $refinementsInner.find('.pending-selections');
23173	}
23174	
23175	var $pendingRefinementsValues;
23176	if (options.StoreUIVersion == '2024Redesign') {
23177		$pendingRefinementsValues = $('.refinement[uuid].expanded ul:not(.popular)').find('li[refinement-value]:not(.selected).modified, input.modified:not([checked="checked"])');
23178	} else {
23179		$pendingRefinementsValues = $('.refinement[uuid] ul:not(.popular)').find('li[refinement-value]:not(.selected).modified');
23180	}
23181	var pendingRefinementsBuffer = [];
23182
23183	$pendingRefinementsValues.each(function(index, element) {
23184		var $this = $(this);
23185		var pendingRefinementId = $this.closest('[refinement-id]').attr('refinement-id');
23186		var pendingRefinementValue = $this.closest('[refinement-value]').attr('refinement-value');
23187		var pendingRefinementText = $this.closest('[refinement-value]').attr('display-value') || pendingRefinementValue;
23188		
23189		if (!pendingRefinementsBuffer[pendingRefinementId]) {
23190			pendingRefinementsBuffer[pendingRefinementId] = [];
23191		}
23192		
23193		var isNewValue = true;
23194		for (var index = 0; index < pendingRefinementsBuffer[pendingRefinementId].length; index++) {
23195			if (pendingRefinementsBuffer[pendingRefinementId][index].Value == pendingRefinementValue) {
23196				isNewValue = false;
23197			}
23198		}
23199		
23200		if (isNewValue) {
23201			pendingRefinementsBuffer[pendingRefinementId].push({
23202				Text: pendingRefinementText,
23203				Value: pendingRefinementValue
23204			});
23205		}
23206	});
23207	
23208	var pendingRefinementsHtml = '';
23209	for (var pendingRefinementElement in pendingRefinementsBuffer) {
23210		var pendingRefinement = pendingRefinementsBuffer[pendingRefinementElement];
23211		
23212		var pendingRefinementValuesHtml = '';
23213		for (var pendingRefinementDataElement in pendingRefinement) {
23214			var pendingRefinementData = pendingRefinement[pendingRefinementDataElement];
23215			var pendingRefinementText = pendingRefinementData.Text;
23216			var pendingRefinementValue = pendingRefinementData.Value;
23217			
23218			if (pendingRefinementValuesHtml) {
23219				pendingRefinementValuesHtml += '<span class="delimiter">,</span>';
23220			}
23221			
23222			var pendingRefinementDisplayValue = pendingRefinementValue;
23223			if (pendingRefinementElement == 'Price') {
23224				pendingRefinementDisplayValue = $refinementsInner.find('[refinement-id="' + pendingRefinementElement + '"]').attr('display-value');
23225			} else if (pendingRefinementElement == 'size') {
23226				pendingRefinementDisplayValue = 'Size ' + pendingRefinementDisplayValue;
23227			}
23228			
23229			pendingRefinementValuesHtml += '<span class="value" refinement-value="' + pendingRefinementValue + '">' +
23230												pendingRefinementText +
23231												'<a class="close-btn"><i class="fa fa-close"></i></a>' +
23232											'</span>';
23233		}
23234		
23235		pendingRefinementsHtml += '<span data-refgroup-id="' + pendingRefinementElement + '">' +
23236										'<span class="key">' + pendingRefinementElement + '</span>' +
23237										'<span class="colon">:</span>' +
23238										pendingRefinementValuesHtml +
23239									'</span>';
23240	}
23241	
23242	$pendingRefinementsContainer.html(pendingRefinementsHtml);
23243}
23244
23245function updateRefinementCounts($contentParameter, urlForParameters) {
23246	var url = buildUrlForSelectedRefinements(Urls.getRefinementCounts, null, true);
23247	var $content = $contentParameter || $(document);
23248	var $refinements = $content.find('.refinement[uuid]');
23249	var $refinementProductCount = $content.find('.refinement button .product-count');
23250	$refinementProductCount.loading();
23251
23252	var refinementGroups = [];
23253	var refinementGroupIndex = 0;
23254	var refinementGroupSizeLimit = 2000;
23255	var refinementGroupSizeCurrent = 0;
23256	$.each($refinements, function(index, element) {
23257		var $refinement = $(element);
23258		var refinementValueCount = $refinement.find('[refinement-value]').length;
23259		if ((refinementValueCount + refinementGroupSizeCurrent) >= refinementGroupSizeLimit) {
23260			refinementGroupIndex++;
23261			refinementGroupSizeCurrent = 0;
23262		}
23263
23264		if (!refinementGroups[refinementGroupIndex]) {
23265			refinementGroups[refinementGroupIndex] = $refinement;
23266		} else {
23267			refinementGroups[refinementGroupIndex] = refinementGroups[refinementGroupIndex].add($refinement);
23268		}
23269
23270		refinementGroupSizeCurrent += refinementValueCount;
23271	});
23272
23273	dataBuffer['updateRefinementCountsRefinementGroupIndex'] = 0;
23274	dataBuffer['updateRefinementCountsRefinementGroupCount'] = refinementGroups.length;
23275
23276	$.each(refinementGroups, function(index, element) {
23277		var $refinementGroup = $(element);
23278		updateRefinementCountsForRefinements(url, $refinementGroup);
23279	});
23280}
23281
23282function updateRefinementCountsForRefinements(url, $refinements) {
23283	var refinementIds = $refinements.map(function(index, element) {
23284		return $(element).attr('refinement-id');
23285	}).get();
23286
23287	if (refinementIds) {
23288		var $refinementProductCount = $refinements.find('button .product-count');
23289		$refinementProductCount.loading();
23290
23291		if (dataBuffer['updateRefinementCountsRefinementGroupCount'] && dataBuffer['updateRefinementCountsRefinementGroupCount'] > 1) {
23292			url = util.appendParamToURL(url, 'refinements', refinementIds.join());
23293		}
23294
23295		$.ajax({
23296			type: 'get',
23297			url: url,
23298			success: function(data) {
23299				$refinements.each(function(index, element) {
23300					var $refinement = $(element);
23301					if (data) {
23302						var refinementId = $refinement.attr('refinement-id');
23303						var $refinementValues = $refinement.find('ul li[refinement-value]');
23304						if (data.Refinements[refinementId]) {
23305							$.each($refinementValues, function(index, element) {
23306								var $refinementValue = $(element);
23307								var refinementValue = $refinementValue.attr('refinement-value');
23308								var refinementHtml = data.Refinements[refinementId][refinementValue] ? data.Refinements[refinementId][refinementValue] : '0';
23309								refinementHtml += '<span class="visually-hidden">&nbsp;Product Count</span>';
23310
23311								if ($refinementValue.find('input[type="checkbox"]').prop('checked') || data.Refinements[refinementId][refinementValue]) {
23312									$refinementValue.attr('refinement-count', data.Refinements[refinementId][refinementValue]).find('.refinement-label').html(refinementValue).find('.refinement-count').html(refinementHtml);
23313								} else {
23314									$refinementValue.attr('refinement-count', 0).find('.refinement-label').html(refinementValue).find('.refinement-count').html(refinementHtml);
23315								}
23316							});
23317
23318							var $popularRefinementValues = $refinement.find('ul.popular-sizes, ul.popular').find('li[refinement-value]');
23319							var $popularRefinementValuesChecksContainer = $refinement.find('.checks-container.popular');
23320							var hasPopularRefinementValues = $popularRefinementValues.filter(':not([refinement-count="0"])').length > 0 || $popularRefinementValues.filter(':not([refinement-count="0"])').find('input
23320:checked').length > 0;
23321							if (hasPopularRefinementValues) {
23322								$popularRefinementValuesChecksContainer.show();
23323							} else {
23324								$popularRefinementValuesChecksContainer.hide();
23325							}
23326							
23327							var $normalRefinementValues = $refinement.find('ul:not(.popular-sizes):not(.popular)').find('li[refinement-value]');
23328							var $normalRefinementValuesChecksContainer = $refinement.find('.checks-container:not(.popular)');
23329							var hasNormalRefinementValues = $normalRefinementValues.filter(':not([refinement-count="0"])').length > 0 || $normalRefinementValues.filter(':not([refinement-count="0"])').find('input:checked').length > 0;
23330							if (hasNormalRefinementValues) {
23331								$normalRefinementValuesChecksContainer.show();
23332							} else {
23333								$normalRefinementValuesChecksContainer.hide();
23334							}
23335
23336							/* --- Price slider modifications --- */
23337							var hasPriceSlider = SitePreferences.PRICE_SLIDER_ENABLED && (refinementId == 'Price' || refinementId == 'ref-postDiscountPrice'); 
23338							if (hasPriceSlider) {
23339								var pageUrl = util.getQueryString(window.location.href);
23340								if (pageUrl) {
23341									var hasPriceAttributes = pageUrl.indexOf('pmin') > -1 || pageUrl.indexOf('pmax') > -1;
23342									var isMobile = $('html').hasClass('size-mobile');
23343									if (hasPriceAttributes && !isMobile) {
23344										$('.price-refinement').find('.refinement-body').show();
23345									} else {
23346										$('.price-refinement').find('.refinement-body').hide();
23347									}
23348								}
23349							}
23350
23351							if (hasNormalRefinementValues || hasPopularRefinementValues || hasPriceSlider) {
23352								$refinement.removeClass('hidden');
23353							} else {
23354								$refinement.addClass('hidden');
23355							}
23356						} else {
23357							$refinement.addClass('hidden');
23358						}
23359					} else {
23360						if ($refinement.find('ul.checks, ul.swatches').find('li').length == 0) {
23361							$refinement.addClass('hidden');
23362						} else {
23363							$refinement.removeClass('hidden');
23364						}
23365					}
23366					$refinement.removeAttr('loading');
23367					updateMoreLess($refinement);
23368				});
23369			},
23370			complete: function() {
23371				dataBuffer['updateRefinementCountsRefinementGroupIndex']++;
23372				// ADA compliance - for easier readability for screen readers
23373				$('.refinements .selected[refinement-value] span.refinement-label').prepend('<span class="visually-hidden">Currently Selected:&nbsp;</span>');
23374				if ($refinementProductCount.attr('loading')) {
23375					$refinementProductCount.loading('destroy');
23376				}
23377				updateRefinementCountsComplete();
23378			}
23379		});
23380	}
23381}
23382
23383function updateRefinementCountsComplete() {
23384	if (dataBuffer['updateRefinementCountsRefinementGroupIndex'] == dataBuffer['updateRefinementCountsRefinementGroupCount']) {
23385		updateProductCountInHeader();
23386		updateRefinementsInHeader();
23387	}
23388}
23389
23390function updateProductCountInHeader() {
23391	var $refinementsProductCount = $('.refinements .slot-grid-subheader .product-count span');
23392	var $headerProductCount = $('.top-banner .slot-grid-subheader .product-count span');
23393
23394	if ($refinementsProductCount.length > 0 && $headerProductCount.length > 0) {
23395		$headerProductCount.text($refinementsProductCount.text());
23396	}
23397}
23398
23399function updateRefinementsInHeader() {
23400	var $refinementsRefinements = $('.primary-content .product-selections');
23401	var $headerRefinements = $('.top-banner .product-selections');
23402
23403	if ($refinementsRefinements.length > 0 && $headerRefinements.length > 0) {
23404		$headerRefinements.html($refinementsRefinements.html());
23405	}
23406}
23407
23408function updateMoreLess($refinement) {
23409	var refinementId = $refinement.attr('refinement-id');
23410	var $refinementValuesContainer = $refinement.find('ul:not(.popular-sizes):not(.popular)');
23411	var $refinementContainer = $refinementValuesContainer.closest('.refinement-body-inner');
23412	var $moreLink = $refinementValuesContainer.find('.ref-filter.more-link').closest('li').detach().first();
23413	var $refinementValues = $refinementValuesContainer.find('li[refinement-value]');
23414	var $refinementValuesVisible = $refinementValues.filter(':not([refinement-count="0"])');
23415
23416	if (!$refinement.hasClass('more-expanded') && $moreLink.length > 0) {
23417		var moreLessCount;
23418		if ((pageContext.site == SiteConstants.SiteIds.BootBarnUS || pageContext.site == SiteConstants.SiteIds.SheplersUS) && $refinement.attr('refinement-id') && $refinement.attr('refinement-id').toLowerCase() == 'size') {
23419			moreLessCount = 12;
23420		} else if (pageContext.site == SiteConstants.SiteIds.BootBarnRspUS && options.StoreUIVersion == '2024Redesign') {
23421			moreLessCount = 100000;
23422		} else {
23423			moreLessCount = 9;
23424		}
23425
23426		if ($refinementValuesVisible.length > moreLessCount) {
23427			var moreLessIndex = $refinementValuesVisible.index($refinementValuesVisible.eq(moreLessCount));
23428			$refinementValuesVisible.slice(0, moreLessIndex).removeClass('toggleable hidden');
23429			$refinementValuesVisible.slice(moreLessIndex, $refinementValues.length).addClass('toggleable hidden');
23430			$refinementValuesVisible.eq(moreLessIndex - 1).after($moreLink);
23431			$refinementValuesContainer.find('.more-link-container').show();
23432			$refinementContainer.find('.less-refinement').show();
23433		} else {
23434			$refinementValuesVisible.removeClass('toggleable hidden');
23435			$refinementValuesVisible.eq($refinementValuesVisible.length - 1).after($moreLink);
23436			$refinementValuesContainer.find('.more-link-container').hide();
23437			$refinementContainer.find('.less-refinement').hide();
23438		}
23439	}
23440
23441	if (!$('html').hasClass('size-mobile') || window.innerWidth > 767) {
23442		if (refinementsExpanded) {
23443			if (refinementsExpanded.indexOf(refinementId) > -1) {
23444				$refinement.addClass('expanded');
23445				$refinement.find('.toggle').addClass('expanded');
23446				$refinement.find('.refinement-body').show();
23447			} else {
23448				$refinement.removeClass('expanded');
23449				$refinement.find('.toggle').removeClass('expanded');
23450				$refinement.find('.refinement-body').hide();
23451			}
23452		}
23453		if ($('.refinement.expanded').length > 0) {
23454			$('.refinements').addClass('refinement-expanded');
23455		} else {
23456			$('.refinements').removeClass('refinement-expanded');
23457		}
23458
23459		if (refinementsViewAll && $refinementValuesVisible.length > moreLessCount) {
23460			if (refinementsViewAll.indexOf(refinementId) > -1) {
23461				$refinement.addClass('more-expanded');
23462				$refinement.find('.toggleable').removeClass('hidden');
23463				$refinement.find('.more-link-container').css('display', 'none');
23464				$refinement.find('.less-refinement.toggleable').removeClass('hidden');
23465			} else {
23466				$refinement.removeClass('more-expanded');
23467				$refinement.find('.more-link-container').css('display', 'list-item');
23468				$refinement.find('.less-refinement.toggleable').addClass('hidden');
23469			}
23470		}
23471	}
23472}
23473
23474var refGroupsStates = {
23475		cookieRefGroupsKey: 'refgroup',
23476		saveState: function(refgroup, visible) {
23477			if (this.getCookie('dw_cookies_accepted')) {
23478				var getRefGroupCookie = this.getCookie(this.cookieRefGroupsKey) || '{}';
23479				var getRefGroupObj = JSON.parse(getRefGroupCookie);
23480				getRefGroupObj[refgroup] = visible;
23481				getRefGroupCookie = JSON.stringify(getRefGroupObj);
23482				this.setCookie('refgroup', getRefGroupCookie);
23483			}
23484		},
23485		checkState: function(refgroup) {
23486			if (this.getCookie('dw_cookies_accepted')) {
23487				var getRefGroupCookie = this.getCookie(this.cookieRefGroupsKey);
23488				if (getRefGroupCookie) {
23489					var getRefGroupObj = JSON.parse(getRefGroupCookie);
23490					if (getRefGroupObj[refgroup] != undefined) {
23491						return getRefGroupObj[refgroup];
23492					}
23493				}
23494			}
23495			return true;
23496		},
23497		getCookie: function (key) {
23498		    var cookies = document.cookie.split(';');
23499		    for (var i = 0; i < cookies.length; i++) {
23500		        var tokens = cookies[i].split('=');
23501		        var cookieKey = tokens[0].trim();
23502		        if (cookieKey === key) {
23503		            return tokens[1];
23504		        }
23505		    }
23506		    return '';
23507		},
23508		setCookie: function(key, value, minutes) {
23509		    var date = new Date();
23510		    date.setTime(date + (minutes * 60 * 1000));
23511		    document.cookie = key + '=' + value + '; expires=' + date.toGMTString() + '; path=/';
23512		}
23513}
23514
23515function searchStores() {
23516	var searchStoresQuery = $('.search-stores-query');
23517
23518	$('.go-find-stores').on('click', function (e) {
23519		var el = $(e.target),
23520			data = {
23521				typeKey: el.attr('type')
23522			};
23523
23524		if (el.attr('type') === 'geo') {
23525			if (navigator.geolocation) {
23526				navigator.geolocation.getCurrentPosition(function(position) {
23527					data.longitude = position.coords.longitude;
23528					data.latitude = position.coords.latitude;
23529
23530					searchStoresAJAX(data);
23531				});
23532			} else {
23533				alert('Your browser not supported geolocation');
23534			}
23535		} else {
23536			data.searchKey = searchStoresQuery.val().trim();
23537
23538			var geocoder = new google.maps.Geocoder();
23539			geocoder.geocode( { 'address': searchStoresQuery.val().trim()}, function(results, status) {
23540				if (status == google.maps.GeocoderStatus.OK) {
23541				  	data.longitude = results[0].geometry.location.lng();
23542				  	data.latitude = results[0].geometry.location.lat();
23543
23544				  	searchStoresAJAX(data);
23545				} else {
23546					alert("Geocode was not successful for the following reason: " + status);
23547				}
23548		    });
23549		}
23550	});
23551}
23552
23553function searchStoresAJAX(params) {
23554	$.ajax({
23555		type: 'get',
23556		url: Urls.searchStores,
23557		data: params,
23558
23559		success: function (data) {
23560			var popupList = $('.category-popup-list-stores');
23561
23562			$('.grid-page-storeselector-popup').dialog('close');
23563			popupList
23564				.html(data)
23565				.dialog({
23566					width: 450,
23567					autoOpen: false,
23568					modal: true,
23569					resizable: false
23570				});
23571
23572			popupList.parent().find('.ui-dialog-title').html(data.match(/Stores near.{0,}/mi)[0].replace('</span>', ''));
23573			popupList.dialog('open');
23574		}
23575	});
23576}
23577
23578function showVariantInventoryMessaging() {
23579  	if (options && options.Bopis && options.Bopis.IsLocationSearchFiltered) {
23580		return;
23581	}
23582
23583  $.ajax({
23584      dataType: 'json',
23585      url: page.variantInventoryMessagingUrl,
23586      async: true
23587  })
23588  // success
23589  .done(function (values) {
23590	  if (values) {
23591		  values.forEach(function(val, idx) {
23592			  var $tile = $('[data-itemid="' + val.masterProductID + '"]');
23593			  if ($tile.length > 0) {
23594				  if ( typeof val.urlHash != 'undefined' ) {
23595					  var filterP = val.urlHash.replace('#','').split('=')[0];
23596					  var rr = new RegExp("prefn[0-9]="+filterP);
23597					  if ( location.href.match(rr) ) {
23598						  if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
23599							//Put the inventory message in the product badge if variantWhsInStock is false
23600							var productBadge = $tile.find('.product-badge.whs-in-stock');
23601							if (val.variantWhsInStock == 'true') {
23602								productBadge.show();
23603							} else if (val.dropshipLead <= 10) {
23604								productBadge.text(val.inventoryMsg);
23605								productBadge.removeClass('whs-in-stock').addClass('availability-msg').show();
23606							}
23607
23608						  } else {
23609							var $msg = $('<div />').attr('class', 'availability-msg').text(val.inventoryMsg); 
23610							$tile.find('.product-pricing').append($msg);
23611							if (val.variantWhsInStock == 'true') {
23612								$tile.find('.whs-in-stock').show();
23613							}
23614						  }
23615					  }
23616				  }
23617			  }
23618		  });
23619	  }
23620  })
23621  .fail(function (xhr, textStatus) {
23622      if (textStatus === 'parsererror') {
23623          console.log('Inventory Messaging Error ' + Resources.BAD_RESPONSE);
23624      }
23625  });
23626}
23627
23628function saveScrollPosition() {
23629	if (sessionStorage) {
23630		$(document).off('scroll.ScrollPositionSave').on('scroll.ScrollPositionSave', function(event) {
23631			var categoryId = $('.currCGID').val();
23632			var sessionStorageScrollPosition;
23633			try {
23634				sessionStorageScrollPosition = JSON.parse(sessionStorage.ScrollPosition);
23635			} catch(exception) {}
23636			if (!sessionStorageScrollPosition || typeof sessionStorageScrollPosition != 'object' || !sessionStorageScrollPosition[categoryId]) {
23637				sessionStorageScrollPosition = {};	
23638			}
23639			sessionStorageScrollPosition[categoryId] = $(window).scrollTop();
23640			sessionStorage.ScrollPosition = JSON.stringify(sessionStorageScrollPosition);
23641		});
23642	}
23643}
23644
23645function loadScrollPosition() {
23646	if (sessionStorage) {
23647		var categoryId = $('.currCGID').val();
23648		var sessionStorageScrollPosition;
23649		try {
23650			sessionStorageScrollPosition = JSON.parse(sessionStorage.ScrollPosition);
23651		} catch(exception) {}
23652		if (sessionStorageScrollPosition && typeof sessionStorageScrollPosition == 'object' && sessionStorageScrollPosition[categoryId]) {
23653			$(window).scrollTop(sessionStorageScrollPosition[categoryId]);
23654		}
23655	}
23656}
23657
23658function initializeStickyRefinements() {
23659	$(document).off('scroll.StickyRefinements StickyRefinements.Refresh').on('scroll.StickyRefinements StickyRefinements.Refresh', function(event) {
23660		var $refinements = $('#secondary');
23661		var $primary = $('#primary');
23662
23663		if ($('html').hasClass('size-mobile') || window.innerWidth <= 767) {
23664			$refinements.css('transform', '');
23665			$refinements.scrollTop(0);
23666			$refinements.height('auto');
23667		} else {
23668			var documentScrollTop = parseInt($(document).scrollTop(), 10);
23669			var documentScrollDelta = 0;
23670			if (dataBuffer['documentScrollTop']) {
23671				documentScrollDelta = documentScrollTop - dataBuffer['documentScrollTop'];
23672			}
23673			var windowHeight = parseInt($(window).height(), 10);
23674			var windowBottom = documentScrollTop + windowHeight;
23675			var refinementsScrollHeight = parseInt($refinements[0].scrollHeight, 10);
23676			var refinementsOffsetTop = parseInt($refinements.offset().top, 10);
23677			var refinementsOffsetBottom = parseInt($refinements.offset().top + refinementsScrollHeight, 10);
23678			var primaryTop = parseInt($primary.offset().top, 10);
23679			var primaryBottom = primaryTop + parseInt($primary.outerHeight(), 10);
23680			var documentScrollTopFromPrimaryTop = documentScrollTop - primaryTop;
23681            if (documentScrollTopFromPrimaryTop < 0) {
23682                documentScrollTopFromPrimaryTop = 0;
23683            }
23684            var windowBottomFromPrimaryBottom = windowBottom - primaryBottom;
23685            if (windowBottomFromPrimaryBottom < 0) {
23686                windowBottomFromPrimaryBottom = 0;
23687            }
23688            var refinementsOffsetTopNew;
23689            var refinementsOffsetBottomNew;
23690            if (documentScrollDelta) {
23691                refinementsOffsetTopNew = refinementsOffsetTop + documentScrollDelta;
23692                refinementsOffsetBottomNew = refinementsOffsetTopNew + refinementsScrollHeight;
23693            }
23694
23695			var refinementsTranslateY;
23696			if ($refinements.outerHeight() >= $primary.outerHeight()) {
23697				refinementsTranslateY = 0;
23698			} else if (documentScrollDelta >= 0) {
23699				refinementsTranslateY = refinementsOffsetTop - primaryTop;
23700
23701				if ($refinements.hasClass('top')) {
23702					$('body').removeClass('sticky-refinements');
23703					$refinements.removeClass('top bottom');
23704					refinementsTranslateY = refinementsTranslateY - documentScrollDelta;
23705				}
23706
23707				if (refinementsOffsetBottom >= primaryBottom) {
23708					$('body').removeClass('sticky-refinements');
23709					$refinements.removeClass('top bottom');
23710					refinementsTranslateY = primaryBottom - primaryTop - refinementsScrollHeight;
23711				} else if (refinementsOffsetBottom < windowBottom && primaryBottom >= windowBottom) {
23712					if (refinementsScrollHeight > windowHeight) {
23713						$('body').addClass('sticky-refinements');
23714						$refinements.removeClass('top bottom');
23715						$refinements.addClass('bottom');
23716					} else if (documentScrollTop >= primaryTop) {
23717						$('body').addClass('sticky-refinements');
23718						$refinements.removeClass('top bottom');
23719						$refinements.addClass('top');
23720					}
23721				} else if (refinementsOffsetBottom < windowBottom && primaryBottom < windowBottom) {
23722					if ((documentScrollTop + refinementsScrollHeight) > primaryBottom) {
23723						$('body').removeClass('sticky-refinements');
23724						$refinements.removeClass('top bottom');
23725						refinementsTranslateY = primaryBottom - primaryTop - refinementsScrollHeight;
23726					} else {
23727						$('body').addClass('sticky-refinements');
23728						$refinements.removeClass('top bottom');
23729						$refinements.addClass('top');
23730					}
23731				}
23732			} else if (documentScrollDelta < 0) {
23733				refinementsTranslateY = refinementsOffsetTop - primaryTop;
23734
23735				if ($refinements.hasClass('bottom')) {
23736					$('body').removeClass('sticky-refinements');
23737					$refinements.removeClass('top bottom');
23738					refinementsTranslateY = refinementsTranslateY - documentScrollDelta;
23739				}
23740
23741				if (refinementsOffsetTop <= primaryTop && documentScrollTop <= primaryTop) {
23742					$('body').removeClass('sticky-refinements');
23743					$refinements.removeClass('top bottom');
23744					refinementsTranslateY = 0;
23745				} else if (refinementsOffsetBottom >= primaryBottom && refinementsOffsetTop <= documentScrollTop) {
23746					$('body').removeClass('sticky-refinements');
23747					$refinements.removeClass('top bottom');
23748					refinementsTranslateY = primaryBottom - primaryTop - refinementsScrollHeight;
23749				} else if (refinementsOffsetTop >= documentScrollTop && refinementsOffsetTop > primaryTop) {
23750					$('body').addClass('sticky-refinements');
23751					$refinements.removeClass('top bottom');
23752					$refinements.addClass('top');
23753				}
23754			}
23755
23756			if (refinementsTranslateY != undefined) {
23757				$refinements.css('transform', 'translateY(' + refinementsTranslateY + 'px)');
23758			}
23759
23760			dataBuffer['documentScrollTop'] = documentScrollTop;
23761		}
23762	});
23763}
23764
23765function initializeStickySubheader() {
23766	$(document).off('scroll.StickySubheader').on('scroll.StickySubheader', function(event) {
23767		var $slotGridSubheader = $('.slot-grid-subheader');
23768		var $slotGridSubheaderFixed = $('.slot-grid-subheader-fixed');
23769		if ($slotGridSubheader.length > 0 && $slotGridSubheaderFixed.length > 0) {
23770			var $primary = $('#primary');
23771			var documentScrollTop = $(document).scrollTop();
23772			var windowHeight = $(window).height();
23773			var windowBottom = documentScrollTop + windowHeight;
23774			var primaryTop = $primary.offset().top;
23775			var primaryBottom = primaryTop + $primary.outerHeight();
23776
23777			if (primaryBottom < windowBottom) {
23778				$slotGridSubheaderFixed.addClass('hidden');
23779			} else if ($slotGridSubheader.offset().top < documentScrollTop) {
23780				$slotGridSubheaderFixed.removeClass('hidden');
23781			} else {
23782				$slotGridSubheaderFixed.addClass('hidden');
23783			}
23784		}
23785	});
23786}
23787
23788function initializeRefinementFilterByText() {
23789	$(document).off('keyup.RefinementFilterText').on('keyup.RefinementFilterText', '.refinement .refinement-filter-text input', function(event) {
23790		var $this = $(this);
23791		var filterText = $this.val();
23792		var filterTextRegEx;
23793		var $container = $this.closest('.refinement-filter-text');
23794		var $reset = $container.find('a.reset');
23795		var $noResults = $container.find('.no-results');
23796		var $refinement = $this.closest('.refinement');
23797		var $refinementValues = $this.closest('.checks-container').find('[refinement-value]:not([refinement-count="0"])');
23798
23799		if (filterText) {
23800			filterTextRegEx = new RegExp('^' + filterText + '| ' + filterText, 'gi');
23801			$reset.addClass('show');
23802			$refinement.addClass('filter-text');
23803			$refinementValues.each(function(index, element) {
23804				var $refinementValue = $(this);
23805				if ($refinementValue.attr('refinement-value').match(filterTextRegEx)) {
23806					$refinementValue.addClass('filter-text-show');
23807				} else {
23808					$refinementValue.removeClass('filter-text-show').hide();
23809				}
23810			});
23811			if ($refinementValues.filter(':visible').length == 0) {
23812				$noResults.find('span').text(filterText);
23813				$noResults.show().attr('tabindex', '0');
23814			} else {
23815				$noResults.hide().removeAttr('tabindex');
23816			}
23817		} else {
23818			$reset.removeClass('show');
23819			$refinement.removeClass('filter-text');
23820			$refinementValues.removeClass('filter-text-show').css('display', '');
23821			$noResults.hide().removeAttr('tabindex');
23822		}
23823	});
23824
23825	$(document).off('click.RefinementFilterTextReset').on('click.RefinementFilterTextReset', '.refinement .refinement-filter-text a.reset', function(event) {
23826		var $this = $(this);
23827		var $container = $this.closest('.refinement-filter-text');
23828		var $noResults = $container.find('.no-results');
23829		var $refinement = $this.closest('.refinement');
23830		var $refinementValues = $this.closest('.checks-container').find('[refinement-value]');
23831
23832		$this.removeClass('show');
23833		$refinement.removeClass('filter-text');
23834		$refinement.find('.refinement-filter-text input').val('');
23835		$refinementValues.removeClass('filter-text-show').css('display', '');
23836		$noResults.hide().removeAttr('tabindex');
23837	});
23838}
23839
23840function saveExpandedRefinements() {
23841	refinementsExpanded = $('.refinements [refinement-id].expanded').map(function(index, element) {
23842		return $(this).attr('refinement-id');
23843	}).get();
23844
23845	refinementsViewAll = $('.refinements [refinement-id].more-expanded').map(function(index, element) {
23846		return $(this).attr('refinement-id');
23847	}).get();
23848}
23849
23850function redrawShopMyStore() {
23851	$.ajax({
23852		method: 'GET',
23853		url: util.appendParamToURL(Urls.Stores.GetShopMyStorePanel + location.search, 'cgid', $('.currCGID').val()),
23854		dataType: 'html',
23855		success: function(data) {
23856			$('.shop-my-store').replaceWith(data);
23857			updateShopMyStore();
23858		}
23859	});
23860}
23861
23862function updateShopMyStore() {
23863	if ($('.shop-my-store-toggle input:checked').length > 0) {
23864		$('body').attr('is-shop-my-store-active', true);
23865	} else {
23866		$('body').attr('is-shop-my-store-active', false);
23867	}
23868}
23869
23870function initializeRangefinderEvents() {
23871	$(document).off('click.RefinementFilterTextReset').on('click.RefinementFilterTextReset', '.product-tile a', function(event) {
23872		event.preventDefault();
23873
23874		var $this = $(this);
23875		var $product = $this.closest('[data-itemid]');
23876		if ($product.length > 0) {
23877			$('body').loading(loadingIndicatorSettings);
23878			dialog.open({
23879                url: util.appendParamToURL(Urls.productDetail, 'pid', $product.attr('data-itemid')),
23880				callback: function() {
23881					$('body').loading('destroy');
23882				}
23883            });
23884		}
23885	});
23886}
23887
23888function initializeBootFinderEvents() {
23889	var $bootFinderResultsBanner = $('.bootfinder-resultsbanner');
23890	if ($bootFinderResultsBanner && $bootFinderResultsBanner.length) {
23891		$bootFinderEmailForm = $bootFinderResultsBanner.find('form.email');
23892		bootFinderEmailValidator = $bootFinderEmailForm.validate(ValidationSettings.Global);
23893		bootFinderEmailValidator.settings.onfocusout = ValidationSettings.Global.onfocusout;
23894		bootFinderEmailValidator.settings.errorPlacement = ValidationSettings.Global.errorPlacement;
23895		$bootFinderEmailForm.find('input[name="email"]').rules('add', $.extend(ValidationRules.Email, {
23896			required: true,
23897			messages: {
23898				required: $.validator.format('Please enter an email address.')
23899			}
23900		}));
23901		$bootFinderEmailForm.find('input[name="useragreement"]').rules('add', {
23902			required: true,
23903			messages: {
23904				required: $.validator.format('Please agree to the terms and conditions.')
23905			}
23906		});
23907
23908		$(document).off('click.BootFinderResultsEmail').on('click.BootFinderResultsEmail', '.bootfinder-resultsbanner .btn.send-results-email', function(event) {
23909			event.preventDefault();
23910
23911			if ($bootFinderEmailForm && $bootFinderEmailForm.valid()) {
23912				var $this = $(this);
23913				var $container = $this.closest('form');
23914				$container.loading($.extend(loadingIndicatorSettings, {
23915					IsLabelShown: true,
23916					Content: 'Sending Email...'
23917				}));
23918				var urlParameters = util.getQueryStringParams(window.location.href);
23919				var bootFinderSendResultsEmailUrl = Urls.BootFinder.SendResultsEmail;
23920				bootFinderSendResultsEmailUrl = util.appendParamToURL(bootFinderSendResultsEmailUrl, 'cgid', $('.currCGID').val());
23921				for (var urlParameter in urlParameters) {
23922					if (urlParameters[urlParameter]) {
23923						bootFinderSendResultsEmailUrl = util.appendParamToURL(bootFinderSendResultsEmailUrl, urlParameter, urlParameters[urlParameter]);
23924					}
23925				}
23926				var payload = {
23927					email: $container.find('input[type="email"]').val(),
23928					url: util.removeParamFromURL(window.location.href, 'bootfinderviewresults')
23929				}
23930				$.ajax({
23931					type: 'post',
23932					url: bootFinderSendResultsEmailUrl,
23933					data: payload,
23934					success: function(data) {
23935						$container.loading('destroy');
23936						if (data && data.success) {
23937							$container.find('.validation-error-container').html('<span class="error">Email sent!</span>');
23938						} else {
23939							$container.find('.validation-error-container').html('<span class="error">An error occurred sending the email. Please try again.</span>');
23940						}
23941					}
23942				});
23943			}
23944		});
23945	}
23946}
23947
23948function initializeEndlessScroll() {
23949	var productTileCount = $('#search-result-items').find('.grid-tile').length;
23950	var productTotalCount = parseInt($('.search-result-options.bottom .count').text().replace(/\D/g, ''), 10);
23951	var $endlessScrollWaypointCheck = $('.endless-scroll-waypoint');
23952	if (productTileCount < productTotalCount && $endlessScrollWaypointCheck && $endlessScrollWaypointCheck.length > 0) {
23953		$(document).off('scroll.EndlessScroll').on('scroll.EndlessScroll', function(event) {
23954			var windowHeight = $(window).height();
23955			var documentScrollTop = $(document).scrollTop();
23956			var windowBottom = documentScrollTop + windowHeight;
23957			var $endlessScrollWaypoint = $('.endless-scroll-waypoint');
23958			var endlessScrollWaypointPosition = $endlessScrollWaypoint.offset().top;
23959			
23960			if (windowBottom > endlessScrollWaypointPosition) {
23961				if (!$endlessScrollWaypoint.attr('loading')) {
23962					$endlessScrollWaypoint.loading();
23963					loadMore(function() {
23964						$endlessScrollWaypoint.loading('destroy');
23965					});
23966				}
23967			}
23968		});
23969		dataBuffer['EndlessScrollInitialized'] = true;
23970	}
23971}
23972
23973function getDecodedNumber(stringNumber) {
23974	var decodedNumber = decodeURIComponent(stringNumber);
23975	var cleanedNumber = parseInt(decodedNumber.replace(/,/g, ''));
23976	return cleanedNumber;
23977}
23978
23979function priceSlider() {
23980	if (!SitePreferences.PRICE_SLIDER_ENABLED) {
23981		return;
23982	} else {
23983		$(document).ready(function() {	
23984			var maxProductPrice = 1200;	// Set default value of slider max to $1200. This gets overwritten later in code depending on returned results.
23985			var urlQueryString = util.getQueryString(window.location.href);
23986			var urlParams = util.getQueryStringParams(urlQueryString); // Get url parameters in object format
23987			var hasCategory = ['cgid'] in urlParams;
23988			var hasQuery = ['q'] in urlParams;
23989			var hiddenFieldCategoryId = $('.currCGID').val();
23990			var sliderMinValue = 0;
23991			var	sliderMaxValue = maxProductPrice;
23992			var psmi = urlParams.psmi;
23993			var convertedPrices;
23994
23995			if (urlParams.pmin) {
23996				urlParams.pmin = getDecodedNumber(urlParams.pmin); // Remove commas from price minimum and transform string into integer
23997				if (urlParams.pmin < 0) {
23998					urlParams.pmin = 0;
23999				}
24000			}
24001			if (urlParams.pmax) {
24002				urlParams.pmax = getDecodedNumber(urlParams.pmax); // Remove commas from price maximum and transform string into integer
24003				if (urlParams.pmax < urlParams.pmin) {
24004					urlParams.pmax = urlParams.pmin;
24005				}
24006				// Needed because server-side will only return products less than priceMax, not equal to. Add 1 to get products equal to.
24007				if (!psmi || psmi != 'true') {
24008					urlParams.pmax = urlParams.pmax + 1; 
24009				}
24010			}
24011			if (!hasQuery && !hasCategory && hiddenFieldCategoryId) {
24012				// If url params don't have a cgid or a query, try grabbing cgid from hidden field on page to use for productSearchModel 
24013				urlParams.cgid = hiddenFieldCategoryId;
24014			}
24015
24016			// Use server-side script to get highest priced product, and to get GLOBALE prices if applicable
24017			var modifiedUrl = util.appendParamsToUrl(Urls.Search.GetPriceSliderPrices, urlParams);
24018			$.ajax({
24019				type: 'POST',
24020				url: modifiedUrl,
24021				contentType: 'application/json',
24022				data: JSON.stringify({
24023					sliderMin: sliderMinValue,
24024					sliderMax: sliderMaxValue,
24025					priceSliderSortingRule: 'price-high-to-low'
24026				}),
24027				success: function(response) {
24028					if (!response || response.status == 'ERROR') {
24029						// Error handling
24030					} else {
24031						try {
24032							maxProductPrice = response.convertedSliderPmax.convertedValue;
24033							convertedPrices = response;
24034						} catch(e) {
24035
24036						}
24037					}
24038					initializePriceSlider(maxProductPrice, convertedPrices, psmi);
24039				}
24040			});		
24041		});
24042	}
24043}
24044
24045function initializePriceSlider(maxProductPrice, convertedPrices, psmi) {
24046	var $priceSlider = $('#price-slider');
24047	var $priceSliderContainer = $priceSlider.closest('.price-slider-parent');
24048	var isMobile = $('html').hasClass('size-mobile');
24049	var sliderBottomValue = 0;
24050	var sliderTopValue = maxProductPrice ? (Math.ceil(maxProductPrice / 5) * 5) : 1200;
24051	var userPmin = 0;
24052	var userPmax = sliderTopValue;
24053	var convertedSymbol, convertedSliderMin, convertedSliderMax, convertedUserMin, convertedUserMax, convertedSliderRange, convertedPriceRange;
24054
24055	// Null checks
24056	if (!convertedPrices) {
24057		convertedSymbol = '$';
24058		convertedSliderMin = sliderBottomValue;
24059		convertedSliderMax = sliderTopValue;
24060		convertedUserMin = userPmin;
24061		convertedUserMax = userPmax;
24062	} else {
24063		convertedSymbol = convertedPrices.convertedSliderPmax.convertedSymbol;
24064		convertedSliderMin = convertedPrices.convertedSliderPmin.convertedValue;
24065		convertedSliderMax = convertedPrices.convertedSliderPmax.convertedValue;
24066		convertedUserMin = convertedPrices.convertedUserPmin.convertedValue;
24067		convertedUserMax = convertedPrices.convertedUserPmax.convertedValue;
24068	}
24069
24070	// If slider instance is already initialized, go ahead and destroy before creating a new initialization
24071	if ($priceSlider.slider('instance')) {
24072		$('#price-slider').slider('destroy');
24073	}
24074	
24075	// Checks rounded pricing against converted pricing
24076	if (sliderTopValue != convertedSliderMax) {
24077		// Do this check before the rounded up value is applied to convertedSliderMax
24078		if (convertedUserMax == convertedSliderMax) {
24079			convertedUserMax = sliderTopValue;
24080		}
24081		// Now update convertedSliderMax to rounded value
24082		convertedSliderMax = sliderTopValue;
24083	}
24084	// If we have a pmin or pmax applied, then ensure that the price refinment bucket is expanded
24085	if ((convertedUserMin != convertedSliderMin) || (convertedUserMax != convertedSliderMax)) {
24086		if (!isMobile && !$('.price-refinement').hasClass('expanded')) {
24087			$('.price-refinement').addClass('expanded');
24088		}
24089		// If we somehow have a greater userPmax than highestPricedProduct in category, go ahead and revert userPmax to highestPricedProduct value
24090		if (convertedUserMax > convertedSliderMax) {
24091			convertedUserMax = convertedSliderMax;
24092		}
24093	}
24094	// Update the DOM attributes with conversion factor to use when price slider is modified
24095	var conversionFactor = 1;
24096	if (convertedPrices) {
24097		conversionFactor = convertedPrices.convertedSliderPmax.defaultValue / convertedPrices.convertedSliderPmax.convertedValue;
24098	}
24099	$priceSliderContainer.attr('data-currency-conversion-factor', conversionFactor.toString());
24100
24101	
24102	// Initialize price slider
24103	$('#price-slider').slider({
24104		range: true,
24105		min: convertedSliderMin,
24106		max: convertedSliderMax,
24107		values: [ convertedUserMin, convertedUserMax ],
24108		slide: function(event, ui) {
24109			// On slide event, grab user input for pmin/pmax and apply to slider text, plus input fields
24110			convertedUserMin = ui.values[0];
24111			convertedUserMax = ui.values[1];
24112			convertedPriceRange = convertedSymbol + convertedUserMin + ' - ' + convertedSymbol + convertedUserMax;
24113			convertedSliderRange = convertedSymbol + convertedSliderMin + ' - ' + convertedSymbol + convertedSliderMax;
24114			$('.price-range-amount').text(convertedPriceRange).attr('data-currency-symbol', convertedSymbol);
24115			$('#price-slider > .ui-slider-range').attr('title', 'Applied Price Range: ' + convertedPriceRange);
24116			$('#price-slider').attr('title', 'Max Range: ' + convertedSliderRange);
24117			$('[data-name="price-input-min"] .price-input-currency-symbol').html(convertedSymbol);
24118			$('[data-name="price-input-max"] .price-input-currency-symbol').html(convertedSymbol);
24119			$('#price-input-min').val(convertedUserMin);
24120			$('#price-input-max').val(convertedUserMax);
24121		},
24122		stop: function(event, ui) {
24123			setTimeout(() => {
24124				convertedUserMin = ui.values[0];
24125				convertedUserMax = ui.values[1];
24126				if (!isMobile) { // Desktop
24127					triggerPriceSlider(convertedUserMin, convertedUserMax);
24128				} else { //Mobile
24129					priceSliderInputs(convertedUserMin, convertedUserMax, isMobile);
24130				}
24131			}, 750); // 0.75 seconds
24132		}
24133	});	
24134
24135	// For slider initialization, grab slider min and max and apply values to slider text and inputs, so everything matches at time of first load
24136	convertedUserMin = $('#price-slider').slider('values', 0);
24137	convertedUserMax = $('#price-slider').slider('values', 1);
24138	convertedPriceRange = convertedSymbol + convertedUserMin + ' - ' + convertedSymbol + convertedUserMax;
24139	convertedSliderRange = convertedSymbol + convertedSliderMin + ' - ' + convertedSymbol + convertedSliderMax;
24140	$('.price-range-amount').text(convertedPriceRange).attr('data-currency-symbol', convertedSymbol);
24141	$('#price-slider > .ui-slider-range').attr('title', 'Applied Price Range: ' + convertedPriceRange);
24142	$('#price-slider').attr('title', 'Max Range: ' + convertedSliderRange);
24143	$('[data-name="price-input-min"] .price-input-currency-symbol').html(convertedSymbol);
24144	$('[data-name="price-input-max"] .price-input-currency-symbol').html(convertedSymbol);
24145	$('#price-input-min').val(convertedUserMin);
24146	$('#price-input-max').val(convertedUserMax);
24147
24148	// Price inputs
24149	$(document).off('keypress', '.price-input-container input').on('keypress', '.price-input-container input', function(e) {
24150		$('.price-inputs-apply-button button').css('display','block');
24151	}).off('click', '.price-inputs-apply-button button').on('click', '.price-inputs-apply-button button', function(e) {
24152		priceSliderInputs(convertedSliderMin, convertedSliderMax, isMobile);
24153	})
24154}
24155
24156function priceSliderGoogleAnalyticsTagging() {
24157	if (SitePreferences.GA_NEW_ACTIONS_ENABLED) {
24158		var refinementId = $('#price-slider').closest('[refinement-id]').attr('refinement-id') || null;
24159		var refinementValue = $('#price-slider').closest('.price-slider-parent').find('.price-range-header .price-range-amount').text() || null;
24160		if (refinementId && refinementValue) {
24161			var refinementAnalyticsTaggingData =  { 
24162				'action_name': 'filter',
24163				'action_page_type': 'plp',
24164				'action_value': refinementId, 
24165				'action_items': refinementValue
24166			};
24167			GoogleTagManagerClient.PushDataLayerForDataLayerType('Event', refinementAnalyticsTaggingData);
24168		}
24169	}
24170}
24171
24172function initMobilePriceSlider(url) {
24173	var isPriceSlider = SitePreferences.PRICE_SLIDER_ENABLED && $('#price-slider').slider('instance');
24174	var isMobile = $('html').hasClass('size-mobile');
24175	var $applyRefinementsBtn = $('.apply-refinements:not(.disabled)');
24176	var isButtonClick = $applyRefinementsBtn.length > 0 && $applyRefinementsBtn[0].hasAttribute('data-apply-refinements') && $applyRefinementsBtn.attr('data-apply-refinements') == 'true';
24177	
24178	if (isPriceSlider && isMobile && isButtonClick) {
24179		var isPriceSliderChanged = ($('#price-slider').slider('option', 'min') != $('#price-slider').slider('values', 0) || $('#price-slider').slider('option', 'max') != $('#price-slider').slider('values', 1)) || false;
24180		if (isPriceSliderChanged) {
24181			var userPriceMin = $('#price-slider').slider('values', 0);
24182			var userPriceMax = $('#price-slider').slider('values', 1);
24183			$applyRefinementsBtn.attr('data-apply-refinements','false');
24184			return triggerPriceSlider(userPriceMin, userPriceMax, url);
24185		} else {
24186			return url;
24187		}
24188	} else {
24189		return url;
24190	}
24191}
24192
24193function priceSliderInputs(sliderBottomValue, sliderTopValue, isMobile) {
24194	// Input fields
24195	var inputPmin = $('#price-input-min').val() ? parseInt($('#price-input-min').val()) : sliderBottomValue;
24196	var inputPmax = $('#price-input-max').val() ? parseInt($('#price-input-max').val()) : sliderTopValue;
24197	if (inputPmin < 0) {
24198		inputPmin = 0;
24199		$('.price-input-min').val(inputPmin);
24200	}
24201	if (inputPmax > sliderTopValue) {
24202		inputPmax = sliderTopValue;
24203		$('#price-input-max').val(inputPmax);
24204	}
24205	// Update the price slider values based on the input fields
24206	var $priceSlider = $('#price-slider').slider('instance');
24207	if ($priceSlider) {
24208		$('#price-slider').slider({
24209			values: [ inputPmin, inputPmax ]
24210		});
24211	}
24212	// Update the price slider text range with correct currency symbol
24213	var convertedSymbol = $('.price-range-amount').attr('data-currency-symbol');
24214	var convertedPriceRange = convertedSymbol + inputPmin + ' - ' + convertedSymbol + inputPmax;
24215	$('.price-range-amount').text(convertedPriceRange);
24216
24217	if (!isMobile || pageContext.site == SiteConstants.SiteIds.BootBarnRspUS) {
24218		// Desktop	
24219		triggerPriceSlider(inputPmin, inputPmax);
24220	} else {
24221		// Mobile
24222		if ($priceSlider) {
24223			var isPriceSliderChanged = ($('#price-slider').slider('option', 'min') != $('#price-slider').slider('values', 0) || $('#price-slider').slider('option', 'max') != $('#price-slider').slider('values', 1)) || false;
24224			if (isPriceSliderChanged) {
24225				$('.refinements').addClass('refinements-modified');
24226				$('.refinements-inner-container-footer .apply-refinements').removeClass('disabled');
24227			} else {
24228				$('.refinements').removeClass('refinements-modified');
24229				$('.refinements-inner-container-footer .apply-refinements').addClass('disabled');
24230			}
24231		}
24232	}
24233}
24234
24235function toggleRefinements() {
24236	if (!$('.refinements-inner-container').hasClass('open')) {
24237		$('.refinements-inner-container').find('.toggle.expanded, .toggle.selected, .refinement.expanded').removeClass('expanded selected');
24238		$('.refinements').removeClass('refinement-expanded');
24239		$('.refinement-body').hide();
24240	}
24241	
24242	toggleBodyScrollLock();
24243	$('.refinements-inner-container').toggleClass('open');
24244	redrawUpdatedRefinementHandling();
24245}
24246
24247// Expects numbers as parameters, not strings
24248function triggerPriceSlider(priceMin, priceMax, passedUrl) {
24249	try {
24250		var url = passedUrl;
24251		if (!passedUrl || pageContext.site == SiteConstants.SiteIds.BootBarnRspUS) {
24252			$('body').loading(loadingIndicatorSettings);
24253			url = window.location.href;
24254		}
24255
24256		// Take user local currency and revert it back to U.S. (merchant site currency) before running the search query
24257		var currencyConversionFactor = parseFloat($('#price-slider').closest('.price-slider-parent').attr('data-currency-conversion-factor'));
24258		var revertedPmin = priceMin * currencyConversionFactor;
24259		var revertedPmax = priceMax * currencyConversionFactor; 
24260		revertedPmax = revertedPmax + 1; // Needed because server-side will only return products less than priceMax, not equal to. Add 1 to get products equal to.
24261
24262		url = util.removeParamFromURL(url, 'pmin');
24263		url = util.removeParamFromURL(url, 'pmax');
24264		url = util.removeParamFromURL(url, 'psmi');
24265		url = util.appendParamToURL(url, 'pmin', revertedPmin.toFixed());
24266		url = util.appendParamToURL(url, 'pmax', revertedPmax.toFixed());
24267		url = util.appendParamToURL(url, 'psmi', 'true');
24268
24269		priceSliderGoogleAnalyticsTagging();
24270		
24271		if (!passedUrl || pageContext.site == SiteConstants.SiteIds.BootBarnRspUS) {
24272			updateProductListing(url);
24273		} else {
24274			return url;
24275		}
24276	} catch (e) {
24277		$('body').loading('destroy');
24278	}
24279}
24280
24281exports.init = function (optionsParameter) {
24282	options = optionsParameter || {};
24283
24284	ImageConstants = pageContext.site === SiteConstants.SiteIds.CountryOutfittersUS ? require('../../../../app_storefront_countryoutfitters/cartridge/js/ImageConstants') : require('../../../../bc_sheplersbootbarn_core/cartridge/js/ImageConstants');
24285	
24286    compareWidget.init();
24287    if ('variantInventoryMessagingUrl' in page && page.variantInventoryMessagingUrl) {
24288    	var urlParams = util.getQueryStringParams(window.location.search.substr(1));
24289    	if(urlParams.hasOwnProperty('start') && urlParams.start > 0){
24290    		updateInventoryMessages(window.location.href, showVariantInventoryMessaging);
24291    	} else {
24292            showVariantInventoryMessaging();
24293    	}
24294    }
24295
24296    productTile.init();
24297    sizeRefinements.init();
24298    initializeEvents();
24299	updateShopMyStore();
24300	sliders.initializeCategorySliders('.main-content .category-landing-slider:not(.slick-initialized)');
24301	initializeRefinementFilterByText();
24302	if (pageContext.site == SiteConstants.SiteIds.BootBarnUS) {
24303		initializeBootFinderEvents();
24304	}
24305	loadScrollPosition();
24306	saveScrollPosition();
24307	if (pageContext.site == SiteConstants.SiteIds.CountryOutfittersUS) {
24308		initializeStickyRefinements();
24309		// initializeStickySubheader();
24310	}
24311
24312	loadingIndicatorSettings = {
24313		IsLabelShown: false,
24314		CssClass: 'fullscreen',
24315		BackgroundColor: 'transparent'
24316	};
24317
24318	if (pageContext.site == SiteConstants.SiteIds.Rangefinder) {
24319		initializeRangefinderEvents();
24320	}
24321
24322	if (options) {
24323		if (options.StyleCountCache) {
24324			styleCounts.render(options.StyleCountCache);
24325		}
24326
24327		if (options.RefinementsUIVersion == RefinementUIVersionConstants.Updated) {
24328			updateRefinementCounts();
24329			if (options.StoreUIVersion == '2024Redesign') {
24330				updateSelectedRefinements();
24331				updatePendingRefinements();
24332			}
24333		
24334			var $productSelectionContainer;
24335			if (pageContext.site === SiteConstants.SiteIds.SheplersUS) {
24336				$productSelectionContainer = $('.primary-content > .sort-and-refine > .product-selections');
24337			} else {
24338				$productSelectionContainer = $('.primary-content > .product-selections')
24339			}
24340
24341			if ($productSelectionContainer.find('.list-selections .value.applied').length > 0) {
24342				$productSelectionContainer.show();
24343			} else {
24344				$productSelectionContainer.hide();
24345			}
24346		}
24347
24348		if (options.Pagination && options.Pagination.Mode == 'EndlessScroll') {
24349			initializeEndlessScroll();
24350		}
24351	}
24352};
24353
24354},{"../../../../app_storefront_countryoutfitters/cartridge/js/ImageConstants":106,"../../../../bc_sheplersbootbarn_core/cartridge/js/ImageConstants":116,"../../../../bc_sheplersbootbarn_core/cartridge/js/RefinementConstants":118,"../../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../../bc_sheplersbootbarn_core/cartridge/js/ValidationConstants":122,"../../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"../compare-widget":15,"../components/LoadingIndicator":25,"../dialog":36,"../page":46,"../product-tile":84,"../size-refinements":93,"../sliders":95,"../style-counts":99,"../util":102,"../wishlist":105}],81:[function(require,module,exports){
24355'use strict';
24356
24357var LocationSearch = require('../../../../app_storefront_core/cartridge/js/bopis/components/LocationSearch');
24358
24359exports.init = function (options) {
24360	var loadingIndicatorSettings = {
24361		IsLabelShown: false,
24362		CssClass: 'fullscreen',
24363		BackgroundColor: 'transparent'
24364	};
24365
24366	if ($('.pt_storefront .search-stores').length > 0) {
24367		var locationSearch = new LocationSearch();
24368		$(document).off('click.Storefront.StoreSearch').on('click.Storefront.StoreSearch', '.pt_storefront .search-stores:not(.disabled)', function(event) {
24369			event.preventDefault();
24370
24371			locationSearch.initialize($('.location-search-dialog'), options);
24372			$('.location-search-dialog').trigger('LocationSearch.FormSubmit');
24373		});
24374	}
24375
24376	$('.pt_storefront .section .btn:not(.search-stores):not(.reopen-rep-selections)').off('click.LoadingIndicator').on('click.LoadingIndicator', function() {
24377		$('body').loading(loadingIndicatorSettings);
24378	});
24379};
24380},{"../../../../app_storefront_core/cartridge/js/bopis/components/LocationSearch":10}],82:[function(require,module,exports){
24381'use strict';
24382
24383var _ = require('lodash');
24384var loadingIndicator = require('../components/LoadingIndicator');
24385var GeolocationHelper = require('../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper');
24386var shopYourStoreDialog = require('../bopis/components/ShopYourStoreDialog');
24387var TemplateConstants = require('../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants');
24388var tooltip = require('../tooltip');
24389var util = require('../util');
24390
24391var options;
24392var dataBuffer;
24393var inputSearchTimer;
24394
24395var $container;
24396var $content;
24397var $useMyLocationButton;
24398var $searchButton;
24399var $locationSearchResults;
24400var locationSearchResultContainerTemplate;
24401var locationSearchResultItemTemplate;
24402
24403function initializeEvents() {
24404	$(document).off('click.ShopYourStoreDialog').on('click.ShopYourStoreDialog', '.shop-store-inventory', function(event) {
24405		event.preventDefault();
24406
24407		var $this = $(this);
24408		var $store = $this.closest('[store-id]');
24409		shopYourStoreDialog.initialize({
24410			StoreId: $store.attr('store-id'),
24411			StoreName: $store.attr('store-name')
24412		});
24413		$this.attr('aria-expanded', 'true');
24414		// Last button triggered
24415   		$this.attr('lastTrigger', 'true');
24416	});
24417
24418	$(document).on('dialogclose', '#shop-your-store-dialog-container', function() {
24419		var $lastButton = $('.shop-store-inventory[lastTrigger="true"]');
24420		if ($lastButton) {
24421			// Focus on last button triggered
24422			$lastButton.attr('aria-expanded', 'false').removeAttr('lastTrigger').focus();
24423		}
24424	});
24425
24426	$('.pt_store-locator').off('click.UseMyLocation').on('click.UseMyLocation', '.use-my-location', function (event) {
24427		event.preventDefault();
24428
24429		dataBuffer.limit = window.outerWidth >= 1024 ? 10 : 5;
24430		dataBuffer.offset = 0;
24431		dataBuffer.query = '';
24432		$('.store-filter-container [type="text"]').val('');
24433
24434		$useMyLocationButton.loading();
24435		$locationSearchResults.html('');
24436		$locationSearchResults.loading();
24437		GeolocationHelper.GetPosition().then(function(position) {
24438			dataBuffer.position = position;
24439			getStoreList();
24440		}).catch(function() {
24441			if ($locationSearchResults.attr('loading')) {
24442				$locationSearchResults.loading('destroy');
24443			}
24444		});
24445	});
24446
24447	$('.pt_store-locator').off('submit.Search').on('submit.Search', '.store-filter-container form', function (event) {
24448		event.preventDefault();
24449		search();
24450	});
24451
24452	$('.pt_store-locator').off('click.Search').on('click.Search', '.store-filter-container .btn.btn-primary', function (event) {
24453		event.preventDefault();
24454		search();
24455	});
24456
24457	$('.pt_store-locator').off('click.LoadMore').on('click.LoadMore', '.location-search-results .load-more', function (event) {
24458		event.preventDefault();
24459
24460		var $this = $(this);
24461		dataBuffer.limit = window.outerWidth >= 1024 ? 10 : 5;
24462		dataBuffer.offset += dataBuffer.limit;
24463		$this.parent().loading();
24464		GeolocationHelper.GetPosition().then(function(position) {
24465			dataBuffer.position = position;
24466			getStoreList();
24467		}).catch(function() {
24468			if ($locationSearchResults.attr('loading')) {
24469				$locationSearchResults.loading('destroy');
24470			}
24471		});
24472	});
24473
24474	// Append "shop my store" url parameters to store ads
24475	setTimeout(function() { 
24476		if ($('.store-locator-detail-form').length > 0) {
24477			var storeID = $('[store-id]').attr('store-id');
24478			if (storeID) {
24479				$('.store-page-bopis-container .content-tiles-slider header').hide();
24480				$('.store-page-bopis-container .content-tiles-slider .swiper-wrapper .swiper-slide, .store-brands.store-page-section .brands-slider .swiper-wrapper .swiper-slide').each(function() {
24481					var $slide = $(this);
24482					var slideUrl = $slide.find('a').attr('href');
24483					if (slideUrl) {
24484						var newUrl = util.appendParamsToUrl(slideUrl, {
24485							'prefn1': 'availableStores',
24486							'prefv1': storeID,
24487							'shopmystore': 'true'
24488						});
24489						$slide.find('a').attr('href', newUrl);
24490					}
24491				});
24492			}
24493		}
24494	}, 1000); // start after 1sec 
24495}
24496
24497function search() {
24498	dataBuffer.limit = window.outerWidth >= 1024 ? 10 : 5;
24499	dataBuffer.offset = 0;
24500	dataBuffer.query = $('.store-filter-container [type="text"]').val();
24501
24502	$searchButton.loading();
24503	$locationSearchResults.html('');
24504	$locationSearchResults.loading();
24505	GeolocationHelper.GetPosition().then(function(position) {
24506		dataBuffer.position = position;
24507		getStoreList();
24508	}).catch(function() {
24509		if ($locationSearchResults.attr('loading')) {
24510			$locationSearchResults.loading('destroy');
24511		}
24512	});
24513}
24514
24515function getStoreList() {
24516	var geolocationPosition;
24517	if (dataBuffer.position && dataBuffer.position.coords) {
24518		geolocationPosition = {
24519			coords: {
24520				accuracy: dataBuffer.position.coords.accuracy,
24521				latitude: dataBuffer.position.coords.latitude,
24522				longitude: dataBuffer.position.coords.longitude
24523			},
24524			timestamp: dataBuffer.position.timestamp
24525		};
24526	}
24527
24528	var payload = {
24529		Source: 'StoreLocatorPage',
24530		GeolocationPosition: geolocationPosition
24531	};
24532	if (dataBuffer.query != undefined) {
24533		payload.Query = dataBuffer.query;
24534	}
24535	if (dataBuffer.limit != undefined) {
24536		payload.Limit = dataBuffer.limit;
24537	}
24538	if (dataBuffer.offset != undefined) {
24539		payload.Offset = dataBuffer.offset;
24540	}
24541
24542	$.ajax({
24543		method: 'POST',
24544		url: Urls.Bopis.GetStoresForGeolocationPosition,
24545		contentType: 'application/json',
24546		data: JSON.stringify(payload),
24547		dataType: 'json',
24548		success: function(data) {
24549			renderStoreList(data);
24550		},
24551		error: function() {
24552			
24553		}
24554	});		
24555}
24556
24557function renderStoreList(data) {
24558	var locationData;
24559	if (data && data.ServiceSuccessfullyCalled && data.InquiryResult && data.InquiryResult.data && data.InquiryResult.data.length > 0) {
24560		locationData = data.InquiryResult.data;
24561	}
24562
24563	$container.attr('state', 'result');
24564	if (locationData && locationData.length > 0) {
24565		locationData = locationData.sort(function(left, right) {
24566			return left.distance - right.distance;
24567		});
24568		
24569		dataBuffer['StoresDisplayed'] = [];
24570		$container.attr('result-count', locationData.length);
24571		
24572		var storeListHtml = '';
24573		$locationSearchResults.find('.no-results').remove();
24574		for (var locationElement in locationData) {
24575			var location = locationData[locationElement];
24576			
24577			if (location && !dataBuffer['StoresDisplayed'][location.code] && location.StoreInfo.StateCodeParsed && location.StoreInfo.PostalCode) {
24578				var storePhone = '';
24579				var storeOpenUntil = '';
24580				if (location.StoreInfo) {
24581					if (location.StoreInfo.Phone) {
24582						storePhone = location.StoreInfo.Phone;
24583					}
24584					if (location.StoreInfo.OpenUntil) {
24585						storeOpenUntil = location.StoreInfo.OpenUntil;
24586					}
24587				}
24588				
24589				var storeInventoryLink = '';
24590				if (location.StoreInfo.StoreInventoryLink) {
24591					storeInventoryLink = '<button class="shop-store-inventory" aria-controls="shop-your-store-dialog-container" aria-haspopup="dialog" aria-expanded="false">' + Resources.Stores.ShopStoreInventory + '</button>';
24592				}
24593
24594				var directionsLink = '';
24595				if (location.StoreInfo.DirectionsUrl) {
24596					directionsLink = '<a class="directions" href="' + location.StoreInfo.DirectionsUrl + '" target="_blank">Directions</a>';
24597				}
24598
24599				storeListHtml += locationSearchResultItemTemplate({
24600					StoreId: location.code,
24601					StoreName: location.StoreInfo.Name,
24602					Distance: location.StoreInfo.Distance.toFixed(1) + ' mi',
24603					Html: '<a href="' + Urls.storeDetails + '?StoreID=' + location.code + '">' + location.StoreInfo.Name + '</a><br/>' +
24604							location.StoreInfo.Address1 + '<br/>' + 
24605							location.StoreInfo.City + ', ' + location.StoreInfo.StateCodeParsed + ' ' + location.StoreInfo.PostalCode.substr(0, 5) + '<br/>' +
24606							storePhone + '<br/>' +
24607							storeOpenUntil,
24608					DirectionsLink: directionsLink,
24609					StoreInventoryLink: storeInventoryLink
24610				});
24611			}
24612			
24613			dataBuffer['StoresDisplayed'][location.code] = true;
24614		}
24615		if (data.InquiryResult.meta && data.InquiryResult.meta.page < data.InquiryResult.meta.pages) {
24616			storeListHtml += '<li class="load-more-container"><a class="load-more">Load More</a></li>';
24617		}
24618		
24619		var resultHtml = locationSearchResultContainerTemplate({
24620			Html: storeListHtml
24621		});
24622		$locationSearchResults.find('.load-more-container').remove();
24623		$locationSearchResults.append(resultHtml);
24624	} else {
24625		$container.attr('result-count', '0');
24626		$locationSearchResults.html('<li class="no-results">No stores found! Please try a different search.</li>');
24627	}
24628
24629	if ($useMyLocationButton.attr('loading')) {
24630		$useMyLocationButton.loading('destroy');
24631	}
24632	if ($searchButton.attr('loading')) {
24633		$searchButton.loading('destroy');
24634	}
24635	if ($locationSearchResults.attr('loading')) {
24636		$locationSearchResults.loading('destroy');
24637	}
24638}
24639
24640exports.init = function (optionsParameter) {
24641	options = optionsParameter || {};
24642	dataBuffer = {
24643		offset: 0
24644	};
24645
24646	$container = $('#stores_main');
24647	$content = $container.find('.content');
24648	$useMyLocationButton = $('.store-filter-container .use-my-location');
24649	$searchButton = $('.store-filter-container .btn.btn-primary');
24650	$locationSearchResults = $('.location-search-results');
24651	locationSearchResultContainerTemplate = _.template($('#template-bopis-result-container').html(), TemplateConstants.Lodash.Settings);
24652	locationSearchResultItemTemplate = _.template($('#template-bopis-result-item-store-locator').html(), TemplateConstants.Lodash.Settings);	
24653
24654	initializeEvents();
24655	tooltip.init({ items: '.tooltip' });
24656};
24657},{"../../../../bc_sheplersbootbarn_core/cartridge/js/Geolocation/GeolocationHelper":115,"../../../../bc_sheplersbootbarn_core/cartridge/js/TemplateConstants":121,"../bopis/components/ShopYourStoreDialog":12,"../components/LoadingIndicator":25,"../tooltip":101,"../util":102,"lodash":133}],83:[function(require,module,exports){
24658'use strict';
24659
24660module.exports = {
24661	
24662	elementPrefix: 'shipping-restrictions-popup',
24663	element: null,
24664	defaultTitle: '',
24665	defaultContent: '',
24666	
24667	init: function (options) {
24668		if (!$( '.' + this.elementPrefix ).length) {
24669			$('body').append('<div class="' + this.elementPrefix + '"><div class="' + this.elementPrefix + '__popup"><h2 class="' + this.elementPrefix + '__h2"></h2><a class="' + this.elementPrefix + '__close" href="#">&times;</a><div class="' + this.elementPrefix + '__content"></div></div></div>');
24670			this.element = $( '.' + this.elementPrefix );
24671			var el = this;
24672			this.element.find('.' + this.elementPrefix + '__close').on('click', function (e) {
24673				el.hidePopup(e);
24674			});
24675		}
24676		this.element.find('.' + this.elementPrefix + '__h2').html(options.title || this.defaultTitle);
24677		this.element.find('.' + this.elementPrefix + '__content').html(options.content || this.defaultContent);
24678		return this;
24679	},
24680	
24681	showPopup: function () {
24682    	this.element.addClass(this.elementPrefix + '__visible');
24683    	return this;
24684    },
24685    
24686    hidePopup: function (e) {
24687    	e.preventDefault();
24688    	this.element.removeClass(this.elementPrefix + '__visible');
24689    },
24690};
24691
24692},{}],84:[function(require,module,exports){
24693'use strict';
24694
24695var imagesLoaded = require('imagesloaded');
24696var util = require('./util');
24697
24698exports.syncHeightOfTiles = syncHeightOfTiles;
24699
24700var windowWidth;
24701
24702function gridViewToggle() {
24703    $('.toggle-grid').on('click', function () {
24704        $('.search-result-content').toggleClass('wide-tiles');
24705        $(this).toggleClass('wide');
24706    });
24707}
24708
24709/**
24710 * @private
24711 * @function
24712 * @description line clamping for product tile product titles.
24713 */
24714
24715 function clampProductTileTitles() {
24716    $('h6.product-name .name-link').each(function(index, clampTitle) {
24717        $clamp(clampTitle, {
24718            clamp: 2,
24719            useNativeCLamp: false
24720        });
24721    });
24722 }
24723 
24724 function initializeSwatches() {
24725	 $('.grid-tile').each(function(index, element) {
24726		$(element).find('.yes-alts ul.swatch-list li').each(function(i, elem) {
24727			var altDisplay = 'show-content';
24728			if ( i >= 4 ){
24729				altDisplay = 'hidden-content';
24730			}
24731			$(elem).addClass(altDisplay);
24732		});
24733		var altHiddenCount = $(element).find('.hidden-content').length;
24734		var altHiddenToggle = '+ ' + altHiddenCount;
24735		var altButtonExists = $(element).find('.altButton').length;
24736		if (altHiddenCount > 0){
24737			$(element).find('.hidden-content').hide();
24738			if (altButtonExists == 0){
24739				$(element).find('.yes-alts ul.swatch-list').append('<button class="altButton">' + altHiddenToggle + '</button>');
24740			} else {
24741				$(element).find('.altButton').text(altHiddenToggle);
24742			}
24743		}
24744	});
24745 }
24746
24747/**
24748 * @private
24749 * @function
24750 * @description Initializes events on the product-tile for the following elements:
24751 * - swatches
24752 * - thumbnails
24753 */
24754function initializeEvents() {
24755    gridViewToggle();
24756    initializeSwatches();
24757    
24758    $(document).ready(function() {
24759    	$('#search-result-items .grid-tile').each(function() {
24760    		var $gridItem = $(this),
24761    			$primaryColor = $gridItem.data('primary-color'),
24762    			$thumb = $gridItem.find('.product-image .thumb-link img').eq(0),
24763    			$thumblink = $gridItem.find('.product-image a.thumb-link').eq(0),
24764    			$namelink = $gridItem.find('.product-content .product-name a.name-link').eq(0);
24765    		$gridItem.find('.product-content .product-swatches .swatch-list li').each(function() {
24766    			var $currswatch = $(this),
24767    				$currhref = $currswatch.find('a.swatch').attr('href'),
24768    				$currcolor = $currswatch.data('color');
24769    			if ($primaryColor == $currcolor) {
24770    				var $data = $currswatch.find('.swatch-image').data('thumb');
24771    				
24772    				if ($thumblink.attr('selected-locations')) {
24773    					$currhref = util.appendParamToURL($currhref, 'selectedLocations', $thumblink.attr('selected-locations'));
24774    				}
24775    				
24776    				$namelink.attr('href', $currhref);
24777    				$thumblink.attr('href', $currhref);
24778    				
24779					if ($data) {
24780						$thumb.attr({
24781							src: $data.src,
24782							alt: $data.alt,
24783							title: $data.title
24784						});
24785					}
24786    			}
24787    		})
24788    	});
24789
24790		$(window).off('load.ProductTile resize.ProductTile').on('load.ProductTile resize.ProductTile', function () {
24791            if (windowWidth != $(window).width()) {
24792			    syncHeightOfTiles();
24793            }
24794		});
24795
24796		imagesLoaded($('#search-result-items .grid-tile')).on('done', function () {
24797			syncHeightOfTiles();
24798        });
24799    });
24800    
24801	$('.swatch-list .swatch').on('click', function (event) {
24802		event.preventDefault();
24803
24804        // get current thumb details
24805        var $tile = $(this).closest('.product-tile'),
24806            $thumb = $tile.find('.product-image .thumb-link img').eq(0),
24807            data = $(this).children('.swatch-image').filter(':first').data('thumb'),
24808            current = $thumb.data('current');
24809
24810        // If this is the first time, then record the current img
24811        if (!current) {
24812            $thumb.data('current', {
24813                src: $thumb[0].src,
24814                alt: $thumb[0].alt,
24815                title: $thumb[0].title
24816            });
24817        }
24818
24819        // Set the tile image to the values provided on the swatch data attributes
24820		if (data) {
24821			$thumb.attr({
24822				src: data.src,
24823				alt: data.alt,
24824				title: data.title
24825			});
24826		}
24827    });
24828
24829    clampProductTileTitles();
24830}
24831
24832function syncHeightOfTiles() {
24833	var $tiles = $('.tiles-container .product-tile');
24834    if ($tiles.length === 0) { return; }
24835
24836	$tiles.find('.product-image .thumb-link').syncHeightToAverage();
24837	$tiles.each(function (idx) {
24838		$(this).data('idx', idx);
24839	});
24840}
24841
24842exports.init = function () {
24843    windowWidth = $(window).width();
24844
24845    initializeEvents();
24846};
24847
24848},{"./util":102,"imagesloaded":132}],85:[function(require,module,exports){
24849'use strict';
24850
24851module.exports = {
24852	
24853	init: function (options) {
24854		if ($('.rotate-3d__img').length) {
24855			$('.rotate-3d__container').prepend('<a href="#" class="rotate-3d__rotate rotate-3d__rotateLeft">&nbsp;</a><a href="#" class="rotate-3d__rotate rotate-3d__rotateRight">&nbsp;</a>');
24856			$('.rotate-3d__rotateLeft').on('click', function (e) {
24857				e.preventDefault();
24858				$('.rotate-3d__img').trigger("stepLeft");
24859			});
24860			$('.rotate-3d__rotateRight').on('click', function (e) {
24861				e.preventDefault();
24862				$('.rotate-3d__img').trigger("stepRight");
24863			});
24864		}
24865		return this;
24866	},
24867
24868};
24869
24870},{}],86:[function(require,module,exports){
24871'use strict';
24872
24873var $loader;
24874
24875/**
24876 * @function
24877 * @description Shows an AJAX-loader on top of a given container
24878 * @param {Element} container The Element on top of which the AJAX-Loader will be shown
24879 */
24880var show = function (container, displayInLine) {
24881    var target = (!container || $(container).length === 0) ? $('body') : $(container);
24882    $loader = $loader || $('.loader');
24883
24884    if ($loader.length === 0) {
24885        $loader = $('<div/>').addClass('loader')
24886            .append($('<div/>').addClass('loader-indicator'), $('<div/>').addClass('loader-bg'));
24887    }
24888
24889    if (displayInLine) {
24890        $loader.css('display', 'inline-block');
24891    } else {
24892        $loader.css('display', 'block');
24893    }
24894    return $loader.appendTo(target);
24895};
24896/**
24897 * @function
24898 * @description Hides an AJAX-loader
24899 */
24900var hide = function () {
24901    if ($loader) {
24902        $loader.hide();
24903    }
24904};
24905
24906exports.show = show;
24907exports.hide = hide;
24908
24909},{}],87:[function(require,module,exports){
24910'use strict';
24911
24912var page = require('./page');
24913var DataLayerConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/DataLayerConstants');
24914var GoogleTagManagerClient = require('../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
24915var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
24916
24917var isCheckout, isEchoCart, fnUpdateCart, fnRenderCart, $focusedElement;
24918var loadingIndicatorSettings = {
24919    IsLabelShown: false,
24920    CssClass: 'fullscreen',
24921    BackgroundColor: 'transparent'
24922};
24923
24924function updateStepperQtyUI($this, increment) {
24925	var $quantityStepper = increment != undefined ? $this.parent('.quantity-stepper') : $this;
24926	var $input = $quantityStepper.find('input.input-text');
24927	var quantity = parseInt($input.val());
24928	// Only increment or decrement quantity if a boolean was passed into the function
24929	if (increment != undefined) {
24930		increment ? quantity++ : quantity--;
24931	}
24932	
24933	// Enable or Disable the plus/minus signs on the quantity stepper
24934	var $incButton = $quantityStepper.find('button.increase.plus');
24935	var $decButton = $quantityStepper.find('button.decrease.minus');
24936
24937	if (quantity <= 1) {
24938		$decButton.addClass('disabled').attr('disabled', 'disabled');
24939		$incButton.removeClass('disabled').removeAttr('disabled');
24940	} else if (quantity > 1) {
24941		$decButton.removeClass('disabled').removeAttr('disabled');
24942		$incButton.removeClass('disabled').removeAttr('disabled');
24943	}
24944
24945	// Update the value of the quantity stepper
24946	$input.attr('quantity-buffer', quantity);
24947	$input.val(quantity);
24948}
24949
24950function updateMiniCartUI(increment, $lineItem) {
24951	var $miniCartElm = $('.mini-cart .minicart-quantity').length > 0 ? $('.mini-cart .minicart-quantity') : $('.mini-cart .btn-minicart .btn-minicart-qty');
24952	var miniCartTotal;
24953	if ($miniCartElm.length > 0) {
24954		miniCartTotal = parseInt($miniCartElm.html().trim());
24955	}
24956
24957	if ((increment != null && typeof increment === "boolean") && !$lineItem) {
24958		increment ? miniCartTotal++ : miniCartTotal--;
24959	} else if ((increment == null || typeof increment != "boolean") && $lineItem) {
24960		var itemQty = parseInt($lineItem.find('.opc-qty').val());
24961		miniCartTotal = itemQty && typeof itemQty == 'number' && itemQty > 0 ? miniCartTotal - itemQty : miniCartTotal;
24962		$lineItem.remove();
24963	}
24964	
24965	// Update the basket quantity on the main navigation bar > minicart icon
24966	miniCartTotal >= 1 ? $miniCartElm.html(miniCartTotal) : $miniCartElm.html(0);
24967
24968	// Update the minicart modal, if it's currently open
24969	if ($('.add-to-cart-modal').length > 0) {
24970		var productRecommendationsHtml = $('.add-to-cart-modal  .main-content .product-recommendations-container').html();
24971		$.ajax({url : Urls.addToCartModalDisplay, dataType: 'html', success : function(modalContent) {
24972			if (modalContent.length > 0) {
24973				$('.add-to-cart-modal .ui-dialog-content').html(modalContent);
24974				$('.add-to-cart-modal-content .main-content .product-recommendations-container').append(productRecommendationsHtml);
24975				// Reference addToCart.js > addItemToCart() > line 314 (Move title from main content to top of the modal)
24976				if ($('.add-to-cart-modal-content').length > 0) {
24977					var $addMessage = $('.add-to-cart-modal-content .main-content .added-message');
24978					// Reference addToCart.js > addItemToCart() > line 377 (Inserts the word 'Item' into the minicart title)
24979					$('.add-to-cart-modal-content .added-message .count').text('Item');
24980					if ($addMessage.length > 0) {
24981						$('.add-to-cart-modal-content').prepend($addMessage);
24982					}
24983				}
24984			}
24985		}});
24986	}
24987}
24988
24989function updateCartTotals() {
24990	if (isCheckout && fnUpdateCart) {
24991		fnUpdateCart($('.update-cart-button').attr('name'));
24992	}
24993	if (isEchoCart && fnRenderCart) {
24994		fnRenderCart();
24995	}
24996}
24997
24998function removeFromCartDataLayer($this) {
24999	try {
25000		var $item = $this.closest('[data-uuid]');
25001		if ($item) {
25002			var productID = $item.attr('data-productid');
25003			var $element, uaObject, ga4Object, productDataForUniversalAnalytics, productDataForGoogleAnalytics4;
25004			$('.add-to-cart-analytics-container').each(function(idx, elm) {
25005				$element = $(elm);
25006				if ($element) {
25007					uaObject = JSON.parse($element.attr('data-product-gtm-json'));
25008					ga4Object = JSON.parse($element.attr('data-product-ga4-json'));
25009					
25010					if (uaObject && uaObject.skuid && uaObject.skuid == productID) {
25011						productDataForUniversalAnalytics = uaObject;
25012						productDataForGoogleAnalytics4 = ga4Object;
25013					}
25014				}
25015			});
25016
25017			if (productDataForUniversalAnalytics && productDataForGoogleAnalytics4) {
25018				var dataObject = {
25019					UniversalAnalytics: {
25020						ecommerce: {
25021							remove: {
25022								products: [productDataForUniversalAnalytics]
25023							}
25024						}
25025					},							
25026					GoogleAnalytics4: {
25027						value: productDataForGoogleAnalytics4.price,
25028						items: [productDataForGoogleAnalytics4]
25029					}
25030				}
25031
25032				GoogleTagManagerClient.PushDataLayerForDataLayerType('RemoveFromCart', dataObject);
25033			}
25034		}
25035	} catch(e) {
25036	}
25037}
25038
25039function quantityDatalayerEvents($this, eventType, quantity) {
25040	try {
25041		var $item = $this.closest('[data-uuid]');
25042		if ($item) {
25043			var eventName;
25044			var ecommerceKey;
25045			if (eventType == DataLayerConstants.EventTypes.Minus) {
25046				eventName = DataLayerConstants.Events.Cart.RemoveFromCart.Name;
25047				ecommerceKey = 'remove';
25048			} else {
25049				eventName = DataLayerConstants.Events.Cart.AddToCart.Name;
25050				ecommerceKey = 'add';
25051			}
25052			var productID = $item.attr('data-productid');
25053			var $element, uaObject, ga4Object, productDataForUniversalAnalytics, productDataForGoogleAnalytics4;
25054			$('.add-to-cart-analytics-container').each(function(idx, elm) {
25055				$element = $(elm);
25056				if ($element) {
25057					uaObject = JSON.parse($element.attr('data-product-gtm-json'));
25058					ga4Object = JSON.parse($element.attr('data-product-ga4-json'));
25059					
25060					if (uaObject && uaObject.skuid && uaObject.skuid == productID) {
25061						productDataForUniversalAnalytics = uaObject;
25062						productDataForGoogleAnalytics4 = ga4Object;
25063					}
25064				}
25065			});
25066			
25067			if (productDataForUniversalAnalytics && productDataForGoogleAnalytics4) {
25068				if (quantity) {
25069					productDataForUniversalAnalytics.quantity = quantity;
25070					productDataForGoogleAnalytics4.quantity = quantity;
25071				}
25072
25073				var dataObject = {
25074					UniversalAnalytics: {
25075						ecommerce: {}
25076					},							
25077					GoogleAnalytics4: {
25078						value: productDataForGoogleAnalytics4.price,
25079						items: [productDataForGoogleAnalytics4]
25080					}
25081				}
25082
25083				dataObject.UniversalAnalytics.ecommerce[ecommerceKey] = {
25084					products: [productDataForUniversalAnalytics]
25085				}
25086
25087				GoogleTagManagerClient.PushDataLayerForDataLayerType(eventName, dataObject);
25088			}
25089		}
25090	} catch(e) {
25091	}
25092}
25093
25094function quantityChangeEvents($this, quantity, increment) {
25095	var eventType = increment ? DataLayerConstants.EventTypes.Plus : DataLayerConstants.EventTypes.Minus;
25096	quantityDatalayerEvents($this, eventType, quantity);
25097}
25098
25099function spinnerClear() {
25100	var spinnerTimer;
25101	var timerIncrement = isCheckout || isEchoCart ? 5000 : 2000;
25102
25103	clearTimeout(spinnerTimer);
25104	spinnerTimer = setTimeout(function() {
25105		if ($('body').attr('loading')) {
25106			$('body').loading('destroy');
25107		}
25108	}, timerIncrement);
25109}
25110
25111function initialize(updateCart, renderCart) {
25112	isCheckout = (pageContext && pageContext.type == 'cart' || pageContext.type == 'onepcheckout_groups') || (page.ns && page.ns == 'cart' || page.ns == 'onepcheckout_groups');
25113	isEchoCart = (pageContext && 'type' in pageContext && pageContext.type == 'cart_echo') || (page.ns && page.ns == 'cart_echo');
25114	fnUpdateCart = updateCart && typeof updateCart === 'function' ? updateCart : null;
25115	fnRenderCart = renderCart && typeof renderCart === 'function' ? renderCart : null;
25116
25117	// Remove Button
25118	$(document).off('click.QuantityStepper.ProductRemove').on('click.QuantityStepper.ProductRemove', '.quantity-stepper-remove .remove', function (event) {
25119		event.preventDefault();
25120
25121		var $this = $(this);
25122		var $itemRow = $this.closest('[data-uuid]');
25123		var isMiniCart = $itemRow.attr('class').indexOf('mini-cart') > -1;
25124
25125		$('body').loading(loadingIndicatorSettings);
25126		$.ajax({
25127			type: 'POST',
25128			url: Urls.Cart.RemoveProduct,
25129			contentType: 'application/json',
25130			data: JSON.stringify({
25131				ProductLineItemUUID: $itemRow.data('uuid'),
25132			}),
25133			dataType: 'json',
25134			complete: function(xhr) {
25135				try {
25136					updateCartTotals();
25137					removeFromCartDataLayer($this);
25138					if (isMiniCart) {
25139						updateMiniCartUI(null, $itemRow);
25140					}
25141					spinnerClear();
25142				} catch(e) {
25143                    console.log(e);
25144					$('body').loading('destroy');
25145                }
25146			}
25147		});
25148	});
25149
25150	// Plus/Minus Qty Buttons
25151	$(document).off('click.QuantityStepper.QuantityChange').on('click.QuantityStepper.QuantityChange', '.quantity-stepper button:not(".disabled")', function (event) {
25152		event.preventDefault();
25153
25154		var $stepperButton = $(this);
25155		var isIncrement = $stepperButton.hasClass('increase');
25156		var cartQtyUrl = isIncrement ? Urls.Cart.AddProductQuantity : Urls.Cart.RemoveProductQuantity;
25157
25158		$focusedElement = $stepperButton;
25159		$('body').loading(loadingIndicatorSettings);
25160		$.ajax({
25161			type: 'POST',
25162			url: cartQtyUrl,
25163			contentType: 'application/json',
25164			data: JSON.stringify({
25165				ProductLineItemUUID: $(this).closest('[data-uuid]').data('uuid'),
25166				Quantity: 1
25167			}),
25168			dataType: 'json',
25169			complete: function(xhr) {
25170				try {
25171                    updateStepperQtyUI($stepperButton, isIncrement);
25172					updateMiniCartUI(isIncrement);
25173					updateCartTotals();
25174					quantityChangeEvents($stepperButton, 1, isIncrement);
25175					spinnerClear();
25176                } catch(e) {
25177                    console.log(e);
25178					$('body').loading('destroy');
25179                }
25180			}
25181		});
25182	});
25183
25184	// Input event event is reliable and fires after any user action that modifies the input field, including: ke
25184yup, paste or change
25185	$(document).off('input.QuantityStepper.QuantityChange').on('input.QuantityStepper.QuantityChange', '.quantity-stepper input[name$=_quantity]', function (event) {
25186		var args = {
25187			element: $(this), 
25188			value: $(this).val(),
25189			event: 'input'
25190		};
25191		$(document).trigger('QuantityStepper.InputChange', args);
25192	});
25193
25194	$(document).off('blur.QuantityStepper.InputBlur').on('blur.QuantityStepper.InputBlur', '.quantity-stepper input[name$=_quantity]', function (event) {
25195		var args = {
25196			element: $(this), 
25197			value: $(this).val(),
25198			event: 'blur'
25199		};
25200		$(document).trigger('QuantityStepper.InputChange', args);
25201	});
25202
25203	// Custom-Named event for the keyboard or paste input for quantity field
25204    $(document).off('QuantityStepper.InputChange').on('QuantityStepper.InputChange', function (event, args) {
25205		var $this = $(args.element);
25206		var triggerEvent = args.event;
25207		var numericValue = parseInt(args.value);
25208		if (isNaN(numericValue) || numericValue <= 0 || numericValue === parseInt($this.attr('quantity-buffer'))) {
25209			if (triggerEvent === 'blur') {
25210				$this.val($this.attr('quantity-buffer'));
25211			} else {
25212				if (args.value != '') {
25213            		$this.val($this.attr('quantity-buffer')); 
25214				}
25215			}
25216		} else {
25217			var quantityDelta = numericValue - parseInt($this.attr('quantity-buffer'));
25218			var isIncrement = quantityDelta < 0 ? false : quantityDelta > 0 ? true : null;
25219			if (isIncrement != null) {
25220				var cartQtyUrl = isIncrement ? Urls.Cart.AddProductQuantity : Urls.Cart.RemoveProductQuantity;
25221
25222				$('body').loading(loadingIndicatorSettings);
25223				$.ajax({
25224					type: 'POST',
25225					url: cartQtyUrl,
25226					contentType: 'application/json',
25227					data: JSON.stringify({
25228						ProductLineItemUUID: $this.closest('[data-uuid]').data('uuid'),
25229						Quantity: Math.abs(quantityDelta)
25230					}),
25231					dataType: 'json',
25232					complete: function(xhr) {
25233						try {
25234							updateStepperQtyUI($this, isIncrement);
25235							updateMiniCartUI(isIncrement);
25236							updateCartTotals();
25237							quantityChangeEvents($this, Math.abs(quantityDelta), isIncrement);
25238							$this.attr('quantity-buffer', numericValue);
25239							spinnerClear();
25240						} catch(e) {
25241							console.log(e);
25242							$('body').loading('destroy');
25243						}
25244					}
25245				});
25246			}
25247			$focusedElement = $this;	
25248		}
25249	});
25250
25251	$(document).off('QuantityStepper.TargetFocus').on('QuantityStepper.TargetFocus', function () {
25252		if ($focusedElement) {
25253			$focusedElement.focus();
25254		}
25255	});
25256}
25257
25258exports.init = initialize;
25259},{"../../../bc_sheplersbootbarn_core/cartridge/js/DataLayerConstants":114,"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"./page":46}],88:[function(require,module,exports){
25260'use strict';
25261
25262var dialog = require('./dialog'),
25263    product = require('./pages/product'),
25264    util = require('./util'),
25265    _ = require('lodash');
25266
25267
25268var makeUrl = function (url, source, productListID) {
25269    if (source) {
25270        url = util.appendParamToURL(url, 'source', source);
25271    }
25272    if (productListID) {
25273        url = util.appendParamToURL(url, 'productlistid', productListID);
25274    }
25275    return url;
25276};
25277
25278var removeParam = function (url) {
25279    if (url.indexOf('?') !== -1) {
25280        return url.substring(0, url.indexOf('?'));
25281    } else {
25282        return url;
25283    }
25284};
25285
25286var quickview = {
25287    init: function () {
25288        if (!this.exists()) {
25289            this.$container = $('<div/>').attr('id', 'QuickViewDialog').appendTo(document.body);
25290        }
25291        this.productLinks = $('#search-result-items .thumb-link').map(function (index, thumbLink) {
25292            return $(thumbLink).attr('href');
25293        });
25294    },
25295
25296    setup: function (qvUrl) {
25297
25298        product.initializeEvents();
25299
25300     },
25301
25302    /**
25303     * @param {Number} step - How many products away from current product to navigate to. Negative number means navigate backward
25304     */
25305    navigateQuickview: function (step) {
25306        // default step to 0
25307        this.productLinkIndex += (step ? step : 0);
25308        var url = makeUrl(this.productLinks[this.productLinkIndex], 'quickview');
25309        dialog.replace({
25310            url: url,
25311            callback: this.setup.bind(this, url)
25312        });
25313    },
25314
25315    /**
25316     * @description show quick view dialog
25317     * @param {Object} options
25318     * @param {String} options.url - url of the product details
25319     * @param {String} options.source - source of the dialog to be appended to URL
25320     * @param {String} options.productlistid - to be appended to URL
25321     * @param {Function} options.callback - callback once the dialog is opened
25322     */
25323    show: function (options) {
25324        var url;
25325        if (!this.exists()) {
25326            this.init();
25327        }
25328        url = makeUrl(options.url, options.source, options.productlistid);
25329
25330        dialog.open({
25331            target: this.$container,
25332            url: url,
25333            options: {
25334                width: 1068,
25335                title: Resources.QUICK_VIEW_POPUP,
25336                open: function () {
25337                    this.setup(url);
25338                    if (typeof options.callback === 'function') { options.callback(); }
25339                }.bind(this)
25340            }
25341        });
25342    },
25343    exists: function () {
25344        return this.$container && (this.$container.length > 0);
25345    }
25346};
25347
25348module.exports = quickview;
25349
25350},{"./dialog":36,"./pages/product":75,"./util":102,"lodash":133}],89:[function(require,module,exports){
25351'use strict';
25352
25353/**
25354 * copied from https://github.com/darkskyapp/string-hash
25355 */
25356function hashFn(str) {
25357    var hash = 5381,
25358        i = str.length;
25359
25360    while (i) {
25361        hash = (hash * 33) ^ str.charCodeAt(--i);
25362    }
25363    /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
25364    * integers. Since we want the results to be always positive, convert the
25365    * signed int to an unsigned by doing an unsigned bitshift. */
25366    return hash >>> 0;
25367}
25368
25369/**
25370 * Create rating based on hash ranging from 2-5
25371 * @param pid
25372 */
25373function getRating(pid) {
25374    return hashFn(pid.toString()) % 30 / 10 + 2;
25375}
25376
25377module.exports = {
25378    init: function () {
25379        $('.variant-info .product-review').each(function (index, review) {
25380            var pid = $(review).data('pid');
25381            if (!pid) {
25382                return;
25383            }
25384            // rating range from 2 - 5
25385            var rating = getRating(pid);
25386            var baseRating = Math.floor(rating);
25387            var starsCount = 0;
25388            for (var i = 0; i < baseRating; i++) {
25389                $('.rating', review).append('<i class="fa fa-star"></i>');
25390                starsCount++;
25391            }
25392            // give half star for anything in between
25393            if (rating > baseRating) {
25394                $('.rating', review).append('<i class="fa fa-star-half-o"></i>');
25395                starsCount++;
25396            }
25397            if (starsCount < 5) {
25398                for (var j = 0; j < 5 - starsCount; j++) {
25399                    $('.rating', review).append('<i class="fa fa-star-o"></i>');
25400                }
25401            }
25402        });
25403    }
25404};
25405
25406},{}],90:[function(require,module,exports){
25407'use strict';
25408
25409/* -------------------------
25410 * REGISTER
25411 * -------------------------
25412 */
25413
25414var alert = require('./alert');
25415var dialog = require('./dialog');
25416var loadingIndicator = require('./components/LoadingIndicator');
25417var wishlist = require('./wishlist');
25418
25419var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
25420
25421var options = {};
25422var loadingIndicatorSettings = {
25423    IsLabelShown: false,
25424    CssClass: 'fullscreen',
25425    BackgroundColor: 'transparent'
25426};
25427
25428function errorHandling(data, dialogTitle, dialogHtml) {
25429    if (data && typeof data === 'object' && 'message' in data) {
25430        console.log(data.message);
25431    }
25432    dialog.open({
25433        html: dialogHtml,
25434        options: {
25435            title: dialogTitle,
25436            draggable: false,
25437            open: function(event, ui) {                                    
25438                $('body').addClass('dialog-open');
25439            },
25440            close: function(event, ui) {
25441                $('body').removeClass('dialog-open');
25442            }
25443        }
25444    });
25445}
25446
25447function validateRegistrationForm($form) {	
25448    var rewardsFormFields = {
25449        email: $form.find('#dwfrm_rewardsregistration_rewardscustomer_email'),
25450        firstName: $form.find('#dwfrm_rewardsregistration_rewardscustomer_firstname'),
25451        lastName: $form.find('#dwfrm_rewardsregistration_rewardscustomer_lastname'),
25452        phone: $form.find('#dwfrm_rewardsregistration_rewardscustomer_phone'),
25453        zip: $form.find('#dwfrm_rewardsregistration_rewardscustomeraddress_zipcode').length > 0 ? $form.find('#dwfrm_rewardsregistration_rewardscustomeraddress_zipcode') : $form.find('#dwfrm_rewardsregistration_rewardscustomer_zipcode'),
25454        birthMonth: $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_month'),
25455        birthDay: $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_day')
25456    }
25457	if ($form.find('input[name*="dwfrm_rewardsregistration_rewardscustomer_password"]').length > 0) {
25458		rewardsFormFields.password = $form.find('input[name*="dwfrm_rewardsregistration_rewardscustomer_password"]');
25459	}
25460    var registrationFormFields = {
25461        email: $form.find('#dwfrm_profile_customer_email'),
25462        confirmEmail: $form.find('#dwfrm_profile_customer_emailconfirm'),
25463        firstName: $form.find('#dwfrm_profile_customer_firstname'),
25464        lastName: $form.find('#dwfrm_profile_customer_lastname')
25465    }
25466	if ($form.find('input[name*="dwfrm_profile_login_password_"]').length > 0) {
25467		registrationFormFields.password = $form.find('input[name*="dwfrm_profile_login_password_"]');
25468	}
25469	if ($form.find('input[name*="dwfrm_profile_login_passwordconfirm"]').length > 0) {
25470		registrationFormFields.confirmPassword = $form.find('input[name*="dwfrm_profile_login_passwordconfirm"]');
25471	}
25472    var checkFormFields = $form.attr('id') == 'dwfrm_rewardsregistration' ? rewardsFormFields : registrationFormFields;
25473	var formValidation = new Array();
25474    var regexEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/g;
25475	var regexPhone = /^\+?\(?([0-9][0-8][0-9])\)?[\-\. ]?([2-9][0-9]{2})[\-\. ]?([0-9]{4})(\s*x[0-9]+)?$/g;
25476    var regexZip = /^(\d{5})(-\d{4})?$/g;
25477    var regexPassword = /^(?=.*?[A-z])(?=.*?[0-9])(?=.*?[#?!\=\/\,\.\~\`\(\)\{\}\[\]\\@_$%^&*-]).{8,20}$/g;
25478
25479    for (var fieldName in checkFormFields) {
25480        var fieldValue = checkFormFields[fieldName].length ? checkFormFields[fieldName].val().trim() : null;
25481
25482        if (fieldValue != null && fieldValue.length > 0) {
25483            if (fieldName == 'email' || fieldName == 'confirmEmail') {
25484                if (String(fieldValue).match(regexEmail) == null) {
25485                    formValidation.push(false);
25486                    checkFormFields[fieldName].addClass('error');
25487					var emailFormatErrorClass = fieldName == 'email' ? 'span.invalid-email-format' : 'span.invalid-email-confirm-format';
25488					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find(emailFormatErrorClass).removeClass('d-none');
25489                }
25490				if (fieldName == 'confirmEmail' && (fieldValue != checkFormFields['email'].val().trim())) {
25491                    formValidation.push(false);
25492                    checkFormFields[fieldName].addClass('error');
25493					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find('span.invalid-email-confirm-mismatch').removeClass('d-none');
25494                }
25495            }
25496            if (fieldName == 'phone') {
25497                if (String(fieldValue).match(regexPhone) == null) {
25498                    formValidation.push(false);
25499                    checkFormFields[fieldName].addClass('error');
25500					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find('span.invalid-phone-format').removeClass('d-none');
25501                }
25502            }
25503            if (fieldName == 'zip') {
25504                if (String(fieldValue).match(regexZip) == null) {
25505                    formValidation.push(false);
25506                    checkFormFields[fieldName].addClass('error');
25507					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find('span.invalid-zip-format').removeClass('d-none');
25508                }
25509            }
25510            if (fieldName == 'password' || fieldName == 'confirmPassword') {
25511                if (String(fieldValue).match(regexPassword) == null) {
25512                    formValidation.push(false);
25513                    checkFormFields[fieldName].addClass('error');
25514					var passwordFormatErrorClass = fieldName == 'password' ? 'span.invalid-password-format' : 'span.invalid-password-confirm-format';
25515					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find(passwordFormatErrorClass).removeClass('d-none');
25516                }
25517                if (fieldName == 'confirmPassword' && (fieldValue != checkFormFields['password'].val().trim())) {
25518                    formValidation.push(false);
25519                    checkFormFields[fieldName].addClass('error');
25520					$('.registration-dialog-error[data-relation="'+fieldName+'"]').find('span.invalid-password-confirm-mismatch').removeClass('d-none');
25521                }
25522            }
25523        } else {
25524            formValidation.push(false);
25525            checkFormFields[fieldName].addClass('error');
25526			$('.registration-dialog-error[data-relation="'+fieldName+'"]').find('span.invalid-missing-fields').removeClass('d-none');
25527        }
25528    }
25529			
25530	if (formValidation.length > 0) {
25531        return false;
25532    } else {
25533        $form.find('.registration-dialog-error span').addClass('d-none');
25534        return true;
25535    }
25536}
25537
25538// Cordial rewards registration tracking (BB)
25539function rewardsRegisterCordialEvent($form, isRegisterDialog, callback) {
25540	var birthMonth = $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_month').val() ? $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_month').val() : '';
25541	var birthDay = $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').val() ? $form.find('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').val() : '';
25542	var birthdate = '';
25543	if (birthMonth != '') {
25544    	birthdate = birthMonth + '/' + birthDay + '/' + '1976';
25545    }
25546	var rewardsFormFields = {
25547        email: $form.find('#dwfrm_rewardsregistration_rewardscustomer_email').val().trim(),
25548        firstName: $form.find('#dwfrm_rewardsregistration_rewardscustomer_firstname').val().trim(),
25549        lastName: $form.find('#dwfrm_rewardsregistration_rewardscustomer_lastname').val().trim(),
25550        phone: $form.find('#dwfrm_rewardsregistration_rewardscustomer_phone').val().trim(),
25551        zip: $form.find('#dwfrm_rewardsregistration_rewardscustomeraddress_zipcode').length > 0 ? $form.find('#dwfrm_rewardsregistration_rewardscustomeraddress_zipcode').val().trim() : $form.find('#dwfrm_rewardsregistration_rewardscustomer_zipcode').val().trim()
25552    }
25553    var signupEvent = "";
25554
25555	var auth_data = {
25556		email: rewardsFormFields['email']
25557	}		
25558	var contact_data = {
25559		'channels': {
25560			'email': {
25561				'address': rewardsFormFields['email']      
25562			}
25563		},		    
25564		'first_name': rewardsFormFields['firstName'],
25565		'last_name': rewardsFormFields['lastName'],
25566		'phone': rewardsFormFields['phone'],
25567		'postal_code': rewardsFormFields['zip'],
25568		'birthday': birthdate,
25569		'source_create': 'myaccount',
25570		'geo_location': {
25571			'postal_code': rewardsFormFields['zip']
25572		}
25573	};			
25574	var loginProperties = {
25575		"email": rewardsFormFields['email'],
25576		"loginType": isRegisterDialog ? "registration-dialog" : "regular-registration"
25577	};	
25578	var registerProperties = {
25579		"email": rewardsFormFields['email'],
25580		"registerType": isRegisterDialog ? "registration-dialog" : "regular-registration"
25581	};			
25582	if ($('input#dwfrm_rewardsregistration_receiveRewardsEmail').is(':checked')) {
25583		var contact_data = {
25584			'channels': {
25585				'email': {
25586					'address': rewardsFormFields['email'],
25587					'subscribeStatus': 'subscribed'				            
25588				}
25589			},		    
25590			'first_name': rewardsFormFields['firstName'],
25591			'last_name': rewardsFormFields['lastName'],
25592			'phone': rewardsFormFields['phone'],
25593			'postal_code': rewardsFormFields['zip'],
25594			'birthday': birthdate,
25595			'signup_source': 'rewards',
25596			'source_create': 'myaccount',
25597			'welcome_signup_rewards_online': true,
25598			'geo_location': {
25599				'postal_code': rewardsFormFields['zip']
25600			}
25601		};		
25602		var properties = {
25603			"signup_source": 'rewards',
25604			"welcome_signup_rewards_online": true
25605		};		
25606		signupEvent = ['event', 'email_signup', properties];
25607	}	
25608	
25609	if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
25610		crdl([				
25611			['contact', auth_data, contact_data],
25612			['event', 'account_create', registerProperties],
25613			['event', 'login', loginProperties],
25614			signupEvent
25615		]);
25616	}
25617	if (callback) callback();
25618}
25619
25620// Cordial registration tracking (SHP and CO)
25621function registerCordialEvent($form, isRegisterDialog, callback) {
25622    var registrationFormFields = {
25623        email: $form.find('#dwfrm_profile_customer_email').val().trim(),
25624        firstName: $form.find('#dwfrm_profile_customer_firstname').val().trim(),
25625        lastName: $form.find('#dwfrm_profile_customer_lastname').val().trim()
25626    }
25627    var loginProperties = {
25628		"email": registrationFormFields['email'],
25629		"loginType": isRegisterDialog ? "registration-dialog" : "registration"
25630	};		
25631	var registerProperties = {
25632		"email": registrationFormFields['email'],
25633		"registerType": isRegisterDialog ? "registration-dialog" : "registration"
25634	};	
25635	var loginEvent = ['event', 'login', loginProperties];
25636	var signupEvent = "";
25637    var accountEvent = 'account_create';
25638    if ($('.profile-header').text().includes('Edit')) {
25639    	accountEvent = 'account_edit';
25640    	loginEvent = "";
25641    }
25642
25643	var auth_data = {
25644		email: registrationFormFields['email']
25645	}			
25646	var contact_data = {
25647		'channels': {
25648			'email': {
25649				'address': registrationFormFields['email']			            
25650			}
25651		},		    
25652		'first_name': registrationFormFields['firstName'],
25653		'last_name': registrationFormFields['lastName'],
25654		'source_create': 'myaccount'
25655	};
25656						
25657	if ($('input#dwfrm_profile_customer_addtoemaillist').is(':checked')) {
25658		var contact_data = {
25659			'channels': {
25660				'email': {
25661					'address': registrationFormFields['email'],
25662					'subscribeStatus': 'subscribed'
25663				}
25664			},		    
25665			'first_name': registrationFormFields['firstName'],
25666			'last_name': registrationFormFields['lastName'],
25667			'signup_source': 'myaccount',
25668			'source_create': 'myaccount',
25669			'welcome_signup': true
25670		};		
25671		var properties = {
25672			"signup_source": "myaccount",
25673			"page": "myaccount",
25674			"welcome_signup": true
25675		};
25676		signupEvent = ['event', 'email_signup', properties];
25677	}
25678	
25679	if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
25680		crdl([				
25681			['contact', auth_data, contact_data],
25682			['event', accountEvent, registerProperties],
25683			loginEvent,
25684			signupEvent
25685		]);
25686	}
25687	if (callback) callback();
25688}
25689
25690/**
25691 * @private
25692 * @function
25693 * @description add rules to code input field
25694 */
25695function initAddRulesForCodeInput() {
25696    $('form#verify-code').validate();
25697    $('#verify-code #enter-code').rules('add', {
25698        required: true,
25699        digits: true,
25700        minlength: 6,
25701        messages: {
25702            required: Resources.VALIDATE_CODE,
25703            minlength: Resources.VALIDATE_CODE
25704        },
25705        normalizer: function(value) {
25706            return $.trim(value);
25707        }
25708    });
25709}
25710
25711/**
25712 * @private
25713 * @function
25714 * @description init registration button event for registration page and dialog
25715 */
25716function initRegistrationButton() {
25717    $(document).off('click.Registration').on('click.Registration', '.accountRegisterButton, .saveRewardsRegistration.btn, .confirmationRegisterButton', function(event) {
25718		event.preventDefault();
25719		event.stopImmediatePropagation();
25720		var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with registration. Please try again later.</p>';
25721        var $registrationButton = $(this);
25722		var $registrationForm = $registrationButton.closest('form[data-form-type="register"]');
25723        var formUrl = $registrationForm.attr('action');
25724		var isConfirmationPage = $registrationButton.hasClass('confirmationRegisterButton');
25725
25726		// Clear previous form errors
25727		$registrationForm.find('input').removeClass('error');
25728		$registrationForm.find('.registration-dialog-error span').addClass('d-none');
25729
25730		var isValidForm = validateRegistrationForm($registrationForm);
25731		var isRegisterDialog = formUrl.indexOf('loginDialog') >= 0;
25732		var formData = $registrationForm.serialize(); 
25733		formData += '&' + $registrationButton.attr('name') + '=' + $registrationButton.attr('value');
25734		if (pageContext.options.Token) {
25735			formData += '&csrf_token=' + pageContext.options.Token;
25736		}
25737		
25738        // Using AJAX instead of default HTML button submit for registration dialog and "Create an Account" page
25739        if (isValidForm) {
25740            $('body').loading(loadingIndicatorSettings);
25741            if (SitePreferences.RETENTION_COM_ENABLED) {
25742            	geq.suppress();
25743            }           
25744            $.ajax({
25745                type: 'POST',
25746                url: formUrl,
25747                data: formData,
25748                success: function(data, textStatus, jqXHR) {
25749                    if (typeof data === 'object' && 'result' in data) {
25750                        if (data.result == 'error') {
25751							$('body').loading('destroy');
25752							if (isRegisterDialog || isConfirmationPage) {
25753								var errorsFound;
25754								if (data.validationErrors) {
25755									errorsFound = JSON.parse(data.validationErrors);
25756								}
25757								if (errorsFound && Object.keys(errorsFound).length > 0) {
25758									for (var fieldName in errorsFound) {
25759										$('[name*="' +fieldName+ '"]').addClass('error');
25760										$(errorsFound[fieldName]).removeClass('d-none');
25761									}
25762								} else {
25763									errorHandling(data, 'Register - Error', dialogHtml);
25764								}
25765							} else {
25766								window.location.href = data.registerDialogContinueUrl;
25767							}
25768                        } else if (data.result == 'success') {
25769							$('body').loading('destroy');
25770							// If a rewards account was found in rewards DB, then verify customer and tie newly registered email address to exisitng rewards account
25771                            if ('validateCustomer' in data && data.validateCustomer) {
25772                                if (SitePreferences.CORDIAL_ENABLED) {
25773                                    if ($registrationForm.attr('id') == 'dwfrm_rewardsregistration') {
25774										rewardsRegisterCordialEvent($registrationForm, isRegisterDialog);
25775									} else {
25776										registerCordialEvent($registrationForm, isRegisterDialog);
25777									}
25778                                }
25779								var newAttributes = {
25780									"data-modal-type": "register",
25781                                    "data-continue-url": 'registerDialogContinueUrl' in data ? data.registerDialogContinueUrl : '',
25782                                    "data-first-name": 'customerFirstName' in data ? data.customerFirstName : '',
25783                                    "data-store-number": 'storeFromProfile' in data ? data.storeFromProfile : '',
25784                                    "data-cart-quantity": 'cartQuantity' in data ? data.cartQuantity : ''
25785                                }
25786                                $('.login-dialog-validate-customer').html(data.validateCustomer);
25787                                $('.login-dialog-validate-customer').addClass('validate-customer-show follow-account-dialog-path').attr(newAttributes);
25788                                // For register-dialog because the validation form is only loaded into page via AJAX, so rules can't be added until form is loaded
25789                                initAddRulesForCodeInput();
25790                            } else {
25791								if (isConfirmationPage) {
25792									// Redirect to My Account if this is a registration from order 
25792confirmation page
25793									data.registerDialogContinueUrl = Urls.account;
25794								}
25795								if (SitePreferences.CORDIAL_ENABLED) {
25796									if ($registrationForm.attr('id') == 'dwfrm_rewardsregistration') {
25797										rewardsRegisterCordialEvent($registrationForm, isRegisterDialog, function() {
25798											$(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: data, isAccountDialog: isRegisterDialog, displayMessage: 'register' });
25799										});
25800									} else {
25801										registerCordialEvent($registrationForm, isRegisterDialog, function() {
25802											$(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: data, isAccountDialog: isRegisterDialog, displayMessage: 'register' });
25803										});
25804									}
25805								} else {
25806									$(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: data, isAccountDialog: isRegisterDialog, displayMessage: 'register' });
25807								}
25808							}
25809                        } else {
25810							$('body').loading('destroy');
25811                        	errorHandling(data, 'Register - Error', dialogHtml);
25812						}
25813                    } else {
25814						$('body').loading('destroy');
25815						if (isRegisterDialog) {
25816							errorHandling(data, 'Register - Error', dialogHtml);
25817						} else {
25818							window.location.href = location.href;
25819						}
25820					}
25821                },
25822                error: function(jqXHR, textStatus, errorThrown) {
25823                    $('body').loading('destroy');
25824					if (!isRegisterDialog) {
25825						window.location.href = location.href;
25826					}
25827                }
25828            });
25829        }  
25830	});
25831}
25832
25833function initializeBirthdayInputs() {
25834	var $birthdayForm = $('#dwfrm_rewardsregistration');
25835	if ($('#dwfrm_birthday').length >= 1 ){
25836		var $birthdayForm = $('#dwfrm_birthday');
25837	}
25838	
25839	$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').prop('disabled', true);
25840	var longMonths = ['1','3','5','7','8','10','12'];
25841	var febMonth = '2';
25842	$birthdayForm.on('change', 'select#dwfrm_rewardsregistration_rewardscustomer_birthday_month', function(e) {
25843		$('.birthday.day').removeClass('disabled');
25844		$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').find('option:selected').prop('selected', false);
25845		$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option').prop('disabled', false);
25846		var monthVal = $('#dwfrm_rewardsregistration_rewardscustomer_birthday_month').val();
25847		if ($.inArray(monthVal, longMonths) === -1) {
25848			$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option[value="31"]').prop('disabled', true);
25849			if (monthVal == febMonth){
25850				$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day .select-option[value="30"]').prop('disabled', true);
25851			}
25852		}
25853		$('#dwfrm_rewardsregistration_rewardscustomer_birthday_day').prop('disabled', false);
25854	});
25855}
25856
25857/**
25858 * @private
25859 * @function
25860 * @description displays the registration form in the dialog on button click
25861 */
25862function initRegistrationForm() {
25863	$('body').on('click', '#toggle-registration', function(e) {
25864		e.preventDefault();
25865		var $dialog = $(this).parents('.login-dialog');
25866		var $dialogTitlebar = $dialog.find('.ui-dialog-titlebar');
25867		var $dialogContent = $dialog.find('.ui-dialog-content');
25868		var $dialogFormsContainer = $dialog.find('.login-dialog-forms-container');
25869		var calculatedSpacing = $(window).height() * 0.21;
25870		var newDialogHeight = $(window).height() - calculatedSpacing;
25871		var newTopValue = $(window).scrollTop() + (calculatedSpacing * 0.233);
25872
25873		if ($dialogTitlebar && $dialogTitlebar.is(":visible")) {
25874			newDialogHeight = newDialogHeight + $dialogTitlebar.outerHeight();
25875		}
25876
25877		var paddingTop = '32px';
25878		var currentSite = pageContext && pageContext.site ? pageContext.site : null;
25879		if ((currentSite && currentSite == SiteConstants.SiteIds.BootBarnUS) || (pageContext && pageContext.isstoresite)) {
25880			paddingTop = '28px';
25881		}
25882		if (currentSite && currentSite == SiteConstants.SiteIds.BootBarnUS) {
25883			initializeBirthdayInputs();
25884		}
25885
25886		$('.login-dialog-signin-container, #toggle-registration').hide();
25887		$('.login-dialog-register-form').addClass('register-form-show');
25888		$dialog.css({
25889			'height': newDialogHeight+'px',
25890			'top': newTopValue+'px',
25891			'overflow': 'visible'
25892		  });
25893		$dialogContent.css({
25894			'height': '100%',
25895			'padding': '0 11px 20px',
25896			'padding-top': paddingTop
25897		});
25898		$dialogFormsContainer.css('height','100%');
25899	});
25900}
25901
25902/**
25903 * @private
25904 * @function
25905 * @description init update button event for edit account page
25906 */
25907function initRegistrationEdit() {
25908	$('body').on('click', '.accountEditButton', function(e) {
25909        var $registrationButton = $(this);
25910		var $registrationForm = $registrationButton.closest('form[data-form-type="register"]');
25911		var formUrl = $registrationForm.attr('action');
25912		var isRegisterDialog = formUrl.indexOf('loginDialog') >= 0;
25913		
25914		if (SitePreferences.CORDIAL_ENABLED) {
25915			registerCordialEvent($registrationForm, isRegisterDialog);
25916        }
25917	});
25918}
25919
25920module.exports = {
25921    init: function (optionsParameter) {
25922        options = optionsParameter;
25923
25924		initRegistrationForm();
25925        initRegistrationButton();
25926        initRegistrationEdit();
25927    }
25928};
25929},{"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"./alert":4,"./components/LoadingIndicator":25,"./dialog":36,"./wishlist":105}],91:[function(require,module,exports){
25930'use strict';
25931
25932var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
25933var util = require('./util');
25934
25935var siteName;
25936var $win = $(window);
25937var getSuggestionsHttpRequest;
25938var getSuggestionsTimer;
25939var getSuggestionsLastQuery;
25940
25941/**
25942     * @function
25943     * @description Configures parameters and required object instances
25944     */
25945function init() {
25946    siteName = window.pageContext.site || '';
25947
25948    $('.top-banner .header-search button[type="submit"]').on('click', function(event) {
25949        event.preventDefault();
25950
25951        var $this = $(this);
25952        var $searchForm = $this.closest('form');
25953        var $searchInput = $searchForm.find('[name="q"]');
25954        var action = $searchForm.attr('action');
25955        var keyword = $searchInput.val();
25956        var url = action + "?q=" + keyword;
25957
25958        if (keyword && keyword != $searchInput.attr('placeholder')) {
25959            $searchForm.attr('action', url);
25960            $searchForm.submit();
25961        }
25962    });
25963	$('.header-search').on('click.SearchClear', 'button[type="reset"]', function(event) {
25964		event.preventDefault();
25965		$('.header-search').attr('has-value', 'false');
25966		$('.header-search input#q, .header-search input#mobile-q').val('');
25967		$('.header-search input#q:visible, .header-search input#mobile-q:visible').focus();
25968	});
25969	$('.top-banner-bar .scan').on('click.SearchClear', 'button[type="reset"]', function(event) {
25970		event.preventDefault();
25971		$('.top-banner-bar .scan').attr('has-value', 'false');
25972		$('.top-banner-bar .scan .search-input').val('').focus();
25973	});
25974	$(document).on('focus click keyup', '.header-search input#q, .header-search input#mobile-q', function(event) {
25975		var $this = $(this);
25976		var keyword = $this.val();
25977		$('.header-search').attr('has-value', !!(keyword && keyword.length > 1 && keyword != $this.attr('placeholder')));
25978		if ((SitePreferences.SEARCH_SUGGESTIONS_ENABLED || (SitePreferences.CIMULATE_ENABLED && SitePreferences.CIMULATE_SEARCH_SUGGESTIONS_ENABLED)) && keyword && keyword.length > 1 && keyword != $this.attr('placeholder')) {
25979			if (getSuggestionsTimer) {
25980				clearTimeout(getSuggestionsTimer);
25981			}
25982			if (keyword == getSuggestionsLastQuery) {
25983				renderSuggestions().catch(promotedSuggestions);
25984			} else {
25985				getSuggestionsTimer = setTimeout(function() {
25986					getSuggestions($this.val()).then(renderSuggestions).catch(promotedSuggestions);
25987				}, 250);
25988			}
25989		} else if (SitePreferences.PROMOTED_SEARCHES) {
25990			promotedSuggestions();
25991		}
25992	});
25993
25994
25995    if (siteName == SiteConstants.SiteIds.BootBarnUS) {
25996        $('.header-search').on('click.SearchActive', function() {
25997            $('.header-search').addClass('active');
25998        });
25999
26000        $('.header-search input#q').on('focusout', function() {
26001			if (!$(this).val()) {
26002            	$('.header-search').removeClass('active');
26003			}
26004        });
26005    }
26006    if (SitePreferences.PROMOTED_SEARCHES || SitePreferences.SEARCH_SUGGESTIONS_ENABLED || (SitePreferences.CIMULATE_ENABLED && SitePreferences.CIMULATE_SEARCH_SUGGESTIONS_ENABLED)) {
26007        $(document).on('mouseleave', '.search-suggestions-container, .header-search [name="q"]', function(e) {
26008            var searchHeader = document.querySelector('.header-search');
26009            if (!searchHeader.contains(e.relatedTarget) && $('.search-suggestions-container').find(e.relatedTarget).length < 1) {
26010                $('.search-suggestions-container').hide();
26011            }
26012        });
26013
26014        $(document).on('click', '.close-search-suggestions', function() {
26015            $('.search-suggestions-container').hide();
26016        });  
26017        
26018        $(document).off('SearchBar.Expand').on('SearchBar.Expand', function() {
26019            expand();
26020        });
26021
26022        $(document).off('SearchBar.ScrollTop').on('SearchBar.ScrollTop', function() {
26023            scrollTop();
26024        });
26025
26026        $(document).off('SearchBar.Hide').on('SearchBar.Hide', function() {
26027            hide();
26028        });
26029    }
26030}
26031
26032/**
26033 * @function
26034 * @description
26035 */
26036function expand() {
26037    $('.top-banner').toggleClass('search-enabled');
26038    var $target = $('.header-search');
26039    if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
26040        $target = $('.search-holder');
26041    }
26042            
26043    if ((siteName == SiteConstants.SiteIds.SheplersUS || siteName == SiteConstants.SiteIds.CountryOutfittersUS) && $('html').hasClass('size-mobile')) {
26044        $target.css('top', $('.top-banner-bar').outerHeight() + $('.top-banner-inner').outerHeight() - $win.scrollTop());
26045    } else {
26046        $target.css('top', '');
26047    }
26048            
26049    if (siteName == SiteConstants.SiteIds.SheplersUS) {
26050        $target.find('input[type="text"]').focus();
26051    }
26052            
26053    if ($('.top-banner').hasClass('search-enabled') && $('html').hasClass('size-mobile') && $('.top-banner-bar').attr('data-header-for') == 'bootbarn_us') {
26054        $('#search #q').focus();
26055    }
26056}
26057
26058/**
26059 * @function
26060 * @description
26061 */
26062function scrollTop() {
26063    if (siteName == SiteConstants.SiteIds.SheplersUS || siteName == SiteConstants.SiteIds.C
26063ountryOutfittersUS) {
26064        var $target = $('.header-search');
26065        if (siteName == SiteConstants.SiteIds.CountryOutfittersUS) {
26066            $target = $('.search-holder');
26067        }
26068        
26069        if ($('html').hasClass('size-mobile')) {
26070            $target.css('top', $('.top-banner-bar').outerHeight() + $('.top-banner-inner').outerHeight() - $win.scrollTop());
26071        } else {
26072            $target.css('top', '');
26073        }
26074    }
26075}
26076
26077/**
26078 * @function
26079 * @description
26080 */
26081function hide() {
26082    if($('.header-search').hasClass('show')) {
26083        $('.header-search').removeClass('show');
26084        if (siteName != SiteConstants.SiteIds.BootBarnRspUS) {
26085            $('.top-banner').removeClass('search-enabled');
26086        }
26087    }
26088}
26089
26090/**
26091 * @function
26092 * @description trigger promoted suggestions for site search
26093 */
26094function promotedSuggestions() {
26095	if (getSuggestionsHttpRequest && getSuggestionsHttpRequest.readyState != 4) { 
26096		getSuggestionsHttpRequest.abort();
26097	}
26098	if (getSuggestionsTimer) {
26099		clearTimeout(getSuggestionsTimer);
26100	}
26101
26102	var $container = $('.search-suggestions-container');
26103	var $suggestionsContentSlot = $container.find('.html-slot-container');
26104    var isContentPopulated = $suggestionsContentSlot.length > 0;
26105    if (isContentPopulated) {
26106		if ($suggestionsContentSlot.is(':hidden')) {
26107			$suggestionsContentSlot.show();
26108		}
26109		var $suggestions = $container.find('.search-suggestion-wrapper');
26110		if ($suggestions.is(':visible')) {
26111			$suggestions.hide();
26112		}
26113		if ($container.is(':hidden')) {
26114        	$container.show();
26115		}		
26116    } else if ($container.is(':visible')) {
26117		$container.hide();
26118	}
26119}
26120
26121function getSuggestions(query) {
26122	return new Promise(function(resolve, reject) {
26123		if (getSuggestionsHttpRequest && getSuggestionsHttpRequest.readyState != 4) { 
26124			getSuggestionsHttpRequest.abort();
26125		}
26126
26127		if (query) {
26128			getSuggestionsLastQuery = query;
26129			getSuggestionsHttpRequest = $.ajax({
26130				url: util.appendParamToURL(Urls.searchsuggest, 'q', query),
26131				type: 'GET',
26132				success: function(html) {
26133					if (html) {
26134						resolve(html);
26135					} else {
26136						reject();	
26137					}
26138				},
26139				error: function() {
26140					reject();
26141				}
26142			});
26143		} else {
26144			reject();
26145		}
26146	});
26147}
26148
26149function renderSuggestions(html) {
26150	return new Promise(function(resolve, reject) {
26151		var $suggestions;
26152		var $container = $('.search-suggestions-container');
26153		if ($container.length > 0) {
26154			if (html) {
26155				$suggestions = $container.find('.search-suggestion-wrapper');
26156				if ($suggestions.length > 0) {
26157					$suggestions[0].outerHTML = html;
26158				} else {
26159					$container.append(html);
26160				}
26161			}
26162			$suggestions = $container.find('.search-suggestion-wrapper');
26163			if ($suggestions.length > 0) {
26164				var $suggestionsContentSlot = $container.find('.html-slot-container');
26165				if ($suggestionsContentSlot.is(':visible')) {
26166					$suggestionsContentSlot.hide();
26167				}	
26168				if ($suggestions.is(':hidden')) {
26169					$suggestions.show();
26170				}
26171				if ($container.is(':hidden')) {
26172					$container.show();
26173				}
26174
26175				resolve();
26176			} else {
26177				reject();
26178			}
26179		}
26180	});
26181}
26182
26183exports.init = init;
26184},{"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"./util":102}],92:[function(require,module,exports){
26185'use strict';
26186
26187/**
26188 * @private
26189 * @function
26190 * @description Binds event to the place holder (.blur)
26191 */
26192function initializeEvents() {
26193    $('#q').focus(function () {
26194        var input = $(this);
26195        if (input.val() === input.attr('placeholder')) {
26196            input.val('');
26197        }
26198    })
26199    .blur(function () {
26200        var input = $(this);
26201        if (input.val() === '' || input.val() === input.attr('placeholder')) {
26202            input.val(input.attr('placeholder'));
26203        }
26204    })
26205    .blur();
26206}
26207
26208exports.init = initializeEvents;
26209
26210},{}],93:[function(require,module,exports){
26211'use strict';
26212
26213var page = require('./page');
26214
26215var isTouchMoved = false;
26216var isCurrentTargetSwatch = false;
26217
26218function initializeEvents() {
26219	$(document).off('click.Swatch.Menu.Close').on('click.Swatch.Menu.Close', function (event) {
26220		var $currentTarget = $(event.currentTarget);
26221		
26222		if ($currentTarget.closest('.slide-down-swatch').length == 0) {
26223			$('.slide-down-swatch ul').slideUp();
26224		}
26225	});
26226	
26227	$(document).off('touchstart.Swatch.Menu.Close').on('touchstart.Swatch.Menu.Close', function (event) {
26228		var $currentTarget = $(event.currentTarget);
26229		
26230		isTouchMoved = false;
26231		isCurrentTargetSwatch = $currentTarget.closest('.slide-down-swatch').length > 0;
26232	});
26233	
26234	$(document).off('touchmove.Swatch.Menu.Close').on('touchmove.Swatch.Menu.Close', function (event) {
26235		isTouchMoved = true;
26236	});
26237	
26238	$(document).off('touchend.Swatch.Menu.Close').on('touchend.Swatch.Menu.Close', function (event) {
26239		if (!isCurrentTargetSwatch && !isTouchMoved) {
26240			$('.slide-down-swatch ul').slideUp();
26241		}
26242	});
26243	
26244	$('.swatches.size > li > a.slide-down').on('click', function(event) {
26245		event.preventDefault();
26246		event.stopPropagation();
26247				
26248		$(this)
26249		.next('ul')
26250		.stop()
26251		.slideToggle()
26252		.parent()
26253		.siblings()
26254		.children('ul')
26255		.slideUp()
26256		.prev('.slide-down');
26257	});
26258
26259	// Default selected value as slide down
26260	$('.swatches.size > li > a.slide-down')
26261	.next('ul')
26262	.find('.current')
26263	.closest('ul')
26264	.prev('.slide-down')
26265	.addClass('current')
26266	.closest('li').addClass('active');
26267
26268	$('.refinements .swatches.size > li.active > ul').slideDown();
26269
26270	$('.swatches > li.slide-down-swatch > ul').off('click');
26271
26272	// Remove empty tiles 
26273	$('.swatches.size > li > a.slide-down').next('ul').each(function() {
26274		if ($(this).find('li').length === 0) {
26275			$(this).closest('li').remove();
26276		}
26277	});
26278}
26279
26280var sizeRefinements = {
26281	init: function() {
26282		initializeEvents();
26283	}
26284};
26285
26286module.exports = sizeRefinements;
26287},{"./page":46}],94:[function(require,module,exports){
26288'use strict';
26289
26290/* -------------------------
26291 * ADA Slick Mods
26292 * -------------------------
26293 */
26294
26295exports.init = function init () {
26296    $(document).off('SlickSliderAccessibilityMods').on('SlickSliderAccessibilityMods', function(event) {
26297        $('.slick-slider .slick-slide, .slick-slider .slick-active').removeAttr('aria-describedby role tabindex');			
26298        $('.slick-slider .slick-dots button').each(function() {
26299            var buttonVal = $(this).html();
26300            var addSpan = '<span class="visually-hidden">' + buttonVal + '</span>';
26301            $(this).html(addSpan);
26302        });
26303    });
26304};
26305},{}],95:[function(require,module,exports){
26306'use strict';
26307
26308/* ----------------------------------------
26309 * -- Sliders --
26310 * 
26311 * Configuration Options:
26312 *                loop:  BOOLEAN  Both; If no option given, programmatically set to true.
26313 *                dots:  BOOLEAN  Both; If no option given, programmatically set to true.
26314 *          pagination:  OBJECT   Swiper only; If no option given, pagination is omitted from slider settings.
26315 *              arrows:  BOOLEAN  Both; If no option given, automatically set to true in widget background.
26316 *      arrowsPosition:  STRING   ['inside' or 'outside'] Swiper only; If no option given, automatically set to the outside.
26317 *          navigation:  OBJECT   Swiper only; If no option given, navigation is omitted from slider settings.
26318 *        slidesToShow:  NUMBER   Both; If no option given, programmatically set to 1.
26319 *      slidesToScroll:  NUMBER   Both; If no option given, programmatically set to 1.
26320 *            autoplay:  NUMBER   Both; When given, number is programmatically set to Autoplay Speed and Autoplay is set to true. If no option given, autoplay is set to false.
26321 *         breakpoints:  OBJECT   Both; Properties are looped over to apply various breakpoint settings. If no option given, breakpoints is omitted from slider settings.
26322 *     
26323 * Example Code:
26324 *        sliders.init('.some-class-name', 
26325 *        {
26326 *             loop: true, 
26327 *             dots: true, 
26328 *             pagination: {el: '.swiper-pagination', clickable: true}, 
26329 *             arrows: true,
26330 *             arrowsPosition: 'inside',
26331 *             navigation: {nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev'},
26332 *             slidesToShow: 6,
26333 *             slidesToScroll: 1,
26334 *             autoplay: 3000,
26335 *             breakpoints: {0: {slidesToShow: 1}, 768: {slidesToShow: 3}}
26336 *        });
26337 * 
26338 * 
26339 * Additional configuration options exist for both sliders, but additional code changes would be needed to include them. 
26340 * 
26341 * Reference slider documentation:
26342 * 
26343 * Swiper documentation - https://swiperjs.com/swiper-api
26344 * Slick documentation - https://kenwheeler.github.io/slick/
26345 *  
26346 * ----------------------------------------
26347 */
26348
26349var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
26350
26351exports.init = init;
26352exports.initializeCategorySliders = initializeCategorySliders;
26353exports.initializeRecommendationSlider = initializeRecommendationSlider;
26354
26355function getUniqueNumber() {
26356	var dateObject = new Date();
26357	var uniqueNumber = dateObject.valueOf();
26358    var suffixNumber = 1 + Math.floor(Math.random() * 100);
26359    uniqueNumber = uniqueNumber + suffixNumber;
26360	return uniqueNumber;
26361}
26362
26363function convertedSlickBreakpointSize(breakpointSize, breakpointSizeArray) {
26364    var sortedBreakpointSizes = breakpointSizeArray.sort(function (a, b) { return a - b; });
26365    var convertedBreakpointSize;
26366    
26367    if (breakpointSize == 0) {
26368        var mobileMaxSize = sortedBreakpointSizes[sortedBreakpointSizes.indexOf(0) + 1];
26369        if (mobileMaxSize) {
26370            convertedBreakpointSize = mobileMaxSize - 1;
26371        } else {
26372            convertedBreakpointSize = 767;
26373        }
26374    } else if (breakpointSize == 768) {
26375        var desktopMaxSize = sortedBreakpointSizes[sortedBreakpointSizes.indexOf(768) + 1];
26376        if (desktopMaxSize) {
26377            convertedBreakpointSize = desktopMaxSize - 1;
26378        } else {
26379            convertedBreakpointSize = 9999;
26380        }
26381    } else {
26382        var breakpointMaxSize = sortedBreakpointSizes[sortedBreakpointSizes.indexOf(breakpointSize) + 1];
26383        if (breakpointMaxSize) {
26384            convertedBreakpointSize = breakpointMaxSize - 1;
26385        } else {
26386            convertedBreakpointSize = 9999;
26387        }
26388    }
26389
26390    return convertedBreakpointSize;
26391}
26392
26393function initSlick(elementSelector, settings) {
26394    if (!elementSelector) {
26395        return;
26396    }
26397
26398    var sliderSettings = {
26399        infinite: true,
26400        slidesToShow: 1,
26401        slidesToScroll: 1
26402    };
26403    if (settings) {
26404        if ('loop' in settings) {
26405            sliderSettings.infinite = settings.loop;
26406        }
26407        if ('dots' in settings) {
26408            sliderSettings.dots = settings.dots;
26409        }
26410        if ('arrows' in settings) {
26411            sliderSettings.arrows = settings.arrows;
26412        }
26413        if ('slidesToShow' in settings) {
26414            sliderSettings.slidesToShow = settings.slidesToShow;
26415        }
26416        if ('slidesToScroll' in settings) {
26417            sliderSettings.slidesToScroll = settings.slidesToScroll;
26418        }
26419        if ('autoplay' in settings) {
26420            sliderSettings.autoplay = true;
26421            sliderSettings.autoplaySpeed = settings.autoplay;
26422        }
26423        if ('breakpoints' in settings) {
26424            sliderSettings.responsive = [];
26425            var breakpointSizeArray = Object.keys(settings.breakpoints).map(function (x) { 
26426                return parseInt(x); // Extra precaution of sanitizing data for use in mathematical equations
26427            });
26428            for (var breakpointSize in settings.breakpoints) {
26429                breakpointSize = parseInt(breakpointSize);
26430                var convertedBreakpointSize = convertedSlickBreakpointSize(breakpointSize, breakpointSizeArray);
26431                var breakpointSettingsObject = {
26432                    breakpoint: convertedBreakpointSize,
26433                    settings: {}
26434                };
26435                var breakpointSizeSettings = settings.breakpoints[breakpointSize];
26436                for (var sizeSetting in breakpointSizeSettings) {
26437                    if ('loop' == sizeSetting) {
26438                        breakpointSettingsObject.settings.infinite = breakpointSizeSettings[sizeSetting];
26439                    }
26440                    if ('dots' == sizeSetting) {
26441                        breakpointSettingsObject.settings.dots = breakpointSizeSettings[sizeSetting];
26442                    }
26443                    if ('arrows' == sizeSetting) {
26444                        breakpointSettingsObject.settings.arrows = breakpointSizeSettings[sizeSetting];
26445                    }
26446                    if ('slidesToShow' == sizeSetting) {
26447                        if (convertedBreakpointSize == 9999) {
26448                            sliderSettings.slidesToShow = breakpointSizeSettings[sizeSetting];
26449                        } else {
26450                            breakpointSettingsObject.settings.slidesToShow = breakpointSizeSettings[sizeSetting];
26451                        }
26452                    }
26453                    if ('slidesToScroll' == sizeSetting) {
26454                        if (convertedBreakpointSize == 9999) {
26455                            sliderSettings.slidesToScroll = breakpointSizeSettings[sizeSetting];
26456                        } else {
26457                            breakpointSettingsObject.settings.slidesToScroll = breakpointSizeSettings[sizeSetting];
26458                        }
26459                    }
26460                    if ('autoplay' == sizeSetting) {
26461                        if (convertedBreakpointSize == 9999) {
26462                            sliderSettings.autoplay = true;
26463                            sliderSettings.autoplaySpeed = breakpointSizeSettings[sizeSetting].autoplay;
26464                        } else {
26465                            breakpointSettingsObject.settings.autoplay = true;
26466                            breakpointSettingsObject.settings.autoplaySpeed = breakpointSizeSettings[sizeSetting];
26467                        }
26468                    }
26469                }
26470                //if (convertedBreakpointSize != 9999) {
26471                    sliderSettings.responsive.push(breakpointSettingsObject);
26472                //}
26473            }
26474        }
26475
26476        $(elementSelector).slick(sliderSettings);
26477    } else {
26478        $(elementSelector).slick();
26479    }
26480}
26481
26482function initSwiper(elementSelector, settings, classesForSettings) {
26483    if (!elementSelector) {
26484        return;
26485    }
26486
26487    var sliderSettings = {
26488        loop: true,
26489        spaceBetween: 20,
26490        preventInteractionOnTransition: true,
26491        a11y: {
26492            enabled: true,
26493            firstSlideMessage: 'This is the first slide',
26494            lastSlideMessage: 'This is the last slide'
26495        },
26496        keyboard: {
26497			enabled: true,
26498			pageUpDown: true
26499		}
26500    };
26501    if (settings) {
26502        if ('spaceBetween' in settings) {
26503            sliderSettings.spaceBetween = settings.spaceBetween;
26504        }
26505        if ('loop' in settings) {
26506            sliderSettings.loop = settings.loop;
26507        }
26508        if ('pagination' in settings || 'dots' in settings) {
26509            if (settings.pagination) {
26510                if (settings.pagination.el && settings.pagination.el.length > 0) {
26511                    sliderSettings.pagination = settings.pagination;
26512                }
26513            } else {
26514                if (settings.dots) {
26515                    sliderSettings.pagination = {
26516                        el: '.swiper-pagination',
26517                        clickable: true
26518                    }
26519                }
26520            }
26521        } 
26522        if ('navigation' in settings || 'arrows' in settings) {
26523            if (settings.navigation) {
26524                if (settings.navigation.nextEl && settings.navigation.nextEl.length > 0) {
26525                    sliderSettings.navigation = settings.navigation;
26526                }
26527            } else {
26528                if (settings.arrows) {
26529                    sliderSettings.navigation = {
26530                        nextEl: '.swiper-button-next',
26531                        prevEl: '.swiper-button-prev'
26532                    }
26533                }
26534            }
26535        }
26536        if ('slidesToShow' in settings) {
26537            sliderSettings.slidesPerView = settings.slidesToShow;
26538        }
26539        if ('autoplay' in settings) {
26540            sliderSettings.autoplay = {
26541                delay: settings.autoplay,
26542                disableOnInteraction: true
26543            };
26544        }
26545        if ('breakpoints' in settings) {
26546            sliderSettings.breakpoints = {};
26547            for (var breakpointSize in settings.breakpoints) {
26548                breakpointSize = parseInt(breakpointSize);
26549                sliderSettings.breakpoints[breakpointSize] = {};
26550                var breakpointSizeSettings = settings.breakpoints[breakpointSize];
26551                for (var sizeSetting in breakpointSizeSettings) {
26552                    if ('loop' == sizeSetting) {
26553                        sliderSettings.loop = breakpointSizeSettings[sizeSetting];
26554                    }
26555                    if ('dots' == sizeSetting && breakpointSizeSettings[sizeSetting] == true) {
26556                        sliderSettings.breakpoints[breakpointSize].pagination = {
26557                            enabled: breakpointSizeSettings[sizeSetting],
26558                            el: '.swiper-pagination',
26559                            clickable: true
26560                        };
26561                        if (classesForSettings && classesForSettings.pagination) {
26562                            sliderSettings.breakpoints[breakpointSize].pagination.el = classesForSettings.pagination;
26563                        }  
26564                    }
26565                    if ('arrows' == sizeSetting && breakpointSizeSettings[sizeSetting] == true) {
26566                        sliderSettings.breakpoints[breakpointSize].navigation = {
26567                            enabled: breakpointSizeSettings[sizeSetting],
26568                            nextEl: '.swiper-button-next',
26569                            prevEl: '.swiper-button-prev'
26570                        };
26571                        if (classesForSettings && classesForSettings.next && classesForSettings.prev) {
26572                            sliderSettings.breakpoints[breakpointSize].navigation.nextEl = classesForSettings.next;
26573                            sliderSettings.breakpoints[breakpointSize].navigation.prevEl = classesForSettings.prev;
26574                        }
26575                    }
26576                    if ('slidesToShow' == sizeSetting) {
26577                        sliderSettings.breakpoints[breakpointSize].slidesPerView = breakpointSizeSettings.slidesToShow;
26578                    }
26579                    if ('slidesToScroll' == sizeSetting) {
26580                        sliderSettings.breakpoints[breakpointSize].slidesPerGroup = breakpointSizeSettings.slidesToScroll;
26581                    }
26582                    if ('autoplay' in breakpointSizeSettings) {
26583                        sliderSettings.autoplay = {
26584                            delay: breakpointSizeSettings.autoplay,
26585                            disableOnInteraction: true
26586                        };
26587                    } else {
26588                        sliderSettings.breakpoints[breakpointSize].autoplay = false;
26589                    }
26590                }
26591            }
26592        }
26593    }
26594    
26595    return new Swiper (elementSelector, sliderSettings);
26596}
26597
26598function formatSlickHTML($element, settings, isEinstein) {
26599    var originalSlideElements = $element.children().toArray();
26600    var uniqueSlider;
26601
26602    if ($element.attr('id') && $element.attr('id').indexOf('slider-index') > -1) {
26603        uniqueSlider = $element.attr('id');
26604    } else {
26605        uniqueSlider = 'slider-index-' + getUniqueNumber().toString();
26606    }
26607
26608    $element.attr('id', uniqueSlider);
26609
26610    // Einstein CQuotient scripts are mixed in with slides at time of slider initialization. Rework the HTML structure.
26611    if (originalSlideElements.length > 0 && isEinstein) {
26612        var modifiedSlideElements = [];
26613        var cqScripts = [];
26614
26615        $.each(originalSlideElements, function(index, element) {
26616            if (element.tagName.toUpperCase() === 'SCRIPT') {
26617                cqScripts.push(element);
26618            } else {
26619                if (element.tagName.toUpperCase() === 'LI') {
26620                    modifiedSlideElements.push(element);
26621                }  
26622            }
26623        });
26624
26625        if (cqScripts.length > 0) {
26626            $element.closest('div.slider-clip').append(cqScripts);
26627        }
26628
26629        $element.html('');
26630        $element.append(modifiedSlideElements);
26631    }
26632    return;
26633}
26634
26635function formatSwiperHTML($element, settings, isEinstein) {
26636    var originalSlideElements = $element.children().toArray();
26637    var uniqueSlider, sortedSlidesPerViewArray, addSlides, finalSlides;
26638    var isLoop = true;
26639    var slidesPerViewArray = [];
26640    var cqScripts = [];
26641    var modifiedSlideElements = [];
26642    var duplicatedSlideElements = [];
26643    var hasSwiperHTML = $element.hasClass('swiper-full-wrapper') && $element.find('.swiper').length > 0;
26644    var swiperClassesForSettings = {};
26645
26646    if ($element.attr('id') && $element.attr('id').indexOf('slider-index') > -1) {
26647        uniqueSlider = $element.attr('id');
26648    } else {
26649        uniqueSlider = 'slider-index-' + getUniqueNumber().toString();
26650    }
26651
26652    var $swiperParent = $('<div />', { 'class': 'swiper', 'id': uniqueSlider }),
26653        $swiperPagination = $('<div />', { 'class': 'swiper-pagination swiper-pagination-' + uniqueSlider }),
26654        $swiperNavigation = $('<div />', { 'class': 'swiper-navigation-buttons-container' }),
26655        $swiperPrevArrow = $('<div />', { 'class': 'swiper-button-prev swiper-button-prev-' + uniqueSlider }),
26656        $swiperNextArrow = $('<div />', { 'class': 'swiper-button-next swiper-button-next-' + uniqueSlider }),
26657        $swiperWrapper = $('<div />', { 'class': 'swiper-wrapper' });
26658
26659    if (settings) {
26660        if ('breakpoints' in settings) {
26661            for (var breakpointSize in settings.breakpoints) {
26662                var breakpointSizeSettings = settings.breakpoints[breakpointSize];
26663
26664                /* --- Swiper Arrows Navigation --- */
26665                // Swiper does not display arrows by default the way Slick does. We must actively include them to match the defaults of Slick.
26666                var navDisplayClass = breakpointSize == '0' ? 'show-mobile-nav-buttons' : 'show-desktop-nav-buttons';
26667                $swiperNavigation.addClass(navDisplayClass);
26668
26669                // If arrows were deliberately set to false in settings, that means hide them for all breakpoint sizes.
26670                if ('arrows' in settings) {
26671                    if (settings.arrows == false) {
26672                        $swiperNavigation.removeClass('show-mobile-nav-buttons show-desktop-nav-buttons');
26673                    }
26674                // Else if arrows were deliberately set to false for specific breakpoint, then hide for that breakpoint.
26675                } else if ('arrows' in breakpointSizeSettings) {
26676                    if (breakpointSizeSettings.arrows == false) {
26677                        $swiperNavigation.removeClass(navDisplayClass);
26678                    }
26679                }
26680
26681                /* --- Swiper Dots Pagination --- */
26682                // Swiper does not display pagination dots by default the way Slick does. We must actively include them to match the defaults of Slick.
26683                var paginationDisplayClass = breakpointSize == '0' ? 'show-mobile-pagination' : 'show-desktop-pagniation';
26684                $swiperPagination.addClass(paginationDisplayClass);
26685
26686                // If dots were deliberately set to false in settings, that means hide them for all breakpoint sizes.
26687                if ('dots' in settings) {
26688                    if (settings.dots == false) {
26689                        $swiperPagination.removeClass('show-mobile-pagination show-desktop-pagniation');
26690                    }
26691                // Else if dots were deliberately set to false for specific breakpoint, then hide for that breakpoint.
26692                } else if ('dots' in breakpointSizeSettings) {
26693                    if (breakpointSizeSettings.dots == false) {
26694                        $swiperPagination.removeClass(paginationDisplayClass);
26695                    }
26696                }
26697
26698                if ('slidesToShow' in breakpointSizeSettings) {
26699                    slidesPerViewArray.push(parseInt(breakpointSizeSettings.slidesToShow));
26700                }
26701            }
26702        } else {
26703            // Swiper does not display arrows by default the way Slick does. We must actively include them to match the defaults of Slick.
26704            $swiperNavigation.addClass('show-mobile-nav-buttons show-desktop-nav-buttons');
26705
26706            // If arrows were deliberately set to false in settings, that means hide them for all breakpoint sizes.
26707            if ('arrows' in settings) {
26708                if (settings.arrows == false) {
26709                    $swiperNavigation.removeClass('show-mobile-nav-buttons show-desktop-nav-buttons');
26710                }
26711            }
26712
26713            // Swiper does not display pagination dots by default the way Slick does. We must actively include them to match the defaults of Slick.
26714            $swiperPagination.addClass('show-desktop-pagniation show-mobile-pagination');
26715
26716            // If dots were deliberately set to false in settings, that means hide them for all breakpoint sizes.
26717            if ('dots' in settings) {
26718                if (settings.dots == false) {
26719                    $swiperPagination.removeClass('show-desktop-pagniation show-mobile-pagination');
26720                }
26721            }
26722        }
26723        if ('loop' in settings) {
26724            isLoop = settings.loop;
26725        }
26726    }   
26727    
26728    // Content overrides for slides to show
26729    if ($element.attr('data-desktop-slides') || $element.attr('data-mobile-slides')) {
26730        if ($element.attr('data-desktop-slides')) {
26731            slidesPerViewArray.push(parseInt($element.attr('data-desktop-slides')));
26732        }
26733        if ($element.attr('data-mobile-slides')) {
26734            slidesPerViewArray.push(parseInt($element.attr('data-mobile-slides')));
26735        }
26736    }
26737
26738    if (slidesPerViewArray) {
26739        sortedSlidesPerViewArray = slidesPerViewArray.sort(function (a, b) { return a - b; });
26740    }
26741
26742    if (!hasSwiperHTML && originalSlideElements.length > 0) {
26743        $element.addClass('swiper-full-wrapper');
26744
26745        // Einstein CQuotient scripts are mixed in with slides at time of slider initialization. Rework the HTML structure.
26746        if (isEinstein) {
26747            $.each(originalSlideElements, function(index, element) {
26748                if (element.tagName.toUpperCase() === 'SCRIPT') {
26749                    cqScripts.push(element);
26750                } else {
26751                    var formattedSlide;
26752                    if (element.tagName.toUpperCase() === 'DIV') {
26753                        formattedSlide = $(element).addClass('swiper-slide');
26754                    } else {
26755                        formattedSlide = $('<div />', { 'class': 'swiper-slide' }).html($(element).html());
26756                    }
26757                    modifiedSlideElements.push(formattedSlide);
26758                }
26759            });
26760        } else {
26761            // Proceed to modify regular slide elements (non-Einstein)
26762            modifiedSlideElements = originalSlideElements.map(function(element, index) {
26763                if (element.tagName.toUpperCase() === 'DIV') {
26764                    return $(element).addClass('swiper-slide')[0];
26765                } else {
26766                    return $('<div />', { 'class': 'swiper-slide' }).html($(element).html())[0];
26767                }
26768            });
26769        }
26770
26771        // Add additional slides (according to lowest common multiple) to fix loop bug in Swiper
26772        if (modifiedSlideElements && sortedSlidesPerViewArray && isLoop) {
26773            var LCM = leastCommonMultiple(sortedSlidesPerViewArray);
26774            if (sortedSlidesPerViewArray.indexOf(LCM) > -1) {
26775                addSlides = (LCM * 2) - modifiedSlideElements.length;
26776            } else {
26777                addSlides = LCM - modifiedSlideElements.length;
26778            }
26779            if (addSlides < 0) {
26780                var HCM = sortedSlidesPerViewArray.reduce(function (a, b) { return a * b; });
26781                addSlides = HCM - modifiedSlideElements.length;
26782            }
26783            $.each(modifiedSlideElements, function(idx, item) {
26784                if (idx < addSlides) {
26785                    duplicatedSlideElements.push($(item).clone());
26786                }
26787            });
26788        }
26789
26790        // Create final array of slide elements
26791        finalSlides = $.merge(modifiedSlideElements, duplicatedSlideElements);
26792
26793        // Append new swiper HTML elements to swiper wrapper
26794        $swiperWrapper.append(finalSlides);
26795        $swiperNavigation.append($swiperPrevArrow).append($swiperNextArrow);
26796        $swiperParent.append($swiperWrapper).append($swiperPagination);
26797
26798        // Set slide arrows either inside or outside slider container
26799        var arrowsPosition = settings && 'arrowsPosition' in settings ? settings.arrowsPosition : null;
26800        if (arrowsPosition && arrowsPosition == 'inside') {
26801            $swiperParent.append($swiperNavigation).addClass('swiper-inside-navigation');
26802        }
26803
26804        // Replace swiper wrapper contents with new HTML
26805        $element.html($swiperParent);
26806
26807        if (!arrowsPosition || arrowsPosition == 'outside') { // If arrows are outside, we have to wait until swiper wrapper has content in line above before we prepend
26808            $element.prepend($swiperNavigation);
26809        }
26810        if (isEinstein && cqScripts.length > 0) {
26811            $element.append(cqScripts);
26812        }
26813
26814        // Grab the unique classes from all the new elements above and return for further use
26815        swiperClassesForSettings.parent = '#' + uniqueSlider;
26816        swiperClassesForSettings.pagination = '.swiper-pagination-' + uniqueSlider;
26817        swiperClassesForSettings.prev = '.swiper-button-prev-' + uniqueSlider;
26818        swiperClassesForSettings.next = '.swiper-button-next-' + uniqueSlider;
26819    }
26820    return swiperClassesForSettings;
26821}
26822
26823function init (elementClass, settings, isEinstein) {
26824    var selector = elementClass;
26825    if (!elementClass || ($(elementClass).length == 0 && elementClass.indexOf(':has') < 0)) {
26826        return;
26827    }
26828    if (SitePreferences.CONTENT_SLOT_SLIDER == 'swiperJS') { 
26829        $(elementClass).each(function(n, elm) {
26830            var $element = $(elm);
26831            var hasSwiperHTML = $element.find('.swiper-initialized').length > 0;
26832
26833            if (!hasSwiperHTML) {
26834                var classesForSettings = formatSwiperHTML($element, settings, isEinstein);
26835                if (!settings) {
26836                    settings = {};
26837                }
26838                if (classesForSettings) {
26839                    // Override selector class for ID if we have it
26840                    selector = classesForSettings.parent ? classesForSettings.parent : selector;
26841                    settings.pagination = {
26842                        el: classesForSettings.pagination,
26843                        clickable: true
26844                    };
26845                    settings.navigation = {
26846                        nextEl: classesForSettings.next,
26847                        prevEl: classesForSettings.prev
26848                    };    
26849
26850                    // Content overrides for slides to show
26851                    if ($element.attr('data-desktop-slides') || $element.attr('data-mobile-slides')) {
26852                        if ($element.attr('data-desktop-slides')) {
26853                            if (settings.breakpoints && settings.breakpoints[768]) {
26854                                settings.breakpoints[768].slidesToShow = parseInt($element.attr('data-desktop-slides'));
26855                            } else {
26856                                if (!settings.breakpoints) {
26857                                    settings.breakpoints = { 768: { slidesToShow: parseInt($element.attr('data-desktop-slides')) }};
26858                                }
26859                                if (!settings.breakpoints[768]) {
26860                                    settings.breakpoints[768] = { slidesToShow: parseInt($element.attr('data-desktop-slides')) };
26861                                }
26862                            }
26863                        }
26864                        if ($element.attr('data-mobile-slides')) {
26865                            if (settings.breakpoints && settings.breakpoints[0]) {
26866                                settings.breakpoints[0].slidesToShow = parseInt($element.attr('data-mobile-slides'));
26867                            } else {
26868                                if (!settings.breakpoints) {
26869                                    settings.breakpoints = { 0: { slidesToShow: parseInt($element.attr('data-mobile-slides')) }};
26870                                }
26871                                if (!settings.breakpoints[0]) {
26872                                    settings.breakpoints[0] = { slidesToShow: parseInt($element.attr('data-mobile-slides')) };
26873                                }
26874                            }
26875                        }   
26876                    }
26877                }
26878                return initSwiper(selector, settings, classesForSettings);
26879            }
26880        });
26881    } else {
26882        $(elementClass).each(function(n, elm) {
26883            var $element = $(elm);
26884            var elementIdSelector;
26885            formatSlickHTML($element, settings, isEinstein);
26886            elementIdSelector = '#' + $element.attr('id');
26887            initSlick(elementIdSelector, settings);
26888        });
26889    }
26890}
26891
26892function leastCommonMultiple(values) {
26893    function gcd(a, b) {
26894        // Greatest common divisor of 2 integers
26895        return !b ? a : gcd(b, a % b);
26896    }
26897
26898    function lcm(a, b) {
26899        // Least common multiple of 2 integers
26900        return (a * b) / gcd(a, b);   
26901    }
26902
26903    var multiple = values[0];
26904    // Least common multiple of a list of integers
26905    values.forEach(function(n) {
26906        multiple = lcm(multiple, n);
26907    });
26908
26909    // Return least common multiple for array of values
26910    return multiple;
26911}
26912
26913function initializeCategorySliders(elementClass) {
26914	var $categorySliders = $(elementClass);
26915	if ($categorySliders.length > 0) {
26916		$categorySliders.each(function(index, element) {
26917			var $element = $(element);
26918            var categorySliderOptions = {
26919                breakpoints: {}
26920            };
26921            var uniqueSlider = 'category-slider-index-' + getUniqueNumber().toString();
26922            $element.addClass(uniqueSlider);
26923
26924			// Desktop slot config options
26925			var categorySliderDesktopOptions = $element.attr('slider-desktop-options');
26926			if (categorySliderDesktopOptions) {
26927				categorySliderDesktopOptions = JSON.parse(categorySliderDesktopOptions);
26928                categorySliderOptions.breakpoints[categorySliderDesktopOptions.breakpointSize] = categorySliderDesktopOptions;
26929			}
26930			// Mobile slot config options
26931			var categorySliderMobileOptions = $element.attr('slider-mobile-options');
26932			if (categorySliderMobileOptions) {
26933				categorySliderMobileOptions = JSON.parse(categorySliderMobileOptions);
26934                categorySliderOptions.breakpoints[categorySliderMobileOptions.breakpointSize] = categorySliderMobileOptions;
26935			}
26936
26937            init('.' + uniqueSlider, categorySliderOptions);
26938		});
26939	}
26940}
26941
26942function initializeRecommendationSlider() {
26943	initializeSFCCProductRecommendationSliders();
26944
26945	var attemptNumber = 0;
26946	var sliderInitializedClass = SitePreferences.CONTENT_SLOT_SLIDER == 'swiperJS' ? 'swiper-initialized' : 'slick-initialized';
26947    var sliderSelector = '.recomm-prod-anchor-slot .product-recommendations .slider-products-primary';
26948	function recommendationSliderIntervalHandler() {
26949    	var recommendationSliderInitialized;
26950        if (SitePreferences.CONTENT_SLOT_SLIDER == 'swiperJS') {
26951            recommendationSliderInitialized = $(sliderSelector).find('.' + sliderInitializedClass).length > 0;
26952        } else {
26953            recommendationSliderInitialized = $(sliderSelector).filter(function(index) {
26954                return $(this).hasClass(sliderInitializedClass);
26955            }).length > 0;
26956        }
26957
26958		attemptNumber++;
26959		if(recommendationSliderInitialized || attemptNumber > 20) {
26960			$(document).trigger('EinsteinProductRecommendations.Done', $('#product-recommendations'));
26961            if (SitePreferences.CONTENT_SLOT_SLIDER == 'imgViewer2Slick') { 
26962			    $(document).trigger('SlickSliderAccessibilityMods');
26963            }
26964			clearInterval(timer);
26965			return;
26966		}
26967        $(sliderSelector).trigger('init-slider');
26968	}
26969	var timer = setInterval(recommendationSliderIntervalHandler, 500);
26970}
26971
26972function initializeSFCCProductRecommendationSliders() {
26973	var attemptNumber = 0;
26974	var sliderInitializedClass = SitePreferences.CONTENT_SLOT_SLIDER == 'swiperJS' ? 'swiper-initialized' : 'slick-initialized';
26975    var sliderSelector = '.product-recommendations-sfcc .slider-products-primary .slides';
26976	var recommendationSlidersCount = $(sliderSelector).length;
26977	function recommendationSliderIntervalHandler() {
26978		var recommendationSlidersInitialized;
26979        if (SitePreferences.CONTENT_SLOT_SLIDER == 'swiperJS') {
26980            recommendationSlidersInitialized = $(sliderSelector).find('.' + sliderInitializedClass).length == recommendationSlidersCount;
26981        } else {
26982            recommendationSlidersInitialized = $(sliderSelector).filter(function(index) {
26983                return $(this).hasClass(sliderInitializedClass);
26984            }).length == recommendationSlidersCount;
26985        }
26986
26987		attemptNumber++;
26988		if(recommendationSlidersInitialized || attemptNumber > 20) {
26989			$(document).trigger('SFCCProductRecommendations.Done');
26990            if (SitePreferences.CONTENT_SLOT_SLIDER == 'imgViewer2Slick') { 
26991			    $(document).trigger('SlickSliderAccessibilityMods');
26992            }
26993			clearInterval(timer);
26994			return;
26995		}
26996        $(sliderSelector).trigger('init-slider');
26997	}
26998	var timer = setInterval(recommendationSliderIntervalHandler, 500);
26999}
27000},{"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120}],96:[function(require,module,exports){
27001'use strict';
27002
27003var inventory = require('./');
27004
27005var cartInventory = {
27006    setSelectedStore: function (storeId) {
27007        var $selectedStore = $('.store-tile.' + storeId),
27008            $lineItem = $('.cart-row[data-uuid="' + this.uuid + '"]'),
27009            storeAddress = $selectedStore.find('.store-address').html(),
27010            storeStatus = $selectedStore.find('.store-status').data('status'),
27011            storeStatusText = $selectedStore.find('.store-status').text();
27012        this.selectedStore = storeId;
27013
27014        $lineItem.find('.instore-delivery .selected-store-address')
27015            .data('storeId', storeId)
27016            .attr('data-store-id', storeId)
27017            .html(storeAddress);
27018        $lineItem.find('.instore-delivery .selected-store-availability')
27019            .data('status', storeStatus)
27020            .attr('data-status', storeStatus)
27021            .text(storeStatusText);
27022        $lineItem.find('.instore-delivery .delivery-option').removeAttr('disabled').trigger('click');
27023    },
27024    cartSelectStore: function (selectedStore) {
27025        var self = this;
27026        inventory.getStoresInventory(this.uuid).then(function (stores) {
27027            inventory.selectStoreDialog({
27028                stores: stores,
27029                selectedStoreId: selectedStore,
27030                selectedStoreText: Resources.SELECTED_STORE,
27031                continueCallback: function () {},
27032                selectStoreCallback: self.setSelectedStore.bind(self)
27033            });
27034        }).done();
27035    },
27036    setDeliveryOption: function (value, storeId) {
27037        // set loading state
27038        $('.item-delivery-options')
27039            .addClass('loading')
27040            .children().hide();
27041
27042        var data = {
27043            plid: this.uuid,
27044            storepickup: (value === 'store' ? true : false)
27045        };
27046        if (value === 'store') {
27047            data.storepickup = true;
27048            data.storeid = storeId;
27049        } else {
27050            data.storepickup = false;
27051        }
27052        $.ajax({
27053            url: Urls.setStorePickup,
27054            data: data,
27055            success: function () {
27056                // remove loading state
27057                $('.item-delivery-options')
27058                    .removeClass('loading')
27059                    .children().show();
27060            }
27061        });
27062    },
27063    init: function () {
27064        var self = this;
27065        $('.item-delivery-options .set-preferred-store').on('click', function (e) {
27066            e.preventDefault();
27067            self.uuid = $(this).data('uuid');
27068            var selectedStore = $(this).closest('.instore-delivery').find('.selected-store-address').data('storeId');
27069            if (!User.zip) {
27070                inventory.zipPrompt(function () {
27071                    self.cartSelectStore(selectedStore);
27072                });
27073            } else {
27074                self.cartSelectStore(selectedStore);
27075            }
27076        });
27077        $('.item-delivery-options .delivery-option').on('click', function () {
27078            // reset the uuid
27079            var selectedStore = $(this).closest('.instore-delivery').find('.selected-store-address').data('storeId');
27080            self.uuid = $(this).closest('.cart-row').data('uuid');
27081            self.setDeliveryOption($(this).val(), selectedStore);
27082        });
27083    }
27084};
27085
27086module.exports = cartInventory;
27087
27088},{"./":97}],97:[function(require,module,exports){
27089'use strict';
27090
27091var _ = require('lodash'),
27092    dialog = require('../dialog'),
27093    TPromise = require('promise'),
27094    util = require('../util');
27095
27096var newLine = '\n';
27097var storeTemplate = function (store, selectedStoreId, selectedStoreText) {
27098    return [
27099        '<li class="store-tile ' + store.storeId + (store.storeId === selectedStoreId ? ' selected' : '') + '">',
27100        '    <p class="store-address">',
27101        '        ' + store.address1 + '<br/>',
27102        '        ' + store.city + ', ' + store.stateCode + ' ' + store.postalCode,
27103        '    </p>',
27104        '    <p class="store-status" data-status="' + store.statusclass + '">' + store.status + '</p>',
27105        '    <button class="select-store-button" data-store-id="' + store.storeId + '"' +
27106        (store.statusclass !== 'store-in-stock' ? 'disabled="disabled"' : '') + '>',
27107        '        ' + (store.storeId === selectedStoreId ? selectedStoreText : Resources.SELECT_STORE),
27108        '    </button>',
27109        '</li>'
27110    ].join(newLine);
27111};
27112
27113var storeListTemplate = function (stores, selectedStoreId, selectedStoreText) {
27114    if (stores && stores.length) {
27115        return [
27116            '<div class="store-list-container">',
27117            '<ul class="store-list">',
27118            _.map(stores, function (store) {
27119                return storeTemplate(store, selectedStoreId, selectedStoreText);
27120            }).join(newLine),
27121            '</ul>',
27122            '</div>',
27123            '<div class="store-list-pagination">',
27124            '</div>'
27125        ].join(newLine);
27126    } else {
27127        return '<div class="no-results">' + Resources.INVALID_ZIP + '</div>';
27128    }
27129};
27130
27131var zipPromptTemplate = function () {
27132    return [
27133        '<div id="preferred-store-panel">',
27134        '    <input type="text" id="user-zip" placeholder="' + Resources.ENTE
27134R_ZIP + '" name="zipCode"/>',
27135        '</div>'
27136    ].join(newLine);
27137};
27138
27139/**
27140 * @description test whether zipcode is valid for either US or Canada
27141 * @return {Boolean} true if the zipcode is valid for either country, false if it's invalid for both
27142 **/
27143var validateZipCode = function (zipCode) {
27144    var regexes = {
27145            canada: /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ]( )?\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i,
27146            usa: /^\d{5}(-\d{4})?$/
27147        },
27148        valid = false;
27149    if (!zipCode) { return; }
27150    _.each(regexes, function (re) {
27151        var regexp = new RegExp(re);
27152        valid = regexp.test(zipCode);
27153    });
27154    return valid;
27155};
27156
27157var storeinventory = {
27158    zipPrompt: function (callback) {
27159        var self = this;
27160        dialog.open({
27161            html: zipPromptTemplate(),
27162            options: {
27163                title: Resources.STORE_NEAR_YOU,
27164                width: 500,
27165                buttons: [{
27166                    text: Resources.SEARCH,
27167                    click: function () {
27168                        var zipCode = $('#user-zip').val();
27169                        if (validateZipCode(zipCode)) {
27170                            self.setUserZip(zipCode);
27171                            if (callback) {
27172                                callback(zipCode);
27173                            }
27174                        }
27175                    }
27176                }],
27177                open: function () {
27178                    $('#user-zip').on('keypress', function (e) {
27179                        if (e.which === 13) {
27180                            // trigger the search button
27181                            $('.ui-dialog-buttonset .ui-button').trigger('click');
27182                        }
27183                    });
27184                }
27185            }
27186        });
27187    },
27188    getStoresInventory: function (pid) {
27189      return TPromise.resolve($.ajax({
27190          url: util.appendParamsToUrl(Urls.storesInventory, {
27191              pid: pid,
27192              zipCode: User.zip
27193          }),
27194          dataType: 'json'
27195      }));
27196    },
27197    /**
27198     * @description open the dialog to select store
27199     * @param {Array} options.stores
27200     * @param {String} options.selectedStoreId
27201     * @param {String} options.selectedStoreText
27202     * @param {Function} options.continueCallback
27203     * @param {Function} options.selectStoreCallback
27204     **/
27205    selectStoreDialog: function (options) {
27206        var self = this,
27207            stores = options.stores,
27208            selectedStoreId = options.selectedStoreId,
27209            selectedStoreText = options.selectedStoreText,
27210            storeList = storeListTemplate(stores, selectedStoreId, selectedStoreText);
27211        dialog.open({
27212            html: storeList,
27213            options: {
27214                title: Resources.SELECT_STORE + ' - ' + User.zip,
27215                buttons: [{
27216                    text: Resources.CHANGE_LOCATION,
27217                    click: function () {
27218                        self.setUserZip(null);
27219                        // trigger the event to start the process all over again
27220                        $('.set-preferred-store').trigger('click');
27221                    }.bind(this)
27222                }, {
27223                    text: Resources.CONTINUE,
27224                    click: function () {
27225                        if (options.continueCallback) {
27226                            options.continueCallback(stores);
27227                        }
27228                        dialog.close();
27229                    }
27230                }],
27231                open: function () {
27232                    $('.select-store-button').on('click', function (e) {
27233                        e.preventDefault();
27234                        var storeId = $(this).data('storeId');
27235                        // if the store is already selected, don't select again
27236                        if (storeId === selectedStoreId) { return; }
27237                        $('.store-list .store-tile.selected').removeClass('selected')
27238                            .find('.select-store-button').text(Resources.SELECT_STORE);
27239                        $(this).text(selectedStoreText)
27240                            .closest('.store-tile').addClass('selected');
27241                        if (options.selectStoreCallback) {
27242                            options.selectStoreCallback(storeId);
27243                        }
27244                    });
27245                }
27246            }
27247        });
27248    },
27249    setUserZip: function (zip) {
27250        User.zip = zip;
27251
27252        $.ajax({
27253            type: 'POST',
27254            url: Urls.setZipCode,
27255            data: {
27256                zipCode: zip
27257            }
27258        });
27259    },
27260    shippingLoad: function () {
27261        var $checkoutForm = $('.address');
27262        $checkoutForm.off('click');
27263        $checkoutForm.on('click', 'input[name$="_shippingAddress_isGift"]', function () {
27264            $(this).parent().siblings('.gift-message-text').toggleClass('hidden', $('input[name$="_shippingAddress_isGift"]:checked').val());
27265        });
27266    }
27267};
27268
27269module.exports = storeinventory;
27270
27271},{"../dialog":36,"../util":102,"lodash":133,"promise":134}],98:[function(require,module,exports){
27272'use strict';
27273
27274var _ = require('lodash'),
27275    inventory = require('./');
27276
27277var newLine = '\n';
27278var pdpStoreTemplate = function (store) {
27279    return [
27280        '<li class="store-list-item ' + (store.storeId === User.storeId ? ' selected' : '') + '">',
27281        '    <div class="store-address">' + store.address1 + ', ' + store.city + ' ' + store.stateCode +
27282        ' ' + store.postalCode + '</div>',
27283        '    <div class="store-status" data-status="' + store.statusclass + '">' + store.status + '</div>',
27284        '</li>'
27285    ].join(newLine);
27286};
27287var pdpStoresListingTemplate = function (stores) {
27288    if (stores && stores.length) {
27289        return [
27290            '<div class="store-list-pdp-container">',
27291            (stores.length > 1 ? '    <a class="stores-toggle collapsed" href="#">' + Resources.SEE_MORE + '</a>' : ''),
27292            '    <ul class="store-list-pdp">',
27293            _.map(stores, pdpStoreTemplate).join(newLine),
27294            '    </ul>',
27295            '</div>'
27296        ].join(newLine);
27297    }
27298};
27299
27300var storesListing = function (stores) {
27301    // list all stores on PDP page
27302    if ($('.store-list-pdp-container').length) {
27303        $('.store-list-pdp-container').remove();
27304    }
27305    $('.availability-results').append(pdpStoresListingTemplate(stores));
27306};
27307
27308var productInventory = {
27309    setPreferredStore: function (storeId) {
27310        User.storeId = storeId;
27311        $.ajax({
27312            url: Urls.setPreferredStore,
27313            type: 'POST',
27314            data: {storeId: storeId}
27315        });
27316    },
27317    productSelectStore: function () {
27318        var self = this;
27319        inventory.getStoresInventory(this.pid).then(function (stores) {
27320            inventory.selectStoreDialog({
27321                stores: stores,
27322                selectedStoreId: User.storeId,
27323                selectedStoreText: Resources.PREFERRED_STORE,
27324                continueCallback: storesListing,
27325                selectStoreCallback: self.setPreferredStore
27326            });
27327        }).done();
27328    },
27329    init: function () {
27330        var $availabilityContainer = $('.availability-results'),
27331            self = this;
27332        this.pid = $('input[name="pid"]').val();
27333
27334        $('#product-content .set-preferred-store').on('click', function (e) {
27335            e.preventDefault();
27336            if (!User.zip) {
27337                inventory.zipPrompt(function () {
27338                    self.productSelectStore();
27339                });
27340            } else {
27341                self.productSelectStore();
27342            }
27343        });
27344
27345        if ($availabilityContainer.length) {
27346            if (User.storeId) {
27347                inventory.getStoresInventory(this.pid).then(storesListing);
27348            }
27349
27350            // See more or less stores in the listing
27351            $availabilityContainer.on('click', '.stores-toggle', function (e) {
27352                e.preventDefault();
27353                $('.store-list-pdp .store-list-item').toggleClass('visible');
27354                if ($(this).hasClass('collapsed')) {
27355                    $(this).text(Resources.SEE_LESS);
27356                } else {
27357                    $(this).text(Resources.SEE_MORE);
27358                }
27359                $(this).toggleClass('collapsed');
27360            });
27361        }
27362    }
27363};
27364
27365module.exports = productInventory;
27366
27367},{"./":97,"lodash":133}],99:[function(require,module,exports){
27368'use strict';
27369
27370var render = function(styleCountCache) {
27371	if (styleCountCache) {
27372		$('[data-link-category]').each(function() {
27373			var categoryId = $(this).data('link-category');
27374			
27375			if (categoryId) {
27376				var categoryRefinements = styleCountCache.CategoryLinks[categoryId];
27377				if (categoryRefinements && categoryRefinements.count > 0) {
27378					var $styleCount = $(this).find('.style-number'); 
27379					var messageTemplate = categoryRefinements.count > 1 ? Resources.STYLE_PLURAL : Resources.STYLE_SINGULAR;
27380					var message = messageTemplate.replace('{0}', categoryRefinements.count);
27381					$styleCount.addClass('visible').text(message);
27382
27383					return;
27384				}	
27385			}
27386
27387			$(this).closest('li').remove();
27388		});
27389
27390		$('[data-link-attribute-id]').each(function() {
27391			var categoryId = $(this).data('link-cgid'),
27392			refinementId = $(this).data('link-attribute-id'),
27393			refinementValue = $(this).data('link-attribute-value');
27394
27395			var categoryRefinements = styleCountCache.RefinementLinks[categoryId];
27396			if (categoryRefinements) {
27397				var attributeRefinements = categoryRefinements[refinementId];
27398				if (attributeRefinements) {
27399					var attributeRefinement = attributeRefinements[refinementValue];
27400					
27401					if (attributeRefinement && attributeRefinement.count > 0) {
27402						var $styleCount = $(this).find('.style-number');
27403						var messageTemplate = attributeRefinement.count > 1 ? Resources.STYLE_PLURAL : Resources.STYLE_SINGULAR;
27404						var message = messageTemplate.replace('{0}', attributeRefinement.count);
27405						$styleCount.addClass('visible').text(message);
27406						
27407						return;
27408					}
27409
27410				}         		
27411			}
27412			
27413			$(this).closest('li').remove();
27414		});
27415	}
27416}
27417
27418module.exports.render = render;
27419},{}],100:[function(require,module,exports){
27420'use strict';
27421
27422/**
27423 * Checks the TLS and displays a warning if appropriate
27424 * @function getUserAgent Checks the TLS and displays a warning if appropriate
27425 **/
27426function getUserAgent() {
27427    // Use an external service to check the TLS of the browser
27428    // NOTE: this implementation uses https://www.howsmyssl.com
27429    //     you may also wish to consider the API available at https://www.ssllabs.com/projects/ssllabs-apis/index.html
27430    var url = 'https://www.howsmyssl.com/a/check';
27431    var cookieName = 'dw_TLSWarning';
27432    var cookieValue = getCookie(cookieName);
27433
27434    // Test to see if this browser has already been flagged by looking at its cookies
27435    if (!cookieValue) {
27436        getTLS(url, function (message) {
27437            if (message.length > 0) {
27438                showWarning(message[0]);
27439
27440                // the browser is bad - set the cookie to true (for 15 minutes)
27441                setCookie(cookieName, 'true', 15);
27442            } else {
27443                // else the browser is good, set the cookie to false (for 30 days) so we don't check again
27444                setCookie(cookieName, 'false', 60 * 24 * 30);
27445            }
27446        });
27447    } else if (cookieValue === 'true') {
27448        // if we already know that this is an invalid browser, show the warning
27449        showWarning(Resources.TLS_WARNING);
27450    }
27451}
27452
27453/**
27454 * Calls out to the TLS service and calls the callback with a message (if necessary)
27455 * @function getTLS
27456 *
27457 * @param {string} url - URL of external TLS-checking API
27458 * @param {function} callback - function to call with response
27459 **/
27460function getTLS(url, callback) {
27461    var message = [];
27462
27463    // First, see if the browser is among the suspect browsers to see if a TLS check is necessary
27464    var userAgent = navigator.userAgent;
27465
27466    /** This list derived from https://www.ssllabs.com/ssltest/clients.html **/
27467    var badBrowsers = ['MSIE 6.0','MSIE 7.0','MSIE 8.0','MSIE 9.0','MSIE 10.0',
27468                       'Android 2.3.7', 'Android 4.0.4', 'Android 4.1.1', 'Android 4.2.2', 'Android 4.3',
27469                       'Safari 5.1.9 / OS X 10.6.8', 'Safari 6.0.4 / OS X 10.8.4 '];
27470    function checkTLSLevel(data) {
27471        // If we can determine the TLS level, check to see if it's less than 1.2
27472        if (parseFloat(data.tls_version.split(' ')[1]) < 1.1) {
27473            message.push(Resources.TLS_WARNING);
27474            callback(message);
27475
27476            //If you want to track statistics on bad TLS hits, include this call
27477            $.ajax({url: Urls.TLSBadTLS});
27478        }
27479    }
27480
27481    function reportBadBrowser () {
27482        // If the TLS level cannot be determined just report that this browser is suspect
27483        message.push(Resources.TLS_WARNING);
27484        callback(message);
27485
27486        //If you want to track statistics on deprecated browsers, include this call
27487        $.ajax({url: Urls.TLSBadBrowser});
27488    }
27489
27490    for (var i = 0; i < badBrowsers.length; i++) {
27491        if (userAgent.match(badBrowsers[i])) {
27492            // It's a suspect browser, let's see what it's TLS level is
27493            $.ajax({
27494                url: url
27495            }).done(checkTLSLevel).fail(reportBadBrowser);
27496            break;
27497        }
27498    }
27499
27500    /** For testing purposes, uncomment this block
27501        message.push(Resources.TLS_WARNING);
27502    **/
27503    callback(message);
27504}
27505
27506/**
27507 * @function showWarning turns on the browser-compatibility-alert and sets the message
27508 *
27509 * @param {string} message - the message that will be shown upon detection of a bad browser
27510 **/
27511function showWarning(message) {
27512    $('<div/>').addClass('browser-compatibility-alert').append($('<p/>').addClass('browser-error').html(message)).appendTo('#browser-check');
27513}
27514
27515/**
27516 * @function getCookie
27517 *
27518 * @param {string} key - The cookie name
27519 * @returns {string} value - the value of the cookie if found, null otherwise
27520 **/
27521function getCookie(key) {
27522    var cookies = document.cookie.split(';');
27523    for (var i = 0; i < cookies.length; i++) {
27524        var tokens = cookies[i].split('=');
27525        var cookieKey = tokens[0].trim();
27526        if (cookieKey === key) {
27527            return tokens[1];
27528        }
27529    }
27530    return '';
27531}
27532
27533/**
27534 * @function setCookie
27535 *
27536 * @param {string} key - The cookie name
27537 * @param {string} value - The cookie value
27538 * @param {integer} minutes - The number of minutes to expire the cookie
27539 **/
27540function setCookie (key, value, minutes) {
27541    var date = new Date();
27542    date.setTime(date + (minutes * 60 * 1000));
27543
27544    document.cookie = key + '=' + value + '; expires=' + date.toGMTString() + '; path=/';
27545}
27546
27547/**
27548 * Export the getUserAgent function
27549 */
27550
27551exports.getUserAgent = getUserAgent;
27552
27553},{}],101:[function(require,module,exports){
27554'use strict';
27555
27556/**
27557 * @function
27558 * @description Initializes the tooltip-content and layout
27559 */
27560exports.init = function(optionsParameter, $containerParameter) {
27561	var defaultOptions = {
27562        items: '.tooltip',
27563        track: true,
27564        content: function () {
27565            return $(this).find('.tooltip-content').html();
27566        }
27567    };
27568	
27569	var options;
27570	if (optionsParameter) {
27571		options = $.extend(defaultOptions, optionsParameter);
27572	} else {
27573		options = defaultOptions;
27574	}
27575	
27576	var $container;
27577	if ($containerParameter) {
27578		$container = $containerParameter;
27579	} else {
27580		$container = $(document);
27581	}
27582	
27583    $container.tooltip(options);
27584    
27585    $(document).off('click', '.tooltip').on('click', '.tooltip', function(e) {
27586    	e.preventDefault();
27587    });
27588    
27589    $(document).off('click', '.share-link').on('click', '.share-link', function(e) {
27590        e.preventDefault();
27591        var target = $(this).data('target');
27592        if (target) {
27593            $(target).toggleClass('active');
27594        }
27595    });
27596};
27597
27598},{}],102:[function(require,module,exports){
27599'use strict';
27600
27601var _ = require('lodash');
27602var PaymentMethodConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
27603var CreditCardConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').CreditCards;
27604
27605var util = {
27606    /**
27607     * @function
27608     * @description appends the parameter with the given name and value to the given url and returns the changed url
27609     * @param {String} url the url to which the parameter will be added
27610     * @param {String} name the name of the parameter
27611     * @param {String} value the value of the parameter
27612     */
27613    appendParamToURL: function (url, name, value) {
27614        // quit if the param already exists
27615        if (url.indexOf(name + '=') !== -1) {
27616            return url;
27617        }
27618        var separator = url.indexOf('?') !== -1 ? '&' : '?';
27619		var anchor = '';
27620		if (url.indexOf('#') > -1) {
27621            var urlAndAnchor = url.split('#');
27622			url = urlAndAnchor[0];
27623			anchor = '#' + urlAndAnchor[1];
27624        }
27625        return url + separator + name + (value ? '=' + encodeURIComponent(value) : '') + anchor;
27626    },
27627
27628    /**
27629     * @function
27630     * @description remove the parameter and its value from the given url and returns the changed url
27631     * @param {String} url the url from which the parameter will be removed
27632     * @param {String} name the name of parameter that will be removed from url
27633     */
27634    removeParamFromURL: function (url, name) {
27635        if (url.indexOf('?') === -1 || url.indexOf(name + '=') === -1) {
27636            return url;
27637        }
27638        var hash;
27639        var params;
27640        var domain = url.split('?')[0];
27641        var paramUrl = url.split('?')[1];
27642        var newParams = [];
27643        // if there is a hash at the end, store the hash
27644        if (paramUrl.indexOf('#') > -1) {
27645            hash = paramUrl.split('#')[1] || '';
27646            paramUrl = paramUrl.split('#')[0];
27647        }
27648        params = paramUrl.split('&');
27649        for (var i = 0; i < params.length; i++) {
27650            // put back param to newParams array if it is not the one to be removed
27651            if (params[i].split('=')[0] !== name) {
27652                newParams.push(params[i]);
27653            }
27654        }
27655        var finalUrl = domain;
27656        if (newParams.length > 0) {
27657            finalUrl += '?' + newParams.join('&');
27658        }
27659        if (hash) {
27660            finalUrl += '#' + hash;
27661        }
27662        return finalUrl;
27663    },
27664
27665    /**
27666     * @function
27667     * @description appends the parameters to the given url and returns the changed url
27668     * @param {String} url the url to which the parameters will be added
27669     * @param {Object} params
27670     */
27671    appendParamsToUrl: function (url, params) {
27672        var _url = url;
27673        _.each(params, function (value, name) {
27674            _url = this.appendParamToURL(_url, name, value);
27675        }.bind(this));
27676        return _url;
27677    },
27678    /**
27679     * @function
27680     * @description extract the query string from URL
27681     * @param {String} url the url to extra query string from
27682     **/
27683    getQueryString: function (url) {
27684        var qs;
27685        if (!_.isString(url)) { return; }
27686        var a = document.createElement('a');
27687        a.href = url;
27688        if (a.search) {
27689            qs = a.search.substr(1); // remove the leading ?
27690        }
27691        return qs;
27692    },
27693    /**
27694     * @function
27695     * @description
27696     * @param {String}
27697     * @param {String}
27698     */
27699    elementInViewport: function (el, offsetToTop) {
27700        var top = el.offsetTop,
27701            left = el.offsetLeft,
27702            width = el.offsetWidth,
27703            height = el.offsetHeight;
27704
27705        while (el.offsetParent) {
27706            el = el.offsetParent;
27707            top += el.offsetTop;
27708            left += el.offsetLeft;
27709        }
27710
27711        if (typeof(offsetToTop) !== 'undefined') {
27712            top -= offsetToTop;
27713        }
27714
27715        if (window.pageXOffset !== null) {
27716            return (
27717                top < (window.pageYOffset + window.innerHeight) &&
27718                left < (window.pageXOffset + window.innerWidth) &&
27719                (top + height) > window.pageYOffset &&
27720                (left + width) > window.pageXOffset
27721            );
27722        }
27723
27724        if (document.compatMode === 'CSS1Compat') {
27725            return (
27726                top < (window.document.documentElement.scrollTop + window.document.documentElement.clientHeight) &&
27727                left < (window.document.documentElement.scrollLeft + window.document.documentElement.clientWidth) &&
27728                (top + height) > window.document.documentElement.scrollTop &&
27729                (left + width) > window.document.documentElement.scrollLeft
27730            );
27731        }
27732    },
27733
27734    /**
27735     * @function
27736     * @description Appends the parameter 'format=ajax' to a given path
27737     * @param {String} path the relative path
27738     */
27739    ajaxUrl: function (path) {
27740        return this.appendParamToURL(path, 'format', 'ajax');
27741    },
27742
27743    /**
27744     * @function
27745     * @description
27746     * @param {String} url
27747     */
27748    toAbsoluteUrl: function (url) {
27749        if (url.indexOf('http') !== 0 && url.charAt(0) !== '/') {
27750            url = '/' + url;
27751        }
27752        return url;
27753    },
27754    /**
27755     * @function
27756     * @description Loads css dynamically from given urls
27757     * @param {Array} urls Array of urls from which css will be dynamically loaded.
27758     */
27759    loadDynamicCss: function (urls) {
27760        var i, len = urls.length;
27761        for (i = 0; i < len; i++) {
27762            this.loadedCssFiles.push(this.loadCssFile(urls[i]));
27763        }
27764    },
27765
27766    /**
27767     * @function
27768     * @description Loads css file dynamically from given url
27769     * @param {String} url The url from which css file will be dynamically loaded.
27770     */
27771    loadCssFile: function (url) {
27772        return $('<link/>').appendTo($('head')).attr({
27773            type: 'text/css',
27774            rel: 'stylesheet'
27775        }).attr('href', url); // for i.e. <9, href must be added after link has been appended to head
27776    },
27777    // array to keep track of the dynamically loaded CSS files
27778    loadedCssFiles: [],
27779
27780    /**
27781     * @function
27782     * @description Removes all css files which were dynamically loaded
27783     */
27784    clearDynamicCss: function () {
27785        var i = this.loadedCssFiles.length;
27786        while (0 > i--) {
27787            $(this.loadedCssFiles[i]).remove();
27788        }
27789        this.loadedCssFiles = [];
27790    },
27791    /**
27792     * @function
27793     * @description Extracts all parameters from a given query string into an object
27794     * @param {String} qs The query string from which the parameters will be extracted
27795     */
27796    getQueryStringParams: function (qs) {
27797        if (!qs || qs.length === 0) { return {}; }
27798        var params = {},
27799            unescapedQS = unescape(qs);
27800        // Use the String::replace method to iterate over each
27801        // name-value pair in the string.
27802        unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'),
27803            function ($0, $1, $2, $3) {
27804                params[$1] = $3;
27805            }
27806        );
27807        return params;
27808    },
27809
27810    fillAddressFields: function (address, $form) {
27811        for (var field in address) {
27812            if (field === 'UUID' || field === 'key') {
27813                continue;
27814            }
27815            // if the key in address object ends with 'Code', remove that suffix
27816            // keys that ends with 'Code' are postalCode, stateCode and countryCode
27817            // update the state fields
27818            if (field === 'countryCode') {
27819            	$form.find('[name$="country"]').val(address['countryCode'].toUpperCase()).trigger('change');
27820                // retrigger state selection after country has changed
27821                // this results in duplication of the state code, but is a 
27821necessary evil
27822                // for now because sometimes countryCode comes after stateCode
27823                            	
27824            	var selectedStateValue = address['stateCode'];
27825            	if ($form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').length > 0) {
27826            		selectedStateValue = $form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').val();
27827            	}
27828            	
27829            	$form.find('[name$="state"]').val(selectedStateValue);
27830            } else if (field === 'stateCode') {
27831				var selectedStateValue = address['stateCode'];
27832            	if ($form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').length > 0) {
27833            		selectedStateValue = $form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').val();
27834            	}
27835            	
27836            	$form.find('[name$="state"]').val(selectedStateValue);
27837            } else {
27838            	if (address[field] != null) {
27839            		$form.find('[name$="_' + field.replace('Code', '') + '"]').val((''+address[field]).replace(/\^/gi,"'"));
27840            	} else {
27841            		$form.find('[name$="_' + field.replace('Code', '') + '"]').val(address[field]);
27842            	}
27843            }
27844        }
27845    },
27846    /**
27847     * @function
27848     * @description Updates the number of the remaining character
27849     * based on the character limit in a text area
27850     */
27851    limitCharacters: function () {
27852        $('form').find('textarea[data-character-limit]').each(function () {
27853            var characterLimit = $(this).data('character-limit');
27854            var charCountHtml = String.format(Resources.CHAR_LIMIT_MSG,
27855                '<span class="char-remain-count">' + characterLimit + '</span>',
27856                '<span class="char-allowed-count">' + characterLimit + '</span>');
27857            var charCountContainer = $(this).next('div.char-count');
27858            if (charCountContainer.length === 0) {
27859                charCountContainer = $('<div class="char-count"/>').insertAfter($(this));
27860            }
27861            charCountContainer.html(charCountHtml);
27862            // trigger the keydown event so that any existing character data is calculated
27863            $(this).change();
27864        });
27865    },
27866    /**
27867     * @function
27868     * @description Binds the onclick-event to a delete button on a given container,
27869     * which opens a confirmation box with a given message
27870     * @param {String} container The name of element to which the function will be bind
27871     * @param {String} message The message the will be shown upon a click
27872     */
27873    setDeleteConfirmation: function (container, message) {
27874        $(container).on('click', '.delete', function () {
27875            return window.confirm(message);
27876        });
27877    },
27878    /**
27879     * @function
27880     * @description Scrolls a browser window to a given x point
27881     * @param {String} The x coordinate
27882     */
27883    scrollBrowser: function (xLocation) {
27884        $('html, body').animate({scrollTop: xLocation}, 500);
27885    },
27886    
27887    isTouchDevice: function() {
27888    	//detect touch-ability (maxTouchPoints for windows surface)
27889    	return ('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints > 0);
27890    },
27891
27892    isMobile: function () {
27893        var mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];
27894        var    idx = 0;
27895        var isMobile = false;
27896        var userAgent = (navigator.userAgent).toLowerCase();
27897
27898        while (mobileAgentHash[idx] && !isMobile) {
27899            isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);
27900            idx++;
27901        }
27902        return isMobile;
27903    },
27904    
27905    /**
27906     * @function
27907     * @description Validates e-mail address
27908     * @param {String} E-mail address
27909     * @returns {Boolean}
27910     */
27911    validateEmail: function (email) {
27912        var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
27913        return re.test(email);
27914    },
27915
27916    /**
27917     * @function
27918     * @description Helper method to encode inputs and prevent xss
27919     * @param {String} input value
27920     * @returns {String}
27921     */
27922    htmlEncode: function (value) { 
27923        if (value) {
27924            return jQuery('<div/>').text(value).html(); 
27925        } else {
27926            return '';
27927        }
27928    },
27929    
27930    /**
27931     * Limits a text input to a defined length. 
27932     * @param (String) inputSelector - A jQuery selector for the input to limit.
27933     * @param (Number) lengthLimit - The length to limit the input to.
27934     * @return void
27935     */
27936    limitInputToLength: function (inputSelector, lengthLimit) {
27937        $(inputSelector).on("keyup click", function () {
27938            if (this.value.length > lengthLimit) {
27939                this.value = this.value.substring(0, lengthLimit);
27940    		}
27941    	});
27942    },
27943    
27944    /**
27945     * Detects the credit card type based off of the provided credit card number.
27946     * @param (String) creditCardNumber - The credit card number to detect the type for.
27947     * @return (String) - The type of credit card.
27948     */
27949    getCreditCardType: function (creditCardNumber) {
27950        creditCardNumber = creditCardNumber.replace(/\D/g, '');
27951        
27952        if (creditCardNumber.length >= 4) {
27953	        var creditCardTypes = [
27954	        	{
27955	        		PaymentCard: CreditCardConstants.Visa,
27956	        		PaymentCardDisplayKey: CreditCardConstants.Visa,
27957	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27958	        		Pattern: new RegExp('^4[0-9]{0,}$')
27959	        	},
27960	        	{
27961	        		PaymentCard: CreditCardConstants.AmericanExpress,
27962	        		PaymentCardDisplayKey: CreditCardConstants.AmericanExpress,
27963	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27964	        		Pattern: new RegExp('^3[47][0-9]{0,}$')
27965	        	},
27966	        	{
27967	        		PaymentCard: CreditCardConstants.Diners,
27968	        		PaymentCardDisplayKey: CreditCardConstants.Discover,
27969	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27970	        		Pattern: new RegExp('^3(?:0[0-59]{1}|[689])[0-9]{0,}$')
27971	        	},
27972	        	{
27973	        		PaymentCard: CreditCardConstants.JCB,
27974	        		PaymentCardDisplayKey: CreditCardConstants.JCB,
27975	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27976	        		Pattern: new RegExp('^(?:2131|1800|35)[0-9]{0,}$')
27977	        	},
27978	        	{
27979	        		PaymentCard: CreditCardConstants.Mastercard,
27980	        		PaymentCardDisplayKey: CreditCardConstants.Mastercard,
27981	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27982	        		Pattern: new RegExp('^(5[1-5])[0-9]{0,}$')
27983	        	},
27984				{
27985	        		PaymentCard: CreditCardConstants.MastercardSeries2,
27986	        		PaymentCardDisplayKey: CreditCardConstants.Mastercard,
27987	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27988	        		Pattern: new RegExp('^(222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$')
27989	        	},
27990	        	{
27991	        		PaymentCard: CreditCardConstants.Discover,
27992	        		PaymentCardDisplayKey: CreditCardConstants.Discover,
27993	        		PaymentMethod: PaymentMethodConstants.CreditCard,
27994	        		Pattern: new RegExp('^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$')
27995	        	},
27996	        	{
27997	        		PaymentCard: CreditCardConstants.Maestro,
27998	        		PaymentCardDisplayKey: CreditCardConstants.Mastercard,
27999	        		PaymentMethod: PaymentMethodConstants.CreditCard,
28000	        		Pattern: new RegExp('^(5[06789]|6)[0-9]{0,}$')
28001	        	},
28002	        	{
28003	        		PaymentCard: CreditCardConstants.BootBarnCredit,
28004	        		PaymentCardDisplayKey: PaymentMethodConstants.AllianceData,
28005	        		PaymentMethod: PaymentMethodConstants.AllianceData,
28006	        		Pattern: new RegExp('^7788[0-9]{0,}$')
28007	        	}
28008	        ]
28009	        
28010	        for (var creditCardTypeKey in creditCardTypes) {
28011	        	var creditCardType = creditCardTypes[creditCardTypeKey];
28012	        	
28013	        	if (creditCardType.Pattern && creditCardNumber.match(creditCardType.Pattern)) {
28014	        		return creditCardType;
28015	        	}
28016	        }
28017        }
28018
28019        return false;
28020    },
28021
28022    /**
28023     * Masks a provided number.
28024     * @param (Number or String) number - The number to mask.
28025     * @param (Number) lengthToShow - The length of the number to leave unmasked at end.
28026     * @param (String) maskCharacter - The character to use for masking. Defaults to *.
28027     * @return (String) - The type of credit card.
28028     */
28029    getMaskedNumber: function (number, lengthToShow, maskPattern) {
28030    	if (!number) {
28031    		return '';	
28032    	}
28033    	
28034    	lengthToShow = lengthToShow || 4;
28035    	maskPattern = maskPattern || '*';
28036       	
28037       	var numberMasked;
28038    	if (maskPattern.length > 1) {
28039    		numberMasked = maskPattern;
28040    	} else if (maskPattern.length >= 1) {
28041    		numberMasked = new Array(number.length + 1).join(maskPattern);
28042    	}
28043            
28044    	number += '';
28045    	if (number != null && number != '') { 
28046    		numberMasked = numberMasked.substr(0, number.length - lengthToShow); 
28047    		numberMasked += number.substr(number.length - lengthToShow);
28048    	}
28049        
28050        return numberMasked;
28051    },
28052
28053	getPreferenceValueFromWindowLocation: function (preferenceName) {
28054		var hash = window.location.hash.substr(1),
28055			preferenceValue = '',
28056			hashTable = hash.split('&').reduce(function (hashTable, item) {
28057				var parts = item.split('=');
28058				hashTable[parts[0]] = decodeURIComponent(parts[1]).replace(/[\+\s]+/g, ' ');
28059				return hashTable;
28060			}, {});
28061		
28062		for (var i = 1; i <= 5; i++){
28063			if(!hashTable.hasOwnProperty('prefn' + i)){   	
28064				break;
28065			} else if(hashTable['prefn' + i] == preferenceName){
28066				preferenceValue = hashTable['prefv' + i];
28067				break;
28068			}
28069		}
28070		
28071		return preferenceValue;
28072	}
28073};
28074
28075module.exports = util;
28076
28077},{"../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"lodash":133}],103:[function(require,module,exports){
28078'use strict';
28079
28080var naPhone = /^\+?\(?([0-9][0-8][0-9])\)?[\-\. ]?([2-9][0-9]{2})[\-\. ]?([0-9]{4})(\s*x[0-9]+)?$/;
28081var internationalNumber = /^(?:\s*\+)?[\d\s\.\-\(\)]+$/;
28082var defaultPostal = /^\d{3,7}$/;
28083var regex = {
28084    phone: {
28085        us: naPhone,
28086        ca: internationalNumber,
28087        fr: internationalNumber,
28088        it: internationalNumber,
28089        jp: internationalNumber,
28090        cn: /.*/,
28091        gb: internationalNumber
28092    },
28093    postal: {
28094        us: /^\d{5}(-\d{4})?$/,
28095        ca: /^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$/i,
28096        fr: /^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/,
28097        it: /^([0-9]){5}$/,
28098        jp: /^([0-9]){3}[-]([0-9]){4}$/,
28099        cn: /^([0-9]){6}$/,
28100        gb: /^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/i
28101    },
28102    notCC: /^(?!(([0-9 -]){13,19})).*$/
28103};
28104// global form validator settings
28105var settings = {
28106    errorClass: 'error',
28107    errorElement: 'span',
28108    onkeyup: false,
28109    onfocusout: function (element) {
28110        if (!this.checkable(element)) {
28111            this.element(element);
28112        }
28113    },
28114    errorPlacement: function (error, element) {
28115        if (element.attr('error-container')) {
28116            error.appendTo($(element.attr('error-container')));
28117        } else {
28118            error.appendTo(element.parent());
28119        }
28120    }
28121};
28122/**
28123 * @function
28124 * @description Validates a given phone number against the countries phone regex
28125 * @param {String} value The phone number which will be validated
28126 * @param {String} el The input field
28127 */
28128var validatePhone = function (value, el) {
28129    var country = $(el).closest('form').find('.country');
28130    var countryValue;
28131    if (country.length > 0) {
28132        countryValue = country.val();
28133    }
28134
28135    if (!countryValue || !regex.phone[country.val().toLowerCase()]) {
28136        return true;
28137    }
28138
28139    var rgx = regex.phone[country.val().toLowerCase()];
28140    var isOptional = this.optional(el);
28141    var isValid = rgx.test($.trim(value));
28142
28143    return isOptional || isValid;
28144};
28145
28146var validatePostal = function (value, el) {
28147    var country = $(el).closest('form').find('.country');
28148    var countryValue;
28149    if (country.length > 0) {
28150        countryValue = country.val();
28151    }
28152
28153    if (!countryValue || !regex.postal[country.val().toLowerCase()]) {
28154        return true;
28155    }
28156
28157    var rgx = regex.postal[countryValue.toLowerCase()];
28158    var isOptional = this.optional(el);
28159    var isValid = rgx.test($.trim(value));
28160
28161    return isOptional || isValid;
28162};
28163/**
28164 * @function
28165 * @description Validates that a credit card owner is not a Credit card number
28166 * @param {String} value The owner field which will be validated
28167 * @param {String} el The input field
28168 */
28169var validateOwner = function (value) {
28170    var isValid = regex.notCC.test($.trim(value));
28171    return isValid;
28172};
28173
28174/**
28175 * Add phone validation method to jQuery validation plugin.
28176 * Text fields must have 'phone' css class to be validated as phone
28177 */
28178$.validator.addMethod('phone', validatePhone, Resources.INVALID_PHONE);
28179$.validator.addMethod('postal', validatePostal, Resources.INVALID_ZIP);
28180
28181/**
28182 * Add CCOwner validation method to jQuery validation plugin.
28183 * Text fields must have 'owner' css class to be validated as not a credit card
28184 */
28185$.validator.addMethod('owner', validateOwner, Resources.INVALID_OWNER);
28186
28187/**
28188 * Add gift cert amount validation method to jQuery validation plugin.
28189 * Text fields must have 'gift-cert-amont' css class to be validated
28190 */
28191$.validator.addMethod('gift-cert-amount', function (value, el) {
28192    var isOptional = this.optional(el);
28193    var isValid = (!isNaN(value)) && (parseFloat(value) >= 5) && (parseFloat(value) <= 5000);
28194    return isOptional || isValid;
28195}, Resources.GIFT_CERT_AMOUNT_INVALID);
28196
28197/**
28198 * Add positive number validation method to jQuery validation plugin.
28199 * Text fields must have 'positivenumber' css class to be validated as positivenumber
28200 */
28201$.validator.addMethod('positivenumber', function (value) {
28202    if ($.trim(value).length === 0) { return true; }
28203    return (!isNaN(value) && Number(value) >= 0);
28204}, Resources.VALIDATE_NUMBER);
28205
28206/**
28207 * Add method to check for email extension when validating emails,
28208 * since default settings don't require an extension
28209 */
28210$.validator.addMethod("requireEmailExtension", function(value, element) {
28211    if ($.trim(value).length === 0) { return true; }
28212    return /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value);
28213}, Resources.VALIDATE_EMAIL);
28214
28215/*
28216 * Lets you say "at least X inputs that match selector Y must be filled."
28217 *
28218 * The end result is that neither of these inputs:
28219 *
28220 *  <input class="productinfo" name="partnumber">
28221 *  <input class="productinfo" name="description">
28222 *
28223 *  ...will validate unless at least one of them is filled.
28224 *
28225 * partnumber:  {require_from_group: [1,".productinfo"]},
28226 * description: {require_from_group: [1,".productinfo"]}
28227 *
28228 * options[0]: number of fields that must be filled in the group
28229 * options[1]: CSS selector that defines the group of conditionally required fields
28230 */
28231$.validator.addMethod( "require_from_group", function( value, element, options ) {
28232    var $fields = $(options[1], element.form);
28233    var $fieldsFirst = $fields.eq(0);
28234    var validator = $fieldsFirst.data("valid_req_grp") ? $fieldsFirst.data("valid_req_grp") : $.extend({}, this);
28235    var isValid = $fields.filter(function() {
28236        return validator.elementValue(this);
28237    }).length >= options[0];
28238
28239    // Store the cloned validator for future validation
28240    $fieldsFirst.data("valid_req_grp", validator);
28241
28242    // If element isn't being validated, run each require_from_group field's validation rules
28243    if (!$(element).data("being_validated")) {
28244        $fields.data("being_validated", true);
28245        $fields.each(function() {
28246            validator.element(this);
28247        });
28248        $fields.data("being_validated", false);
28249    }
28250    return isValid;
28251}, $.validator.format("Please fill at least {0} of these fields."));
28252
28253$.extend($.validator.messages, {
28254    required: Resources.VALIDATE_REQUIRED,
28255    remote: Resources.VALIDATE_REMOTE,
28256    email: Resources.VALIDATE_EMAIL,
28257    url: Resources.VALIDATE_URL,
28258    date: Resources.VALIDATE_DATE,
28259    dateISO: Resources.VALIDATE_DATEISO,
28260    number: Resources.VALIDATE_NUMBER,
28261    digits: Resources.VALIDATE_DIGITS,
28262    creditcard: Resources.VALIDATE_CREDITCARD,
28263    equalTo: Resources.VALIDATE_EQUALTO,
28264    maxlength: $.validator.format(Resources.VALIDATE_MAXLENGTH),
28265    minlength: $.validator.format(Resources.VALIDATE_MINLENGTH),
28266    rangelength: $.validator.format(Resources.VALIDATE_RANGELENGTH),
28267    range: $.validator.format(Resources.VALIDATE_RANGE),
28268    max: $.validator.format(Resources.VALIDATE_MAX),
28269    min: $.validator.format(Resources.VALIDATE_MIN)
28270});
28271
28272var validator = {
28273    regex: regex,
28274    settings: settings,
28275    init: function () {
28276        var self = this;
28277        $('form:not(.suppress)').each(function () {
28278            $(this).validate(self.settings);
28279        });
28280    },
28281    initForm: function (f) {
28282        $(f).validate(this.settings);
28283    }
28284};
28285
28286module.exports = validator;
28287
28288},{}],104:[function(require,module,exports){
28289/* 
28290 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
28291 * watermark.js - Create watermarked images with Canvas and JS
28292 *
28293 * Version: 1 (2011-04-04)
28294 * Copyright (c) 2011	Patrick Wied ( http://www.patrick-wied.at )
28295 * This code is licensed under the terms of the MIT LICENSE
28296 *
28297 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
28298 */
28299
28300module.exports = function(w){
28301	var w = window;
28302	var wm = (function(w){
28303		var doc = w.document,
28304		gcanvas = {},
28305		gctx = {},
28306		imgQueue = [],
28307		classNames = ["watermark"],
28308		watermark = false,
28309		watermarkPosition = "bottom-right",
28310		watermarkPath = "watermark.png?"+(+(new Date())),
28311		opacity = (255/(100/50)), // 50%
28312		initCanvas = function(){
28313			gcanvas = doc.createElement("canvas");
28314			gcanvas.style.cssText = "display:none;";
28315			gctx = gcanvas.getContext("2d");
28316			doc.body.appendChild(gcanvas);
28317		},
28318		initWatermark = function(){
28319			watermark = new Image();
28320			watermark.src = "";
28321			watermark.src = watermarkPath;
28322			
28323			if(opacity != 255){
28324				if(!watermark.complete)
28325					watermark.onload = function(){	
28326						applyTransparency();
28327					}
28328				else
28329					applyTransparency();
28330				
28331
28332			}else{
28333				applyWatermarks();
28334			}
28335			
28336		},
28337		// function for applying transparency to the watermark
28338		applyTransparency = function(){
28339			var w = watermark.width || watermark.offsetWidth,
28340			h = watermark.height || watermark.offsetHeight;
28341			setCanvasSize(w, h);
28342			gctx.drawImage(watermark, 0, 0);
28343					
28344			var image = gctx.getImageData(0, 0, w, h);
28345			var imageData = image.data,
28346			length = imageData.length;
28347			for(var i=3; i < length; i+=4){  
28348				imageData[i] = (imageData[i]<opacity)?imageData[i]:opacity;
28349			}
28350			image.data = imageData;
28351			gctx.putImageData(image, 0, 0);
28352			watermark.onload = null;
28353			watermark.src = "";
28354			watermark.src = gcanvas.toDataURL();
28355			// assign img attributes to the transparent watermark
28356			// because browsers recalculation doesn't work as fast as needed
28357			watermark.width = w;
28358			watermark.height = h;
28359
28360			applyWatermarks();
28361		},
28362		configure = function(config){
28363			if(config["watermark"])
28364				watermark = config["watermark"];
28365			if(config["path"])
28366				watermarkPath = config["path"];
28367			if(config["position"])
28368				watermarkPosition = config["position"];
28369			if(config["opacity"])
28370				opacity = (255/(100/config["opacity"]));
28371			if(config["classNames"])
28372				classNames = config["classNames"];
28373			
28374			initCanvas();
28375			initWatermark();
28376		}
28377		setCanvasSize = function(w, h){
28378			gcanvas.width = w;
28379			gcanvas.height = h;
28380		},
28381		applyWatermark = function(img){
28382			gcanvas.width = img.naturalWidth || img.width || img.offsetWidth;
28383			gcanvas.height = img.naturalHeight || img.height || img.offsetHeight;
28384			gctx.drawImage(img, 0, 0);
28385			var position = watermarkPosition,
28386			x = 0,
28387			y = 0;
28388			if(position.indexOf("top")!=-1)
28389				y = 10;
28390			else if(position.indexOf("middle")!=-1)
28391				y = (gcanvas.height-watermark.height) / 2;
28392			else
28393				y = gcanvas.height-watermark.height-10;
28394			
28395			if(position.indexOf("left")!=-1)
28396				x = 10;
28397			else if(position.indexOf("center")!=-1)
28398				x = (gcanvas.width-watermark.width) / 2;
28399			else
28400				x = gcanvas.width-watermark.width-10;
28401
28402
28403			gctx.drawImage(watermark, x, y);
28404			img.onload = null;
28405	
28406			img.src = gcanvas.toDataURL();
28407
28408		},
28409		applyWatermarks = function(){
28410			setTimeout(function(){
28411				for(var i = 0; i < classNames.length; i++){
28412					var els = doc.getElementsByClassName(classNames[i]),
28413					len = els.length;
28414					while(len--){
28415						var img = els[len];
28416						if(img.tagName.toUpperCase() != "IMG" || (img.tagName.toUpperCase() == "IMG" && img.getAttribute('src').indexOf('data:image/png') > -1) || img.naturalWidth == 0)
28417							continue;
28418						
28419						if(!img.complete){
28420							img.onload = function(){
28421								applyWatermark(this);
28422							};
28423						}else{
28424							applyWatermark(img);
28425						}
28426					}
28427				}
28428			},10);
28429		};
28430		
28431		
28432		return {
28433			init: function(config){
28434				configure(config);
28435			}
28436		};
28437	})(w);
28438	w.wmark = wm;
28439};
28440},{}],105:[function(require,module,exports){
28441'use strict';
28442
28443var dialog = require('./dialog');
28444var progress = require('./progress');
28445var loginDialog = require('./loginDialog');
28446
28447/* -------------------------
28448 * WISHLIST
28449 * -------------------------
28450 */
28451
28452function errorHandling(data, dialogTitle, dialogHtml) {
28453    if (data && typeof data === 'object' && 'message' in data) {
28454        console.log(data.message);
28455    }
28456    dialog.open({
28457        html: dialogHtml,
28458        options: {
28459            title: dialogTitle,
28460            draggable: false,
28461            open: function(event, ui) {                                    
28462                $('body').addClass('dialog-open');
28463            },
28464            close: function(event, ui) {
28465                $('body').removeClass('dialog-open');
28466            }
28467        }
28468    });
28469}
28470
28471function updateCSRFToken() {
28472	if (pageContext && pageContext.options) {
28473		$.ajax({
28474			method: 'get',
28475			url: Urls.CSRFToken.Get,
28476			dataType: 'json',
28477			success: function(response) {
28478				pageContext.options.Token = response.token;
28479			}
28480		});
28481	}
28482}
28483
28484function checkWishlistForItem(wishlistItemId) {
28485    if ('localStorage' in window && window['localStorage'] !== null && 'WishlistProductIDs' in window.localStorage && window.localStorage.WishlistProductIDs.length > 0) {
28486        var wishlistItems = JSON.parse(window.localStorage.WishlistProductIDs);
28487        var pageType = pageContext && 'type' in pageContext && pageContext.type ? pageContext.type : $('#wrapper').attr('class');
28488        var isWishlistItem = wishlistItems.filter(function(productObject) {
28489            if (productObject.productID == wishlistItemId) { return true }
28490            // If this is NOT a PDP, then we need to run additional checks to see if Master Product or any of the Variations are in the wishlist
28491            if (pageType != 'product') {
28492                if (productObject.masterID == wishlistItemId) { return true }
28493                if (productObject.variants.indexOf(wishlistItemId) > -1) { return true }
28494            }
28495        });
28496        return isWishlistItem.length > 0;
28497    } else {
28498        return false;
28499    }
28500}
28501
28502function modifyWishlistInStorage(data) {
28503    if ('localStorage' in window && window['localStorage'] !== null) {
28504        if (!window.localStorage.WishlistProductIDs) {
28505            window.localStorage.WishlistProductIDs = '[]';
28506        }
28507        var item = 'productData' in data && data.productData.productID ? data.productData.productID : null;
28508        var isDataAction = data.action;
28509        var wishlistItems = JSON.parse(window.localStorage.WishlistProductIDs);
28510
28511        if (item && isDataAction) {
28512            if (isDataAction == 'wishlistItemAdded') {
28513                wishlistItems.push(data.productData);
28514                window.localStorage.WishlistProductIDs = JSON.stringify(wishlistItems);
28515            }
28516            if (isDataAction == 'wishlistItemRemoved' && wishlistItems.length > 0) {
28517                var modifiedWishlist = wishlistItems.filter(function(productObject) {
28518                    if (productObject.productID != item) { return true }
28519                });
28520                window.localStorage.WishlistProductIDs = JSON.stringify(modifiedWishlist);
28521            }
28522        }
28523    }
28524}
28525
28526function addWishlistEvent(event, productID) {
28527	var eventType = "wishlist_" + event;
28528	var url, image;
28529	var images = [];
28530	
28531	var thisTile = $('.product-tile[data-itemid="'+productID+'"]');
28532	var thisItem = JSON.parse($('.productCardMetadata.event-browse[data-pid="'+productID+'"]').attr('data-product-gtm-json'));
28533	if (thisTile.length > 0) {
28534		url = thisTile.find('.thumb-link').attr('href');
28535		image = thisTile.find('.thumb-link img').attr('src');
28536		images.push(image);
28537	} else {
28538		var selectedSizeOption = $('div.product-variations .size.size-options li.selected .swatchanchor');
28539		var selectedSizeUrl = selectedSizeOption.attr('href').replace('Product-Variation', 'Product-Show');
28540		var selectedSizeValue = selectedSizeOption.attr('data-size-id').replace(/ /g, '%20');
28541		url = selectedSizeUrl.replace("_size=", "_size="+selectedSizeValue);
28542		$('.product-primary-image [class*="-slide"]').not('.slick-cloned').each(function (index) {
28543			if(index > -1) {
28544				var image = $(this).find('a').attr('href');
28545				if (!image || image == undefined || image == null) {
28546					image = $(this).find('img').attr('src');
28547				}
28548				if (images.indexOf(image) === -1) {
28549					images.push(image);
28550				}
28551			}
28552		});
28553	}
28554	
28555	var properties = {
28556		"productID": thisItem["id"],
28557		"productName": thisItem["name"].replace(/\\/g, ""),
28558		"category": thisItem["category"].replace(/\\/g, ""),
28559		"price": thisItem["price"],
28560		"images": images,
28561		"url": url,
28562		"attr": {
28563			"manufacturer": thisItem["brand"]
28564		},
28565	};
28566
28567	if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
28568		crdl('event', eventType, properties); 
28569	}	
28570}
28571
28572function prepareWishlistAddUI(buttons, wishlistItemId, sizeText) {
28573    if (!buttons) {
28574        return;
28575    } else {
28576        buttons.each(function(idx, element) {
28577            var $button = $(element);
28578            var addUrl = Urls.wishlistAdd + '?pid=' + $button.attr('data-pid');
28579            $button.removeClass('wishlist-item-added').addClass('wishlist-item-ready');
28580            $button.css('display', 'inline-block');
28581            $button.attr('data-url', addUrl);
28582            $button.attr('data-action', 'AddInternal');
28583            $button.attr('title', ($button.attr('data-title-add') + ' - ' + sizeText));
28584            if ($button.attr('id') == 'wishlist-button') {
28585                $button.find('span').html($button.attr('data-add-message'));
28586            }
28587        });
28588    }
28589    
28590    if ($('.pt_wish-list').length > 0 && (wishlistItemId)) {
28591        if ('localStorage' in window && window['localStorage'] !== null && 'WishlistProductIDs' in window.localStorage && window.localStorage.WishlistProductIDs.length > 0) {
28592            var wishlistItems = JSON.parse(window.localStorage.WishlistProductIDs);
28593            var variationModel = wishlistItems.filter(function(productObject) {
28594                if (productObject.productID == wishlistItemId || productObject.masterID == wishlistItemId || productObject.variants.indexOf(wishlistItemId) > -1) { 
28595                    return productObject;
28596                }
28597            });
28598            if (variationModel.length) {
28599                var wishlistItemsToRemove = variationModel[0];
28600                for (var key in wishlistItemsToRemove) {
28601                    var value = wishlistItemsToRemove[key];
28602                    // If matching variant or master
28603                    if (key == 'productID' || key == 'masterID') {
28604                        $('.product-tile[data-itemid="'+value+'"]').parents('li.wishlist-item').remove();
28605                    // Else, check the other variants
28606                    } else {
28607                        var variants = value.split(',');
28608                        var i = 0;
28609                        while (i < variants.length) {
28610                            var variantID = variants[i];
28611                            $('.product-tile[data-itemid="'+variantID+'"]').parents('li.wishlist-item').remove();
28612                            i++;
28613                        }
28614                    }
28615                }
28616                // If user removes all shareable products from wishlist, then hide the share toggle and set the wishlist to private
28617                if ($('li.wishlist-item[data-shareable-item="true"]').length < 1) {
28618                    $('.wishlist-share-container').hide();
28619                    $('.share-wishlist-toggle input[name="share-wishlist-checkbox"]').prop('checked', false).trigger('change');
28620                }
28621                if ($('li.wishlist-item').length < 1) {
28622                    $('.wishlist-message.empty-wishlist').removeClass('d-none');
28623                }
28624            }
28625        }
28626    }
28627}
28628
28629function prepareWishlistRemoveUI(buttons, sizeText) {
28630    if (!buttons) {
28631        return;
28632    } else {
28633        buttons.each(function(idx, element) {
28634            var $button = $(element);
28635            var removeUrl = Urls.wishlistRemove + '?pid=' + $button.attr('data-pid');
28636            $button.removeClass('wishlist-item-ready').addClass('wishlist-item-added');
28637            $button.css('display', 'inline-block');
28638            $button.attr('data-url', removeUrl);
28639            $button.attr('data-action', 'RemoveInternal');
28640            $button.attr('title', ($button.attr('data-title-remove') + ' - ' + sizeText));
28641            if ($button.attr('id') == 'wishlist-button') {
28642                $button.find('span').html($button.attr('data-remove-message'));
28643            }
28644        });
28645    }
28646}
28647
28648function renderAllWishlistButtons() {
28649    $('.wishlist-item-status-button').each(function() {
28650        var $button = $(this);
28651        var wishlistItemId = $button.attr('data-pid');
28652        if (wishlistItemId && wishlistItemId != '') {
28653            var isWishlistItem = checkWishlistForItem(wishlistItemId);
28654            if (isWishlistItem) { 
28655                prepareWishlistRemoveUI($button);
28656            } else {
28657                prepareWishlistAddUI($button, wishlistItemId);
28658            }
28659        }
28660    });
28661}
28662
28663function validateEmailForm($email, $message) {	
28664    $('.share-wishlist-menu-popup .error-container').addClass('d-none');
28665
28666    var regexEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/g;	
28667    var regexMessage = /^[A-Za-z0-9 _.,!"'\/()$]*/g;
28668    var email = $email.val().trim();
28669    var message = $message.val().trim();
28670	var formValidation = true;
28671			
28672	if ((!email || email.length == 0) || String(email).match(regexEmail) == null) {
28673		formValidation = false;
28674        $email.addClass('error');
28675	    $('#share-wishlist-email-popup .invalid-email-format').removeClass('d-none');
28676	}
28677    if ((!message || message.length == 0) || String(message).match(regexMessage) == null) {
28678        formValidation = false;
28679        $message.addClass('error');
28680        $('#share-wishlist-email-popup .invalid-email-message-format').removeClass('d-none');
28681    }
28682			
28683	if (!formValidation) {
28684        return false;
28685    } else {
28686        return true;
28687    }
28688}
28689
28690function wishlistPostOrder() {
28691    var orderNo = window.orderNumber;
28692    if (orderNo) {
28693        $.ajax({
28694            type: 'POST',
28695            url: Urls.wishlistPostOrder,
28696            contentType: 'application/json',
28697            data: JSON.stringify({
28698                orderNumber: orderNo
28699            }),
28700            error: function(jqXHR, textStatus, errorThrown) {
28701                console.log('Wishlist-PostOrder: Error in removing ordered products from wishlist. ERROR TEXT: '+textStatus+', ERROR THROWN: '+errorThrown+'.');
28702            },
28703            success: function(response) {
28704                if (response) {
28705                    if (response.result && response.result == 'success') {
28706                        if (SitePreferences.CORDIAL_ENABLED && response.removedProducts && response.removedProducts.length > 0) {
28707                            $(document).trigger('CordialTagging.WishlistPostOrder', [response.removedProducts, orderNo]);
28708                        }
28709                    }
28710                }
28711            }
28712        });
28713    }
28714}
28715
28716function init () {
28717    $(document).ready(function() {
28718
28719        updateCSRFToken();
28720
28721        /**
28722         * On page load, make AJAX call to get wishlist items and store in localStorage. From localStorage, compare the items being loaded on 
28723         * page to see if any are in wishlist, and mark the heart icons accordingly. 
28724         */
28725        if ('localStorage' in window && window['localStorage'] !== null) {
28726            // Only make AJAX call to get wishlist product IDs if customer is logged in
28727            if (Customer.authenticated) {
28728                $.ajax({
28729                    type: 'GET',
28730                    url: Urls.wishlistGetItems,
28731                    contentType: 'application/json',
28732                    error: function(jqXHR, textStatus, errorThrown) {
28733                        $('.wishlist-item-status-button').css('display', 'inline-block');
28734                        window.localStorage.WishlistProductIDs = '';
28735                    },
28736                    success: function(response) {
28737                        if (response) {
28738                            if (typeof response === 'object' && 'wishlist' in response) {
28739                                window.localStorage.WishlistProductIDs = JSON.stringify(response.wishlist);
28740                                renderAllWishlistButtons();
28741                            } else {
28742                                $('.wishlist-item-status-button').css('display', 'inline-block');
28743                                window.localStorage.WishlistProductIDs = '';
28744                            }
28745                        }
28746                    }
28747                });   
28748            // If guest user, just render the wishlist buttons as empty
28749            } else {
28750                window.localStorage.WishlistProductIDs = '';
28751                renderAllWishlistButtons();
28752            }
28753        }
28754
28755        /**
28756         * On click of a wishlist heart button, open a login dialog (if guest user) or add/remove item from wishlist (if logged-in customer)
28757         */
28758        $('body').on('click', '.wishlist-item-status-button, .wishlist-button', function(event) {
28759            event.preventDefault();
28760            var $button = $(this);
28761            var wishlistUrl = $button.attr('data-url');
28762            var wishlistController = $button.attr('data-controller');
28763            var wishlistAction = $button.attr('data-action');
28764            var productID = $button.attr('data-pid');
28765
28766            var isProductPage = $('.pt_product-details').length;
28767            var continueWishlist = true;
28768
28769            if (isProductPage) {
28770                var sizeSelected = $('div.product-variations .size.size-options li.selected');
28771				var lengthSelected = $('div.product-variations .length.length-options li.selected').length;
28772				var widthSelected = $('div.product-variations .width.width-options li.selected').length;
28773				var isSizeSelected = sizeSelected.length || (lengthSelected && widthSelected);
28774				var isColorSelected = $('div.product-variations .color.color-options li.selected').length;
28775                if (!isSizeSelected) {
28776                    alert('Please select a size');
28777                    continueWishlist = false;
28778                }
28779            }
28780            
28781            if (continueWishlist) {
28782            	//Cordial wishlist add/remove events
28783            	if ($button.hasClass('wishlist-item-added')) {
28784    				addWishlistEvent('remove', productID);
28785    			} else {
28786    				addWishlistEvent('add', productID);
28787    			}
28788            	
28789                // WISHLIST REPORTING: Track wishlist-item-status-button clicks (both guest and logged-in users)
28790                $.ajax({
28791                    type: 'POST',
28792                    url: Urls.wishlistTrackActions,
28793                    contentType: 'application/json',
28794                    data: JSON.stringify({
28795                        action: wishlistAction,
28796                        pid: productID
28797                    }),
28798                    error: function(jqXHR, textStatus, errorThrown) {
28799                        console.log('Wishlist-TrackActions: Error in incrementing wishlist action counters. ERROR TEXT: '+textStatus+', ERROR THROWN: '+errorThrown+'.');
28800                    },
28801                    success: function(response) {
28802                        if (response) {}
28803                    }
28804                });
28805
28806                // If guest user, open a login dialog
28807                if (!Customer.authenticated) {
28808                    var params = wishlistUrl.substring(wishlistUrl.indexOf('?'), wishlistUrl.length);
28809                    loginDialog.show(function() {
28810                        // For reporting
28811                        $('.login-dialog-forms-container [data-login-source]').attr('data-login-source', 'wishlist-modal'); 
28812                    }, wishlistController, wishlistAction, productID, params, wishlistUrl);
28813                // Else, if logged-in user, allow customer to add/remove wishlist item
28814                } else {
28815                    $.ajax({
28816                        type: 'GET',
28817                        url: wishlistUrl,
28818                        contentType: 'application/json',
28819                        error: function(jqXHR, textStatus, errorThrown) {
28820                            errorHandling(errorThrown, 'Wishlist - Error', '<p style="padding: 10px;">There was a problem handling your request. Please try again later.</p>');
28821                        },
28822                        success: function(response) {
28823                            if (response && response.result && response.result == 'success' && response.action) {
28824                                modifyWishlistInStorage(response);
28825                                if (response.action == 'wishlistItemRemoved') {
28826                                    var productIdRemoved = response && response.formattedData && response.formattedData.productID ? response.formattedData.productID : null;
28827                                    prepareWishlistAddUI($button, productIdRemoved);
28828                                } else {
28829                                    prepareWishlistRemoveUI($button);
28830                                }
28831                            } else {
28832                                var dialogHtml = '<p style="padding: 10px;">Unable to modify wishlist at this time. Please try again later.</p>';
28833                                if (response && response.action) {
28834                                    if (response.action == 'wishlistItemRemoved') {
28835                                        dialogHtml = '<p style="padding: 10px;">Unable to remove item from wishlist. Please try again later.</p>';
28836                                    } else {
28837                                        dialogHtml = '<p style="padding: 10px;">Unable to add item to wishlist. Please try again later.</p>';
28838                                    }
28839                                }
28840                                errorHandling(null, 'Wishlist - Error', dialogHtml);
28841                            }
28842                        }
28843                    });
28844                }
28845            }
28846        });
28847
28848        /**
28849         * Add product to customer's cart when "Add to Cart" button is clicked on Wishlist page
28850         */
28851        $('body').on('click', '.wishlist-add-to-cart-button', function() {
28852            var $button = $(this);
28853            var productId = $button.attr('data-pid');
28854            $button.find('span').html('');
28855            progress.show($button.find('span'), true);
28856
28857            $.ajax({
28858                type: 'POST',
28859                url: Urls.addProduct,
28860                data: {
28861                     pid: productId,
28862                     csrf_token: pageContext.options.Token
28863                },
28864                error: function(jqXHR, textStatus, errorThrown) {
28865                    progress.hide();
28866                    $button.find('span').html('Add to Cart');
28867                    errorHandling(errorThrown, 'Wishlist - Error', '<p style="padding: 10px;">Unable to add item to cart. Please try again later.</p>');
28868                },
28869                success: function(response) {
28870                    var $miniCartElm = $('.mini-cart .minicart-quantity').length > 0 ? $('.mini-cart .minicart-quantity') : $('.mini-cart .btn-minicart .btn-minicart-qty');
28871                    var miniCartTotal;
28872                    if ($miniCartElm.length > 0) {
28873                        miniCartTotal = parseInt($miniCartElm.html().trim());
28874                    }  
28875                    miniCartTotal++;                  
28876                    $miniCartElm.html(miniCartTotal);
28877                    $button.attr('disabled', true).addClass('disabled');
28878                    $button.find('span').html('<i class="fa fa-check" aria-hidden="true"></i> Item Added');
28879
28880                    // Only fire this AJAX call if the customer successfully added the product to their cart. On error of addProduct, do nothing.
28881                    if (Customer.authenticated) {
28882                        // WISHLIST REPORTING: Track 'Add to Cart' clicks from wishlist page
28883                        $.ajax({
28884                            type: 'GET',
28885                            url: Urls.wishlistTrackAddToCart,
28886                            contentType: 'application/json',
28887                            error: function(jqXHR, textStatus, errorThrown) {
28888                                console.log('Wishlist-TrackAddToCart: Error in incrementing wishlist Add-to-Cart counter. ERROR TEXT: '+textStatus+', ERROR THROWN: '+errorThrown+'.');
28889                            },
28890                            success: function(response) {
28891                                if (response) {}
28892                            }
28893                        });
28894                    }
28895                }
28896            });
28897            
28898            var thisItem = $('.product-tile[data-itemid="'+productId+'"]');
28899        	var itemMeta = JSON.parse($('.productCardMetadata.event-browse[data-pid="'+productId+'"]').attr('data-product-gtm-json'));
28900        	var image = thisItem.find('.tile-image').attr('src');
28901			if (!image || image == undefined || image == null) {
28902				image = thisItem.find('video').attr('poster');
28903			}
28904        	
28905            if (SitePreferences.CORDIAL_ENABLED) {
28906            	var properties = {
28907					'productID': itemMeta["id"]
28908				};
28909				var cart_data = [{
28910				    'productID': itemMeta["id"],
28911				    'sku': itemMeta["skuid"],
28912				    'category': itemMeta["category"].replace(/\\/g, ""),
28913				    'name': itemMeta["name"].replace(/\\/g, ""),
28914				    'qty': '1', 
28915				    'itemPrice': itemMeta["price"],
28916				    'images': [image],
28917				    'url': thisItem.find('.thumb-link').attr('href'),
28918				    'attr': {
28919				        'manufacturer': itemMeta["brand"],
28920				    	'color': thisItem.find('.product-color').text().replace('Color: ',""),
28921				        'size': thisItem.find('.product-size').text().replace('Size: ',"")
28922				    }
28923				}];
28924
28925				if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
28926					crdl([
28927						['cart', 'update'],
28928						['cartitem', 'add' , cart_data], 
28929						['event', 'cart', properties]
28930					]);
28931				}
28932			}
28933            
28934            if (SitePreferences.PIXLEE_ENABLED) {
28935				if (typeof pixlee_analytics !== 'undefined') {
28936				    pixlee_analytics.events.trigger('add:to:cart', {
28937				        'product_sku': itemMeta["id"],
28938				        'price': itemMeta["price"],
28939				        'quantity': '1',
28940				        'currency': 'USD'
28941				    });
28942				 }
28943			}
28944        });
28945
28946        $('body').on('click', '#mobile-share-wishlist-button', function() {
28947            $('.wishlist-share-mobile-container').slideToggle('slow');
28948        });
28949        $('body').on('change', '.share-wishlist-toggle input[name="share-wishlist-checkbox"]', function() {
28950            var status = $(this).is(':checked');
28951            if (status) {
28952                $('#share-wishlist-menu').slideDown('slow').css('display','flex');
28953            } else {
28954                $('#share-wishlist-menu').slideUp('slow').removeClass('show-share-wishlist-menu').css('display','none');
28955            }
28956            if (Customer.authenticated) {
28957                var wishlistID = $('input[name="current_wishlist_id"]').val().trim();
28958                if (wishlistID) {
28959                    $.ajax({
28960                        method: 'GET',
28961                        url: Urls.wishlistSetPublic,
28962                        data: {
28963                            WishlistID: wishlistID,
28964                            SetPublic: status
28965                        }
28966                    }).done(function(response) {
28967                        if (response.result == 'error') {
28968                            errorHandling(null, '', 'Wishlist Share - Error', '<p style="padding: 10px;">Oops, something went wrong and cannot update your Wishlist at this time. Please try again later.</p>');
28969                        } else if (response.result == 'success') {
28970                            // No on-screen visuals need to occur
28971                        }
28972                    });
28973                } else {
28974                    errorHandling(null, '', 'Wishlist Share - Error', '<p style="padding: 10px;">Oops, something went wrong and we could not retrieve your Wishlist. Please try again later.</p>');
28975                }
28976            } else {
28977                window.location.href = Urls.account;
28978            }
28979        });
28980        $('body').on('click', '.share-wishlist-menu-popup .close-button', function() {
28981            var $this = $(this);
28982            $this.parents('.share-wishlist-menu-popup').addClass('d-none');
28983            var shareLink = $this.parents('.share-wishlist-menu-popup').attr('id').indexOf('link-popup') > -1;
28984            if (shareLink) {
28985                $this.siblings('.form-row-button').find('button[name="share-wishlist-link-copy"]').removeClass('btn-complete disabled').html('Copy');
28986            }
28987        });
28988        $('body').on('click', 'button[name="share-wishlist-link-copy"]:not(.disabled)', function() {
28989            var $button = $(this);
28990            var wishlistUrl = $('#share-wishlist-link-popup .share-wishlist-url a').html();
28991            navigator.clipboard.writeText(wishlistUrl);
28992            $button.addClass('btn-complete disabled').html('<i class="fa fa-check" aria-hidden="true"></i> Copied!');
28993        });
28994        $('body').on('click', 'button[name="share-wishlist-email-send"]:not(.disabled)', function() {
28995            var $this = $(this);
28996            var $email = $('#wishlist-share-email-input');
28997            var $message = $('#wishlist-share-email-message');
28998            var isValidEmailForm = validateEmailForm($email, $message);
28999            if (isValidEmailForm) {
29000                if (Customer.authenticated) {
29001                    var wishlistID = $('input[name="current_wishlist_id"]').val().trim();
29002                    if (wishlistID) {
29003                        $.ajax({
29004                            method: 'POST',
29005                            url: Urls.wishlistShareByEmail,
29006                            data: {
29007                                WishlistID: wishlistID,
29008                                ToEmail: $email.val(),
29009                                EmailMessage: $message.val()
29010                            }
29011                        }).done(function(response) {
29012                            if (response.result == 'error') {
29013                                errorHandling(null, '', 'Wishlist Share - Error', '<p style="padding: 10px;">Oops, something went wrong and could not send your Wishlist email. Please try again later.</p>');
29014                            } else if (response.result == 'success') {
29015                                $this.parents('#share-wishlist-email-popup').addClass('share-wishlist-email-success');
29016                            }
29017                        });
29018                    } else {
29019                        errorHandling(null, '', 'Wishlist Share - Error', '<p style="padding: 10px;">Oops, something went wrong and we could not retrieve your Wishlist for sharing. Please try again later.</p>');
29020                    }
29021                } else {
29022                    window.location.href = Urls.account;
29023                }
29024            }
29025        });
29026        $('body').on('click', '#share-wishlist-menu .btn-open', function() {
29027            var $button = $(this);
29028            var $sharePopup = $button.siblings('.share-wishlist-menu-popup');
29029            if ($sharePopup.length) {
29030                $sharePopup.removeClass('d-none share-wishlist-email-success');
29031            }
29032        });
29033    });
29034
29035    // If this is an order confirmation page, kick off wishlist remove products post order
29036    if ($(document).find('.pt_order-confirmation').length > 0) {
29037        wishlistPostOrder();
29038    }
29039}
29040
29041exports.checkWishlistForItem = checkWishlistForItem;
29042exports.prepareWishlistAddUI = prepareWishlistAddUI;
29043exports.prepareWishlistRemoveUI = prepareWishlistRemoveUI;
29044exports.renderAllWishlistButtons = renderAllWishlistButtons;
29045exports.init = init;
29046},{"./dialog":36,"./loginDialog":44,"./progress":86}],106:[function(require,module,exports){
29047'use strict';
29048
29049exports.SlickSlider = {
29050	Options: {
29051		Single: {
29052			accessibility:  true,
29053			infinite: 		true,
29054			dots: 			false,
29055			arrows: 		false,
29056			slidesToShow: 	1,
29057			slidesToScroll: 1,
29058			responsive: 	[
29059				{
29060					breakpoint: 767,
29061					settings: 	{
29062						dots: true
29063					}
29064				}
29065			]
29066		},
29067		Thumbnail: {
29068			accessibility:  true,
29069			draggable: true,
29070			infinite: false,
29071			focusOnSelect: 	false,
29072			dots: false,
29073			arrows: false,
29074			slidesToShow: 7,
29075			slidesToScroll: 7
29076		},
29077		CategoryLanding: {
29078			accessibility:  true,
29079			draggable: true,
29080			infinite: true,
29081			focusOnSelect: 	false,
29082			dots: false,
29083			arrows: true,
29084			slidesToShow: 4,
29085			slidesToScroll: 4,
29086			responsive: 	[
29087				{
29088					breakpoint: 767
29089				},
29090				{
29091					breakpoint: 1200,
29092					settings: {
29093						slidesToShow: 3,
29094						slidesToScroll: 3
29095					}
29096				}
29097			]
29098		}
29099	}
29100}
29101},{}],107:[function(require,module,exports){
29102'use strict';
29103
29104var _ = require('lodash');
29105var PaymentMethodConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').PaymentMethods;
29106var CreditCardConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants').CreditCards;
29107
29108var util = {
29109    /**
29110     * @function
29111     * @description appends the parameter with the given name and value to the given url and returns the changed url
29112     * @param {String} url the url to which the parameter will be added
29113     * @param {String} name the name of the parameter
29114     * @param {String} value the value of the parameter
29115     */
29116    appendParamToURL: function (url, name, value) {
29117        // quit if the param already exists
29118        if (url.indexOf(name + '=') !== -1) {
29119            return url;
29120        }
29121        var separator = url.indexOf('?') !== -1 ? '&' : '?';
29122		var anchor = '';
29123		if (url.indexOf('#') > -1) {
29124            var urlAndAnchor = url.split('#');
29125			url = urlAndAnchor[0];
29126			anchor = '#' + urlAndAnchor[1];
29127        }
29128        return url + separator + name + (value ? '=' + encodeURIComponent(value) : '') + anchor;
29129    },
29130
29131    /**
29132     * @function
29133     * @description remove the parameter and its value from the given url and returns the changed url
29134     * @param {String} url the url from which the parameter will be removed
29135     * @param {String} name the name of parameter that will be removed from url
29136     */
29137    removeParamFromURL: function (url, name) {
29138        if (url.indexOf('?') === -1 || url.indexOf(name + '=') === -1) {
29139            return url;
29140        }
29141        var hash;
29142        var params;
29143        var domain = url.split('?')[0];
29144        var paramUrl = url.split('?')[1];
29145        var newParams = [];
29146        // if there is a hash at the end, store the hash
29147        if (paramUrl.indexOf('#') > -1) {
29148            hash = paramUrl.split('#')[1] || '';
29149            paramUrl = paramUrl.split('#')[0];
29150        }
29151        params = paramUrl.split('&');
29152        for (var i = 0; i < params.length; i++) {
29153            // put back param to newParams array if it is not the one to be removed
29154            if (params[i].split('=')[0] !== name) {
29155                newParams.push(params[i]);
29156            }
29157        }
29158        return domain + '?' + newParams.join('&') + (hash ? '#' + hash : '');
29159    },
29160
29161    /**
29162     * @function
29163     * @description appends the parameters to the given url and returns the changed url
29164     * @param {String} url the url to which the parameters will be added
29165     * @param {Object} params
29166     */
29167    appendParamsToUrl: function (url, params) {
29168        var _url = url;
29169        _.each(params, function (value, name) {
29170            _url = this.appendParamToURL(_url, name, value);
29171        }.bind(this));
29172        return _url;
29173    },
29174    /**
29175     * @function
29176     * @description extract the query string from URL
29177     * @param {String} url the url to extra query string from
29178     **/
29179    getQueryString: function (url) {
29180        var qs;
29181        if (!_.isString(url)) { return; }
29182        var a = document.createElement('a');
29183        a.href = url;
29184        if (a.search) {
29185            qs = a.search.substr(1); // remove the leading ?
29186        }
29187        return qs;
29188    },
29189    /**
29190     * @function
29191     * @description
29192     * @param {String}
29193     * @param {String}
29194     */
29195    elementInViewport: function (el, offsetToTop) {
29196        var top = el.offsetTop,
29197            left = el.offsetLeft,
29198            width = el.offsetWidth,
29199            height = el.offsetHeight;
29200
29201        while (el.offsetParent) {
29202            el = el.offsetParent;
29203            top += el.offsetTop;
29204            left += el.offsetLeft;
29205        }
29206
29207        if (typeof(offsetToTop) !== 'undefined') {
29208            top -= offsetToTop;
29209        }
29210
29211        if (window.pageXOffset !== null) {
29212            return (
29213                top < (window.pageYOffset + window.innerHeight) &&
29214                left < (window.pageXOffset + window.innerWidth) &&
29215                (top + height) > window.pageYOffset &&
29216                (left + width) > window.pageXOffset
29217            );
29218        }
29219
29220        if (document.compatMode === 'CSS1Compat') {
29221            return (
29222                top < (window.document.documentElement.scrollTop + window.document.documentElement.clientHeight) &&
29223                left < (window.document.documentElement.scrollLeft + window.document.documentElement.clientWidth) &&
29224                (top + height) > window.document.documentElement.scrollTop &&
29225                (left + width) > window.document.documentElement.scrollLeft
29226            );
29227        }
29228    },
29229
29230    /**
29231     * @function
29232     * @description Appends the parameter 'format=ajax' to a given path
29233     * @param {String} path the relative path
29234     */
29235    ajaxUrl: function (path) {
29236        return this.appendParamToURL(path, 'format', 'ajax');
29237    },
29238
29239    /**
29240     * @function
29241     * @description
29242     * @param {String} url
29243     */
29244    toAbsoluteUrl: function (url) {
29245        if (url.indexOf('http') !== 0 && url.charAt(0) !== '/') {
29246            url = '/' + url;
29247        }
29248        return url;
29249    },
29250    /**
29251     * @function
29252     * @description Loads css dynamically from given urls
29253     * @param {Array} urls Array of urls from which css will be dynamically loaded.
29254     */
29255    loadDynamicCss: function (urls) {
29256        var i, len = urls.length;
29257        for (i = 0; i < len; i++) {
29258            this.loadedCssFiles.push(this.loadCssFile(urls[i]));
29259        }
29260    },
29261
29262    /**
29263     * @function
29264     * @description Loads css file dynamically from given url
29265     * @param {String} url The url from which css file will be dynamically loaded.
29266     */
29267    loadCssFile: function (url) {
29268        return $('<link/>').appendTo($('head')).attr({
29269            type: 'text/css',
29270            rel: 'stylesheet'
29271        }).attr('href', url); // for i.e. <9, href must be added after link has been appended to head
29272    },
29273    // array to keep track of the dynamically loaded CSS files
29274    loadedCssFiles: [],
29275
29276    /**
29277     * @function
29278     * @description Removes all css files which were dynamically loaded
29279     */
29280    clearDynamicCss: function () {
29281        var i = this.loadedCssFiles.length;
29282        while (0 > i--) {
29283            $(this.loadedCssFiles[i]).remove();
29284        }
29285        this.loadedCssFiles = [];
29286    },
29287    /**
29288     * @function
29289     * @description Extracts all parameters from a given query string into an object
29290     * @param {String} qs The query string from which the parameters will be extracted
29291     */
29292    getQueryStringParams: function (qs) {
29293        if (!qs || qs.length === 0) { return {}; }
29294        var params = {},
29295            unescapedQS = decodeURIComponent(qs);
29296        // Use the String::replace method to iterate over each
29297        // name-value pair in the string.
29298        unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'),
29299            function ($0, $1, $2, $3) {
29300                params[$1] = $3;
29301            }
29302        );
29303        return params;
29304    },
29305
29306    fillAddressFields: function (address, $form) {
29307        for (var field in address) {
29308            if (field === 'UUID' || field === 'key') {
29309                continue;
29310            }
29311            // if the key in address object ends with 'Code', remove that suffix
29312            // keys that ends with 'Code' are postalCode, stateCode and countryCode
29313            // update the state fields
29314            if (field === 'countryCode') {
29315            	$form.find('[name$="country"]').val(address['countryCode'].toUpperCase()).trigger('change');
29316                // retrigger state selection after country has changed
29317                // this results in duplication of the state code, but is a 
29317necessary evil
29318                // for now because sometimes countryCode comes after stateCode
29319                            	
29320            	var selectedStateValue = address['stateCode'];
29321            	if ($form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').length > 0) {
29322            		selectedStateValue = $form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').val();
29323            	}
29324            	
29325            	$form.find('[name$="state"]').val(selectedStateValue);
29326            } else if (field === 'stateCode') {
29327				var selectedStateValue = address['stateCode'];
29328            	if ($form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').length > 0) {
29329            		selectedStateValue = $form.find('[name$="state"] option[label="' + address['stateCode'] + '"]').val();
29330            	}
29331            	
29332            	$form.find('[name$="state"]').val(selectedStateValue);
29333            } else {
29334            	if (address[field] != null) {
29335            		$form.find('[name$="_' + field.replace('Code', '') + '"]').val((''+address[field]).replace(/\^/gi,"'"));
29336            	} else {
29337            		$form.find('[name$="_' + field.replace('Code', '') + '"]').val(address[field]);
29338            	}
29339            }
29340        }
29341    },
29342    /**
29343     * @function
29344     * @description Updates the number of the remaining character
29345     * based on the character limit in a text area
29346     */
29347    limitCharacters: function () {
29348        $('form').find('textarea[data-character-limit]').each(function () {
29349            var characterLimit = $(this).data('character-limit');
29350            var charCountHtml = String.format(Resources.CHAR_LIMIT_MSG,
29351                '<span class="char-remain-count">' + characterLimit + '</span>',
29352                '<span class="char-allowed-count">' + characterLimit + '</span>');
29353            var charCountContainer = $(this).next('div.char-count');
29354            if (charCountContainer.length === 0) {
29355                charCountContainer = $('<div class="char-count"/>').insertAfter($(this));
29356            }
29357            charCountContainer.html(charCountHtml);
29358            // trigger the keydown event so that any existing character data is calculated
29359            $(this).change();
29360        });
29361    },
29362    /**
29363     * @function
29364     * @description Binds the onclick-event to a delete button on a given container,
29365     * which opens a confirmation box with a given message
29366     * @param {String} container The name of element to which the function will be bind
29367     * @param {String} message The message the will be shown upon a click
29368     */
29369    setDeleteConfirmation: function (container, message) {
29370        $(container).on('click', '.delete', function () {
29371            return window.confirm(message);
29372        });
29373    },
29374    /**
29375     * @function
29376     * @description Scrolls a browser window to a given x point
29377     * @param {String} The x coordinate
29378     */
29379    scrollBrowser: function (xLocation) {
29380        $('html, body').animate({scrollTop: xLocation}, 500);
29381    },
29382    
29383    isTouchDevice: function() {
29384    	return ('ontouchstart' in window || navigator.msMaxTouchPoints);
29385    },
29386
29387    isMobile: function () {
29388        var mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];
29389        var    idx = 0;
29390        var isMobile = false;
29391        var userAgent = (navigator.userAgent).toLowerCase();
29392
29393        while (mobileAgentHash[idx] && !isMobile) {
29394            isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);
29395            idx++;
29396        }
29397        return isMobile;
29398    },
29399    
29400    /**
29401     * @function
29402     * @description Validates e-mail address
29403     * @param {String} E-mail address
29404     * @returns {Boolean}
29405     */
29406    validateEmail: function (email) {
29407        var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
29408        return re.test(email);
29409    },
29410    
29411    /**
29412     * @function
29413     * @description Helper method to encode inputs and prevent xss
29414     * @param {String} input value
29415     * @returns {String}
29416     */
29417    htmlEncode: function (value) { 
29418        if (value) {
29419            return jQuery('<div/>').text(value).html(); 
29420        } else {
29421            return '';
29422        }
29423    },
29424    
29425    /**
29426     * Limits a text input to a defined length. 
29427     * @param (String) inputSelector - A jQuery selector for the input to limit.
29428     * @param (Number) lengthLimit - The length to limit the input to.
29429     * @return void
29430     */
29431    limitInputToLength: function (inputSelector, lengthLimit) {
29432        $(inputSelector).on("keyup click", function () {
29433            if (this.value.length > lengthLimit) {
29434                this.value = this.value.substring(0, lengthLimit);
29435    		}
29436    	});
29437    },
29438    
29439    /**
29440     * Detects the credit card type based off of the provided credit card number.
29441     * @param (String) creditCardNumber - The credit card number to detect the type for.
29442     * @return (String) - The type of credit card.
29443     */
29444    getCreditCardType: function (creditCardNumber) {
29445        creditCardNumber = creditCardNumber.replace(/\D/g, '');
29446        
29447        if (creditCardNumber.length >= 4) {
29448        	var creditCardTypes = [
29449	        	{
29450	        		PaymentCard: CreditCardConstants.Visa,
29451	        		PaymentCardDisplayKey: CreditCardConstants.Visa,
29452	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29453	        		Pattern: new RegExp('^4[0-9]{0,}$')
29454	        	},
29455	        	{
29456	        		PaymentCard: CreditCardConstants.AmericanExpress,
29457	        		PaymentCardDisplayKey: CreditCardConstants.AmericanExpress,
29458	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29459	        		Pattern: new RegExp('^3[47][0-9]{0,}$')
29460	        	},
29461	        	{
29462	        		PaymentCard: CreditCardConstants.Diners,
29463	        		PaymentCardDisplayKey: CreditCardConstants.Discover,
29464	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29465	        		Pattern: new RegExp('^3(?:0[0-59]{1}|[689])[0-9]{0,}$')
29466	        	},
29467	        	{
29468	        		PaymentCard: CreditCardConstants.JCB,
29469	        		PaymentCardDisplayKey: CreditCardConstants.JCB,
29470	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29471	        		Pattern: new RegExp('^(?:2131|1800|35)[0-9]{0,}$')
29472	        	},
29473	        	{
29474	        		PaymentCard: CreditCardConstants.Mastercard,
29475	        		PaymentCardDisplayKey: CreditCardConstants.Mastercard,
29476	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29477	        		Pattern: new RegExp('^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$')
29478	        	},
29479	        	{
29480	        		PaymentCard: CreditCardConstants.Discover,
29481	        		PaymentCardDisplayKey: CreditCardConstants.Discover,
29482	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29483	        		Pattern: new RegExp('^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$')
29484	        	},
29485	        	{
29486	        		PaymentCard: CreditCardConstants.Maestro,
29487	        		PaymentCardDisplayKey: CreditCardConstants.Mastercard,
29488	        		PaymentMethod: PaymentMethodConstants.CreditCard,
29489	        		Pattern: new RegExp('^(5[06789]|6)[0-9]{0,}$')
29490	        	},
29491	        	{
29492	        		PaymentCard: CreditCardConstants.BootBarnCredit,
29493	        		PaymentCardDisplayKey: PaymentMethodConstants.AllianceData,
29494	        		PaymentMethod: PaymentMethodConstants.AllianceData,
29495	        		Pattern: new RegExp('^7788[0-9]{0,}$')
29496	        	}
29497	        ]
29498	        
29499	        for (var creditCardTypeKey in creditCardTypes) {
29500	        	var creditCardType = creditCardTypes[creditCardTypeKey];
29501	        	
29502	        	if (creditCardType.Pattern && creditCardNumber.match(creditCardType.Pattern)) {
29503	        		return creditCardType;
29504	        	}
29505	        }
29506        }
29507
29508        return false;
29509    },
29510    
29511    /**
29512     * Masks a provided number.
29513     * @param (Number or String) number - The number to mask.
29514     * @param (Number) lengthToShow - The length of the number to leave unmasked at end.
29515     * @param (String) maskCharacter - The character to use for masking. Defaults to *.
29516     * @return (String) - The type of credit card.
29517     */
29518    getMaskedNumber: function (number, lengthToShow, maskPattern) {
29519    	if (!number) {
29520    		return '';	
29521    	}
29522    	
29523    	lengthToShow = lengthToShow || 4;
29524    	maskPattern = maskPattern || '*';
29525       	
29526       	var numberMasked;
29527    	if (maskPattern.length > 1) {
29528    		numberMasked = maskPattern;
29529    	} else if (maskPattern.length >= 1) {
29530    		numberMasked = new Array(number.length + 1).join(maskPattern);
29531    	}
29532            
29533    	number += '';
29534    	if (number != null && number != '') { 
29535    		numberMasked = numberMasked.substr(0, number.length - lengthToShow); 
29536    		numberMasked += number.substr(number.length - lengthToShow);
29537    	}
29538        
29539        return numberMasked;
29540    },
29541
29542	getPreferenceValueFromWindowLocation: function (preferenceName) {
29543		var hash = window.location.hash.substr(1),
29544			preferenceValue = '',
29545			hashTable = hash.split('&').reduce(function (hashTable, item) {
29546				var parts = item.split('=');
29547				hashTable[parts[0]] = decodeURIComponent(parts[1]).replace(/[\+\s]+/g, ' ');
29548				return hashTable;
29549			}, {});
29550		
29551		for (var i = 1; i <= 5; i++){
29552			if(!hashTable.hasOwnProperty('prefn' + i)){   	
29553				break;
29554			} else if(hashTable['prefn' + i] == preferenceName){
29555				preferenceValue = hashTable['prefv' + i];
29556				break;
29557			}
29558		}
29559		
29560		return preferenceValue;
29561	}
29562};
29563
29564module.exports = util;
29565
29566},{"../../../bc_sheplersbootbarn_core/cartridge/js/PaymentInstrumentConstants":117,"lodash":133}],108:[function(require,module,exports){
29567'use strict';
29568
29569var naPhone = /^\+?\(?([0-9][0-8][0-9])\)?[\-\. ]?([2-9][0-9]{2})[\-\. ]?([0-9]{4})(\s*x[0-9]+)?$/;
29570var internationalNumber = /^(?:\s*\+)?[\d\s\.\-\(\)]+$/;
29571var defaultPostal = /^\d{3,7}$/;
29572var regex = {
29573    phone: {
29574        us: naPhone,
29575        ca: internationalNumber,
29576        fr: internationalNumber,
29577        it: internationalNumber,
29578        jp: internationalNumber,
29579        cn: /.*/,
29580        gb: internationalNumber
29581    },
29582    postal: {
29583        us: /^\d{5}(-\d{4})?$/,
29584        ca: /^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$/i,
29585        fr: /^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/,
29586        it: /^([0-9]){5}$/,
29587        jp: /^([0-9]){3}[-]([0-9]){4}$/,
29588        cn: /^([0-9]){6}$/,
29589        gb: /^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/i
29590    },
29591    notCC: /^(?!(([0-9 -]){13,19})).*$/
29592};
29593// global form validator settings
29594var settings = {
29595    errorClass: 'error',
29596    errorElement: 'span',
29597    onkeyup: false,
29598    onfocusout: function (element) {
29599        if (!this.checkable(element)) {
29600            this.element(element);
29601        }
29602    },
29603    errorPlacement: function (error, element) {
29604        error.appendTo(element.parent());
29605    }
29606};
29607/**
29608 * @function
29609 * @description Validates a given phone number against the countries phone regex
29610 * @param {String} value The phone number which will be validated
29611 * @param {String} el The input field
29612 */
29613var validatePhone = function (value, el) {
29614    var country = $(el).closest('form').find('.country');
29615    if (country.length === 0 || country.val().length === 0 || !regex.phone[country.val().toLowerCase()]) {
29616        return true;
29617    }
29618
29619    var rgx = regex.phone[country.val().toLowerCase()];
29620    var isOptional = this.optional(el);
29621    var isValid = rgx.test($.trim(value));
29622
29623    return isOptional || isValid;
29624};
29625
29626var validatePostal = function (value, el) {
29627    var country = $(el).closest('form').find('.country');
29628    if (country.length === 0 || country.val().length === 0 || !regex.phone[country.val().toLowerCase()]) {
29629        return true;
29630    }
29631
29632    var rgx = regex.postal[country.val().toLowerCase()];
29633    var isOptional = this.optional(el);
29634    var isValid = rgx.test($.trim(value));
29635
29636    return isOptional || isValid;
29637};
29638/**
29639 * @function
29640 * @description Validates that a credit card owner is not a Credit card number
29641 * @param {String} value The owner field which will be validated
29642 * @param {String} el The input field
29643 */
29644var validateOwner = function (value) {
29645    var isValid = regex.notCC.test($.trim(value));
29646    return isValid;
29647};
29648
29649/**
29650 * Add phone validation method to jQuery validation plugin.
29651 * Text fields must have 'phone' css class to be validated as phone
29652 */
29653$.validator.addMethod('phone', validatePhone, Resources.INVALID_PHONE);
29654$.validator.addMethod('postal', validatePostal, Resources.INVALID_ZIP);
29655
29656/**
29657 * Add CCOwner validation method to jQuery validation plugin.
29658 * Text fields must have 'owner' css class to be validated as not a credit card
29659 */
29660$.validator.addMethod('owner', validateOwner, Resources.INVALID_OWNER);
29661
29662/**
29663 * Add gift cert amount validation method to jQuery validation plugin.
29664 * Text fields must have 'gift-cert-amont' css class to be validated
29665 */
29666$.validator.addMethod('gift-cert-amount', function (value, el) {
29667    var isOptional = this.optional(el);
29668    var isValid = (!isNaN(value)) && (parseFloat(value) >= 5) && (parseFloat(value) <= 5000);
29669    return isOptional || isValid;
29670}, Resources.GIFT_CERT_AMOUNT_INVALID);
29671
29672/**
29673 * Add positive number validation method to jQuery validation plugin.
29674 * Text fields must have 'positivenumber' css class to be validated as positivenumber
29675 */
29676$.validator.addMethod('positivenumber', function (value) {
29677    if ($.trim(value).length === 0) { return true; }
29678    return (!isNaN(value) && Number(value) >= 0);
29679}, Resources.VALIDATE_NUMBER);
29680
29681/**
29682 * Add method to check for email extension when validating emails,
29683 * since default settings don't require an extension
29684 */
29685$.validator.addMethod("requireEmailExtension", function(value, element) {
29686    if ($.trim(value).length === 0) { return true; }
29687    return /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value);
29688}, Resources.VALIDATE_EMAIL);
29689
29690/*
29691 * Lets you say "at least X inputs that match selector Y must be filled."
29692 *
29693 * The end result is that neither of these inputs:
29694 *
29695 *  <input class="productinfo" name="partnumber">
29696 *  <input class="productinfo" name="description">
29697 *
29698 *  ...will validate unless at least one of them is filled.
29699 *
29700 * partnumber:  {require_from_group: [1,".productinfo"]},
29701 * description: {require_from_group: [1,".productinfo"]}
29702 *
29703 * options[0]: number of fields that must be filled in the group
29704 * options[1]: CSS selector that defines the group of conditionally required fields
29705 */
29706$.validator.addMethod( "require_from_group", function( value, element, options ) {
29707    var $fields = $(options[1], element.form);
29708    var $fieldsFirst = $fields.eq(0);
29709    var validator = $fieldsFirst.data("valid_req_grp") ? $fieldsFirst.data("valid_req_grp") : $.extend({}, this);
29710    var isValid = $fields.filter(function() {
29711        return validator.elementValue(this);
29712    }).length >= options[0];
29713
29714    // Store the cloned validator for future validation
29715    $fieldsFirst.data("valid_req_grp", validator);
29716
29717    // If element isn't being validated, run each require_from_group field's validation rules
29718    if (!$(element).data("being_validated")) {
29719        $fields.data("being_validated", true);
29720        $fields.each(function() {
29721            validator.element(this);
29722        });
29723        $fields.data("being_validated", false);
29724    }
29725    return isValid;
29726}, $.validator.format("Please fill at least {0} of these fields."));
29727
29728$.extend($.validator.messages, {
29729    required: Resources.VALIDATE_REQUIRED,
29730    remote: Resources.VALIDATE_REMOTE,
29731    email: Resources.VALIDATE_EMAIL,
29732    url: Resources.VALIDATE_URL,
29733    date: Resources.VALIDATE_DATE,
29734    dateISO: Resources.VALIDATE_DATEISO,
29735    number: Resources.VALIDATE_NUMBER,
29736    digits: Resources.VALIDATE_DIGITS,
29737    creditcard: Resources.VALIDATE_CREDITCARD,
29738    equalTo: Resources.VALIDATE_EQUALTO,
29739    maxlength: $.validator.format(Resources.VALIDATE_MAXLENGTH),
29740    minlength: $.validator.format(Resources.VALIDATE_MINLENGTH),
29741    rangelength: $.validator.format(Resources.VALIDATE_RANGELENGTH),
29742    range: $.validator.format(Resources.VALIDATE_RANGE),
29743    max: $.validator.format(Resources.VALIDATE_MAX),
29744    min: $.validator.format(Resources.VALIDATE_MIN)
29745});
29746
29747var validator = {
29748    regex: regex,
29749    settings: settings,
29750    init: function () {
29751        var self = this;
29752        $('form:not(.suppress)').each(function () {
29753            $(this).validate(self.settings);
29754        });
29755    },
29756    initForm: function (f) {
29757        $(f).validate(this.settings);
29758    }
29759};
29760
29761module.exports = validator;
29762
29763},{}],109:[function(require,module,exports){
29764'use strict';
29765
29766/* -------------------------
29767 * LOGIN
29768 * -------------------------
29769 */
29770
29771var loadingIndicator = require('../../../app_storefront_core/cartridge/js/components/LoadingIndicator');
29772var dialog = require('../../../app_storefront_core/cartridge/js/dialog');
29773var loginDialog = require('../../../app_storefront_core/cartridge/js/loginDialog');
29774var recaptcha = require('../../../app_storefront_core/cartridge/js/components/Recaptcha');
29775var util = require('../../../app_storefront_whip/cartridge/js/util'); //util reference
29776var	validator = require('../../../app_storefront_whip/cartridge/js/validator');
29777
29778var GoogleTagManagerClient = require('../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient');
29779var SiteConstants = require('../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants');
29780var TPromise = require('promise');
29781
29782var dataBuffer = {};
29783var options = {};
29784var loaded = {};
29785var loadingIndicatorSettings = {
29786    IsLabelShown: false,
29787    CssClass: 'fullscreen',
29788    BackgroundColor: 'transparent'
29789};
29790var opcLoggedInCustomerAddresses = [];
29791
29792/**
29793 * @private
29794 * @function
29795 * @description init events for the loginPage
29796 */
29797function initLoginEvents() {
29798    //toggle the value of the rememberme checkbox
29799    $(document).on('change', '.login-rememberme input', function () {
29800        if ($(this).prop('checked')) {
29801            $('#rememberme').val('true');
29802        } else {
29803            $('#rememberme').val('false');
29804        }
29805    });
29806    $(document).on('click', '#optin-privacy-policy', function(e) {
29807        e.preventDefault();
29808        location.href = Urls.privacyPage;
29809    });
29810    $(document).on('click', '#reward-learn-more', function(e) {
29811        e.preventDefault();
29812        location.href = Urls.learnFAQ;
29813    });
29814    $(document).on('click', '.movetoaddr', function(e){
29815        e.preventDefault();
29816        $('html, body').animate({
29817            scrollTop: $('.billing-address-form').eq(0).offset().top
29818        }, 1000);
29819    });
29820    $(document).on('click', '#attach-rewards-form label', function() {
29821        $('#attach-rewards-form input').removeAttr('checked');
29822        $(this).siblings('[name="attach-rewards-options"]').attr('checked', 'checked');
29823    });
29824}
29825
29826function validateLoginForm($form, checkFields) {
29827    if (!$form || !checkFields) { return false }
29828
29829    var $emailField = $form.find('input[name*="_username"]');
29830    var $passwordField = $form.find('input[name*="_password"]');
29831    var emailValue = $emailField.length > 0 ? $emailField.val().trim() : null;
29832	var passwordValue = $passwordField.length > 0 ? $passwordField.val().trim() : null;
29833	var formValidation = true;
29834
29835    for (var i = 0; i < checkFields.length; i++) {
29836        var currentField = checkFields[i];
29837
29838        if (currentField.toUpperCase() == 'EMAIL' && (!emailValue || emailValue.length == 0)) {
29839            formValidation = false;
29840            $form.find('input[name*="_username"]').addClass('error');
29841        }
29842        if (currentField.toUpperCase() == 'PASSWORD' && (!passwordValue || passwordValue.length == 0)) {
29843            formValidation = false;
29844            $form.find('input[name*="_password"]').addClass('error');
29845        }
29846    }
29847
29848	if (!formValidation) {
29849        $form.find('.login-dialog-error .invalid-missing-fields').removeClass('d-none');
29850        return false;
29851    } else {
29852        $form.find('.login-dialog-error span').addClass('d-none');
29853        return true;
29854    }
29855}
29856
29857function errorHandling(data, dialogTitle, dialogHtml) {
29858    if (data && typeof data === 'object' && 'message' in data) {
29859        console.log(data.message);
29860    }
29861    dialog.open({
29862        html: dialogHtml,
29863        options: {
29864            title: dialogTitle,
29865            draggable: false,
29866            open: function(event, ui) {
29867                $('body').addClass('dialog-open');
29868            },
29869            close: function(event, ui) {
29870                $('body').removeClass('dialog-open');
29871            }
29872        }
29873    });
29874}
29875
29876// Cordial tracking for loginPage and loginDialog
29877function loginCordialAccountEvent($form, isLoginDialog) {
29878    var username = $form.find('input[name*="_username"]').val().trim();
29879
29880    var auth_data = {
29881        email: username
29882    }
29883
29884    var contact_data = {
29885        'channels': {
29886            'email': {
29887                'address': username
29888            }
29889        }
29890    };
29891
29892    var properties = {
29893        "email": username,
29894        "loginType": isLoginDialog ? "login-dialog" : "login"
29895    };
29896
29897    if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
29898        crdl([
29899            ['contact', auth_data, contact_data],
29900            ['event', 'login', properties]
29901        ]);
29902    }
29903}
29904
29905// Cordial password reset - no account event
29906function passwordResetNoAccountCordialEvent(container) {
29907    if (container && $(container).find('.reset-password .no-account').length > 0) {
29908        var email = $('.no-account').text();
29909
29910		var auth_data = {
29911			email: email
29912		}
29913		var contact_data = {
29914			'channels': {
29915			    'email': {
29916			        'address': email
29917			    }
29918			}
29919		};
29920
29921		var properties = {
29922			"email": email
29923		};
29924
29925		if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
29926			crdl([
29927				['contact', auth_data, contact_data],
29928				['event', 'reset-password-no-account', properties]
29929			]);
29930		}
29931	}
29932}
29933
29934function initializeRecaptchaForLogin() {
29935	renderRecaptchaForLogin();
29936	$(document).on('Account.SubmitLogin', function(event) {
29937		$('#accountLoginButton').click();
29938	});
29939}
29940
29941function renderRecaptchaForLogin() {
29942	if (loaded.Recaptcha && $('#accountLoginButton').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForAccountLogin && options.Recaptcha.GoogleSiteKey) {
29943		dataBuffer.LoginRecaptcha = grecaptcha.render('accountLoginButton', {
29944	        'sitekey': options.Recaptcha.GoogleSiteKey,
29945	        'callback': accountLogin,
29946	        'badge': 'bottomleft'
29947	    });
29948	}
29949}
29950
29951function renderRecaptchaForPasswordReset() {
29952	if (loaded.Recaptcha && $('#request-password-send-btn').length > 0 && options && options.Recaptcha && options.Recaptcha.EnabledForPasswordReset && options.Recaptcha.GoogleSiteKey) {
29953		if (dataBuffer.ResetPasswordRecaptcha) {
29954			grecaptcha.reset(dataBuffer.ResetPasswordRecaptcha);
29955		}
29956
29957		dataBuffer.ResetPasswordRecaptcha = grecaptcha.render('request-password-send-btn', {
29958	        'sitekey': options.Recaptcha.GoogleSiteKey,
29959	        'callback': window.resetPassword,
29960	        'badge': 'bottomleft'
29961	    });
29962	}
29963}
29964
29965var updateCustomerDetails = function (form, name) {
29966    var $form = $(form);
29967
29968    return TPromise.resolve($.ajax({
29969        type: 'POST',
29970        url: util.ajaxUrl(Urls.onePageLogin),
29971        data: $form.serialize() + '&' + name + '=' + name
29972    }));
29973 }
29974
29975function showNotification(text, selector, msgType) {
29976    var $selector = $(selector);
29977	if ($selector.length == 0) {
29978        var $form = $('form.opc-login'),
29979            $selector = $('<span/>', {'class': 'login-notification', 'tabindex': '0'}).wrap('<div class="formfield clearfix error-box"><div class="form-row required"><div class="field-wrapper"></div></div></div>');
29980
29981        $selector.parent().parent().parent().insertBefore('form.opc-login .form-row-button');
29982        $selector = $(selector);
29983    }
29984
29985    $selector.removeClass('success error').addClass(msgType).text(text).focus();
29986}
29987
29988function clearNotification(selector) {
29989     if(selector.length){
29990        $('.formfield.error-box').remove();
29991		selector.removeClass('success error');
29992     }
29993}
29994
29995// GTM and Cordial tagging for cart opc-login
29996function opcLoginTagging($form, callback) {
29997    var email =  $form.find('input[name*="username"]').val();
29998    var auth_data = {
29999        email: email
30000    }
30001    var contact_data = {
30002        'channels': {
30003            'email': {
30004                'address': email
30005            }
30006        }
30007    };
30008    var properties = {
30009        "email": email
30010    };
30011    GoogleTagManagerClient.PushDataLayerForDataLayerType('CustomerLogin', { method: 'cart' }).then(function() {
30012        if (SitePreferences.CORDIAL_ENABLED) {
30013            if (typeof crdl !== 'undefined' && typeof crdl === 'function') {
30014                crdl([
30015                    ['contact', auth_data, contact_data],['event', 'login', properties]
30016                ]);
30017            }
30018        }
30019        if (callback) callback();
30020    });
30021}
30022
30023/**
30024 * Login reporting - increments counters in custom object
30025 * @param {String} type - login type
30026 * @param {String} source - Source of login, which can be original, cart, cart-modal, or wishlist-modal
30027 */
30028function trackLoginActions(type, source) {
30029    if (!type || !source) {
30030        return;
30031    }
30032
30033    $.ajax({
30034        type: 'POST',
30035        url: Urls.loginTracking,
30036        contentType: 'application/json',
30037        data: JSON.stringify({
30038            type: type,
30039            source: source
30040        }),
30041        error: function(jqXHR, textStatus, errorThrown) {
30042            console.log('Login-TrackActions: Error in incrementing login action counters. ERROR TEXT: '+textStatus+', ERROR THROWN: '+errorThrown+'.');
30043        },
30044        success: function(response) {
30045            if (response) {}
30046        }
30047    });
30048}
30049
30050/**
30051 * @private
30052 * @function
30053 * @description init login button event for the loginPage, loginDialog and checkout login
30054 */
30055function initLoginButtons() {
30056    $(document).on('click', '#accountLoginButton', function(event) {
30057        event.preventDefault();
30058        var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with login. Please try again later.</p>';
30059        var $accountLoginButton = $(this);
30060        var $accountLoginForm = $accountLoginButton.closest('form#dwfrm_login');
30061        var formUrl = $accountLoginForm.attr('action');
30062        var validationUrl = $accountLoginForm.attr('validation-url');
30063
30064        // Clear previous form errors
30065        $accountLoginForm.find('input').removeClass('error');
30066        $accountLoginForm.find('.login-dialog-error span').addClass('d-none');
30067
30068        var isValidForm = validateLoginForm($accountLoginForm, ['email']);
30069        var isLoginDialog = formUrl.indexOf('loginDialog') >= 0;
30070        var formData = $accountLoginForm.serialize();
30071
30072        if (!isLoginDialog) {
30073          $('[data-login-source]').attr('data-login-source', 'original'); // For reporting
30074        }
30075
30076        if (isValidForm) {
30077          $('body').loading(loadingIndicatorSettings);
30078          if (window['ownid'] && typeof window['ownidStartOtp'] === 'function') {
30079            if (formUrl.indexOf('SignUp') >= 0) {
30080              const urlParams = new URLSearchParams(window.location.search);
30081              const companyCode = urlParams.get('cc');
30082              const companyKey = urlParams.get('key');
30083              // Make ajax call to check if user exists before starting OTP process
30084              $.ajax({
30085                type: 'POST',
30086                url: validationUrl,
30087                contentType: 'application/json',
30088                data: JSON.stringify({
30089                  email: $accountLoginForm.find('input[name*="_username"]').val().trim(),
30090                  companyCode: companyCode,
30091                  companyKey: companyKey
30092                }),
30093                success: function(data) {
30094                    console.log('Login AJAX call successful. Data returned: ', data);
30095                    if (typeof data === 'object' && 'result' in data) {
30096                        $('body').loading('destroy');
30097                        if (data.result == 'error') {
30098                            $('.error-form.login-dialog-error').removeClass('d-none');
30099                            $('.error-form.login-dialog-error span').removeClass('d-none');
30100                            $('.error-form.login-dialog-error span').html(data.message ? data.message : 'Login has failed. Please try again.');
30101                        } else {
30102                          window.ownidStartOtp();
30103                          setTimeout(() => { $('body').loading('destroy') }, 3000);
30104                        }
30105                    } else {
30106                        $('body').loading('destroy');
30107                        if (isLoginDialog) {
30108                            errorHandling(data, 'Login - Error', dialogHtml);
30109                        } else {
30110                            window.location.href = location.href.split('?')[0];
30111                        }
30112                    }
30113                },
30114                error: function(jqXHR, textStatus, errorThrown) {
30115                    console.log('Login Validation AJAX call failed. Error thrown: ', errorThrown);
30116                    $('body').loading('destroy');
30117                    return;
30118                }
30119              });
30120            } else {
30121              window.ownidStartOtp();
30122              setTimeout(() => { $('body').loading('destroy') }, 3000);
30123            }
30124          }
30125          else {
30126
30127            // Using AJAX instead of default HTML button submit for login page and dialog
30128            $.ajax({
30129              type: 'POST',
30130              url: formUrl,
30131              data: formData,
30132              success: function(data, textStatus, jqXHR) {
30133                  console.log('Login AJAX call successful. Data returned: ', data);
30134                  if (data.hideForm) {
30135                      $('.form-create-account .form-body').hide();
30136                  }
30137                  if (typeof data === 'object' && 'result' in data) {
30138                      if (data.result == 'error') {
30139                          $('body').loading('destroy');
30140                          $('.error-form.login-dialog-error span').removeClass('d-none');
30141                          if (data.successMessage) {
30142                              $('.error-form').addClass('login-dialog-success');
30143                          } else {
30144                              $('.error-form').removeClass('login-dialog-success');
30145                          }
30146                          $('.error-form.login-dialog-error span').html(data.message ? data.message : 'Login has failed. Please try again.');
30147                      } else if (data.result == 'success' || data.result == 'complete') {
30148                          $('body').loading('destroy');
30149                          // 'validateCustomer' property only returns in the data if this is a dialog. Otherwise, rewards validation happens through the URL using loginDialogContinueUrl.
30150                          // If this is a rewards customer, and the cusomter account requires validation...
30151                          if ('validateCustomer' in data && data.validateCustomer) {
30152                              if (SitePreferences.CORDIAL_ENABLED) {
30153                                  loginCordialAccountEvent($accountLoginForm, isLoginDialog);
30154                              }
30155                              var newAttributes = {
30156                                  "data-modal-type": "login",
30157                                  "data-continue-url": 'loginDialogContinueUrl' in data ? data.loginDialogContinueUrl : '',
30158                                  "data-first-name": 'customerFirstName' in data ? data.customerFirstName : '',
30159                                  "data-store-number": 'storeFromProfile' in data ? data.storeFromProfile : '',
30160                                  "data-cart-quantity": 'cartQuantity' in data ? data.cartQuantity : ''
30161                              }
30162                              $('.login-dialog-validate-customer').html(data.validateCustomer);
30163                              $('.login-dialog-validate-customer').addClass('validate-customer-show follow-account-dialog-path').attr(newAttributes);
30164                              // Do this in two places:
30165                              // (1) for login-dialog here because the element is only loaded into page via AJAX,
30166                              // (2) init of page load for regular rewards verification pages through normal login flow
30167                              initAddRulesForCodeInput();
30168                          } else {
30169                              // Fire the cordial login event
30170                              if (SitePreferences.CORDIAL_ENABLED) {
30171                                  loginCordialAccountEvent($accountLoginForm, isLoginDialog);
30172                              }
30173                              trackLoginActions('password', $('[data-login-source]').attr('data-login-source'));
30174
30175                              // Handle follow-up actions accordingly
30176                              $(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: data, isAccountDialog: isLoginDialog, displayMessage: 'login' });
30177                          }
30178                      } else {
30179                          $('body').loading('destroy');
30180                          errorHandling(data, 'Login - Error', dialogHtml);
30181                      }
30182                  } else {
30183                      $('body').loading('destroy');
30184                      if (isLoginDialog) {
30185                          errorHandling(data, 'Login - Error', dialogHtml);
30186                      } else {
30187                          window.location.href = location.href.split('?')[0];
30188                      }
30189                  }
30190              },
30191              error: function(jqXHR, textStatus, errorThrown) {
30192                  console.log('Login AJAX call failed. Error thrown: ', errorThrown);
30193                  $('body').loading('destroy');
30194                  if (!isLoginDialog) {
30195                      window.location.href = location.href.split('?')[0];
30196                  }
30197              }
30198            });
30199          }
30200        }  
30201	  });
30202
30203    $(document).on('click', '.opc-login-submit-button', function(event) {
30204        event.preventDefault();
30205        var $opcLoginButton = $(this);
30206        var $opcLoginForm = $opcLoginButton.closest('form.opc-login');
30207        var notificationSelector = $('.login-notification');
30208
30209        $('body').loading(loadingIndicatorSettings);
30210
30211        $('[data-login-source]').attr('data-login-source', 'cart'); // For reporting
30212        $opcLoginForm.validate().form();
30213        clearNotification(notificationSelector);
30214        updateCustomerDetails($opcLoginForm, $(this).attr('name')).then(function (response) {
30215            if (typeof response != 'object') {
30216                try {
30217                    response = JSON.parse(response);
30218                } catch(e) {
30219                    console.log(e);
30220                    response = {};
30221                }
30222            }
30223            if ('viewModel' in response && 'Customer' in response.viewM
30223odel && response.viewModel.Customer.authenticated == true) {
30224                Customer.authenticated = response.viewModel.Customer.authenticated;
30225                opcLoggedInCustomerAddresses = response.viewModel.Addresses;
30226                if ('validateCustomer' in response) {
30227                    $.ajax({
30228                        type: 'GET',
30229                        url: Urls.renderDialogTemplate,
30230                        error: function(jqXHR, textStatus, errorThrown) {
30231                            $('body').loading('destroy');
30232                            console.log(errorThrown);
30233                        },
30234                        success: function(data) {
30235                            if (data) {
30236                                $('body').loading('destroy');
30237                                opcLoginTagging($opcLoginForm);
30238                                var opcLoginContinueUrl = $('#cart-items-form').length ? Urls.onePage : Urls.account;
30239
30240                                dialog.open({
30241                                    html: data,
30242                                    options: {
30243                                        dialogClass: 'login-dialog',
30244                                        draggable: false,
30245                                        height: 600,
30246                                        open: function() {
30247                                            $('body').addClass('dialog-open');
30248                                            $(this).scrollTop(0);
30249                                            if (SitePreferences.CORDIAL_ENABLED) {
30250                                                loginCordialAccountEvent($opcLoginForm, false);
30251                                            }
30252
30253                                            $('.ui-dialog.login-dialog').css('height', '430px');
30254                                            $('.ui-dialog.login-dialog .login-dialog-forms-container').css('margin', '0 -8px');
30255                                            $('.login-dialog-register-container').hide();
30256
30257                                            var newAttributes = {
30258                                                "data-modal-type": "login",
30259                                                "data-continue-url": opcLoginContinueUrl,
30260                                                "data-first-name": 'customerFirstName' in response ? response.customerFirstName : '',
30261                                                "data-store-number": 'storeFromProfile' in response ? response.storeFromProfile : '',
30262                                                "data-cart-quantity": 'cartQuantity' in response ? response.cartQuantity : '',
30263                                                "data-opc-login": true
30264                                            }
30265                                            $('.login-dialog-validate-customer').html(response.validateCustomer);
30266                                            $('.login-dialog-validate-customer').addClass('validate-customer-show follow-account-dialog-path').attr(newAttributes);
30267
30268                                            initAddRulesForCodeInput();
30269
30270                                            var $passwordReset = $(this).find('.login-dialog-forms-container #password-reset-button');
30271                                            $passwordReset.attr('data-target', $passwordReset.attr('data-target') + '?loginDialog=true');
30272                                        },
30273                                        close: function() {
30274                                            $('body').removeClass('dialog-open');
30275                                        }
30276                                    }
30277                                });
30278                            }
30279                        }
30280                    });
30281                } else {
30282                    $('body').loading('destroy');
30283                    opcLoginTagging($opcLoginForm);
30284                    trackLoginActions('password', $('[data-login-source]').attr('data-login-source'));
30285                    location.href = Urls.onePage;
30286                }
30287            } else {
30288                $('body').loading('destroy');
30289                $('.opc-login .field-wrapper input').addClass('error');
30290                showNotification(Resources.PWD_EMAIL_ERROR, '.login-notification', 'error');
30291            }
30292        }.bind(this));
30293    });
30294}
30295
30296function callNextController(url, callback) {
30297    $.ajax({
30298        type: 'GET',
30299        url: url,
30300        contentType: 'application/json',
30301        error: function(jqXHR, textStatus, errorThrown) {
30302        },
30303        success: function(response) {
30304            if (response) {
30305                if (callback) callback(response);
30306            }
30307        }
30308    });
30309}
30310
30311/**
30312 * @private
30313 * @function
30314 * @description register on-click event for rewards validation > send code button
30315 * @notes changed the button.on('click') to a document.on('click') for HTML that is dynamically loaded in page with AJAX well after initial page load
30316 */
30317function initValidateRewardsAccount() {
30318	$(document).on('click', '#validate-account button#send-code', function(event) {
30319		event.preventDefault();
30320
30321        var $validateAccountForm = $('#validate-account');
30322        var isAccountDialog = $('.login-dialog-validate-customer').hasClass('follow-account-dialog-path');
30323        var sendType = $validateAccountForm.find('input[name=validate-account]:checked').attr('id').indexOf('none') > -1 ? 'none' : 'other';
30324
30325		if (sendType == "none") {
30326			$.ajax({
30327				type: 'post',
30328				url: Urls.createRewardsAccount,
30329				contentType: 'application/json',
30330				data: JSON.stringify({
30331					firstName: $('.customer-account .first-name').text(),
30332					lastName: $('.customer-account .last-name').text(),
30333					emailAddress: $('.customer-account .email-address').text(),
30334					phoneNumber: $('.customer-account .phone-number').text(),
30335					zipCode: $('.customer-account .zip-code').text(),
30336					autoEnroll: false
30337				}),
30338				success: function (data) {
30339					console.log('Rewards account created.');
30340					initVerificationDirection();
30341				}
30342			});
30343		} else {
30344			$.ajax({
30345				url: Urls.sendValidationCode,
30346				dataType: 'JSON',
30347				method: 'POST',
30348				data: {
30349					'sendMethod': $validateAccountForm.find('input[name=validate-account]:checked').val(),
30350					'sendTo': $validateAccountForm.find('input[name=validate-account]:checked').attr('data-value')
30351				},
30352				success : function(data) {
30353					var resultStatus = 'status' in data && data.status != 'pending' ? true : 'result' in data && 'status' in data.result && data.result.status != 'pending' ? true : false;
30354					if (('error' in data && data.error) || resultStatus) {
30355						$('.further-info').hide();
30356						$('.verify-form').hide();
30357						$('.verify-fail').removeClass('hidden');
30358					}
30359					$('.account-recognized').hide();
30360					if (isAccountDialog) {
30361						$('.verify-account').removeClass('hidden').addClass('validate-customer-show');
30362					} else {
30363						$('.verify-account').removeClass('hidden');
30364					}
30365					$('.' + sendType + '-method').removeClass('hidden');
30366				},
30367				complete: function(data, status, xhr) {
30368					if (data.status == 302 && data.responseText) {
30369						location.href = data.responseText;
30370					}
30371				}
30372			});
30373		}
30374	});
30375
30376    $(document).on('click', '#attach-rewards-button', function(event) {
30377        event.preventDefault();
30378        var errorMessage = '<p style="padding: 10px;">Sorry, we were unable to finalize your rewards account updates.</p>';
30379        var $attachRewardsForm = $('#attach-rewards-form');
30380        var $selectedOption = $attachRewardsForm.find('input[name=attach-rewards-options]:checked');
30381        var submittedEmail = $('.login-dialog-passcode-container, .otp-login-form-container').attr('data-email');
30382
30383        if (!$selectedOption) {
30384            $('.attach-rewards-form-errors').removeClass('d-none');
30385            return;
30386        }
30387
30388        $.ajax({
30389            url: Urls.attachRewards,
30390            dataType: 'JSON',
30391            method: 'POST',
30392            data: {
30393                'option': $selectedOption.val(),
30394                'email': submittedEmail
30395            },
30396            success : function(data) {
30397                if (data && data.result && data.result == 'success') {
30398                    console.log('Rewards attachment complete.');
30399                    initVerificationDirection();
30400                } else {
30401                    errorHandling(data, 'Rewards Account - Error', errorMessage);
30402                }
30403            },
30404            error: function(jqXHR, textStatus, errorThrown) {
30405                errorHandling(data, 'Rewards Account - Error', errorMessage);
30406            }
30407        });
30408    });
30409}
30410
30411/**
30412 * @private
30413 * @function
30414 * @description register on-click event for rewards validation > verify code button
30415 */
30416function initVerifyCodeSent() {
30417    $(document).on('click', '#verify-code button#verify-user', function(event) {
30418        event.preventDefault();
30419
30420        var $verifyCodeForm = $('#verify-code');
30421        if (!$verifyCodeForm.valid()) {
30422            return false;
30423        }
30424
30425        $.ajax({
30426            url: Urls.verifySubmittedCode,
30427            dataType: 'JSON',
30428            method: 'POST',
30429            data: {
30430                'code':  $verifyCodeForm.find('input[name=enter-code]', '#verify-code').val(),
30431                'sendTo': $('#validate-account').find('input[name=validate-account]:checked').attr('data-value')
30432            },
30433            success : function(data) {
30434                if (('error' in data && data.error) || ('status' in data && data.status != 'approved')) {
30435                    $('.further-info').hide();
30436                    $('.verify-form').hide();
30437                    $('.verify-fail').removeClass('hidden');
30438                } else {
30439                    console.log('Rewards verification complete.');
30440                    initVerificationDirection();
30441                }
30442            },
30443            complete: function(data, status, xhr) {
30444                if (data.status == 302 && data.responseText) {
30445                    location.href = data.responseText;
30446                }
30447            }
30448        });
30449    });
30450}
30451
30452/**
30453 * @private
30454 * @function
30455 * @description directs the page load or the page redraw after rewards verification screen
30456 */
30457function initVerificationDirection() {
30458    var $validateDialog = $('.login-dialog-validate-customer');
30459    var isAccountDialog = $('.login-dialog-validate-customer').hasClass('follow-account-dialog-path');
30460    var dataModalType, dataNextControllerUrl, dataContinueUrl, dataFirstName, dataStore, dataCartQuantity, isOpcLogin;
30461
30462    if ($validateDialog.length) {
30463        dataModalType = $validateDialog.attr('data-modal-type');
30464        dataNextControllerUrl = $validateDialog.attr('data-next-controller-url');
30465        dataContinueUrl = $validateDialog.attr('data-continue-url');
30466        dataFirstName = $validateDialog.attr('data-first-name');
30467        dataStore = $validateDialog.attr('data-store-number');
30468        dataCartQuantity = $validateDialog.attr('data-cart-quantity');
30469        isOpcLogin = $validateDialog.attr('data-opc-login') == 'true';
30470
30471        if (isOpcLogin) {
30472            dialog.close();
30473            location.href = Urls.onePage;
30474        }
30475        if (isAccountDialog) {
30476            if (dataNextControllerUrl) {
30477                callNextController(dataNextControllerUrl, function(response) {
30478                    if (response && 'result' in response && (response.result == 'success' || response.result == 'complete')) {
30479                        response.customerFirstName = dataFirstName;
30480                        response.storeFromProfile = dataStore;
30481                        response.cartQuantity = dataCartQuantity;
30482                        // Handle follow-up actions accordingly
30483                        $(document).trigger('PostAjaxAuthenticationActions.Initialize', { results: response, isAccountDialog: isAccountDialog, displayMessage: dataModalType });
30484                    }
30485                });
30486            } else {
30487                if (dataContinueUrl) {
30488                    location.href = dataContinueUrl;
30489                } else {
30490                    location.href = Urls.account;
30491                }
30492            }
30493        }
30494    } else {
30495        location.href = Urls.account;
30496    }
30497}
30498
30499/**
30500 * @private
30501 * @function
30502 * @description add rules to code input field
30503 */
30504function initAddRulesForCodeInput() {
30505    $('form#verify-code').validate();
30506    $('#verify-code #enter-code').rules('add', {
30507        required: true,
30508        digits: true,
30509        minlength: 6,
30510        messages: {
30511            required: Resources.VALIDATE_CODE,
30512            minlength: Resources.VALIDATE_CODE
30513        },
30514        normalizer: function(value) {
30515            return $.trim(value);
30516        }
30517    });
30518}
30519
30520/**
30521 * @private
30522 * @function
30523 * @description register on-click event for rewards validation > verify code > resend code link
30524 */
30525function initResendValidationCode() {
30526    $(document).on('click', '.verify-account a.resend-code', function(event) {
30527        event.preventDefault();
30528        if ($('.login-dialog-validate-customer').hasClass('follow-account-dialog-path')) {
30529            $('.verify-account, .further-info span').addClass('hidden');
30530            $('.account-recognized').css('left','-9999px').removeClass('hidden').show().addClass('validate-customer-show');
30531        } else {
30532            location.reload();
30533        }
30534    });
30535}
30536
30537/**
30538 * @private
30539 * @function
30540 * @description register on-click events for reset-password flow
30541 */
30542function initResetPasswordEvents() {
30543    // Show reset password form
30544	$(document).on('click', '#password-reset-button', function(event) {
30545        event.preventDefault();
30546        var $resetPasswordButton = $(this);
30547        var resetDialogUrl = $resetPasswordButton.attr('data-target');
30548        var isLoginDialog = resetDialogUrl.indexOf('loginDialog') >= 0;
30549
30550        if (isLoginDialog) {
30551            $('body').loading(loadingIndicatorSettings);
30552            var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with Password Reset. Please try again later.</p>';
30553
30554            $.ajax({
30555                type: 'GET',
30556                url: resetDialogUrl,
30557                success: function(data, textStatus, jqXHR) {
30558                    if (typeof data === 'object' && 'result' in data) {
30559                        if (data.result == 'complete') {
30560                            $('body').loading('destroy');
30561                            // If we received HTML in the data for displaying the reset password form, then dis
30561play it...
30562                            if ('resetPasswordForm' in data && data.resetPasswordForm) {
30563                                $('.login-dialog-reset-password').html(data.resetPasswordForm);
30564                                $('.login-dialog-reset-password').addClass('reset-password-show');
30565                                $('.login-dialog-reset-password').find('#PasswordResetForm').attr('action', $('.login-dialog-reset-password').find('#PasswordResetForm').attr('action') + '?loginDialog=true');
30566								renderRecaptchaForPasswordReset();
30567                            } else {
30568                                $('body').loading('destroy');
30569                                errorHandling(data, 'Reset Password - Error', dialogHtml);
30570                            }
30571                        } else {
30572                            $('body').loading('destroy');
30573                            errorHandling(data, 'Reset Password - Error', dialogHtml);
30574                        }
30575                    } else {
30576                        $('body').loading('destroy');
30577                        if (isLoginDialog) {
30578                            errorHandling(data, 'Reset Password - Error', dialogHtml);
30579                        } else {
30580                            window.location.href = location.href;
30581                        }
30582                    }
30583                },
30584                error: function(jqXHR, textStatus, errorThrown) {
30585                    $('body').loading('destroy');
30586                    if (!isLoginDialog) {
30587                        window.location.href = location.href;
30588                    }
30589                }
30590            });
30591        } else if (SitePreferences.RESET_POPUP_ENABLED) {
30592			dialog.open({
30593	            url: resetDialogUrl,
30594	            options: {
30595	            	dialogClass: 'resetpwdpopup',
30596	            	width: 385,
30597	                open: function () {
30598						renderRecaptchaForPasswordReset();
30599	                    $(".reset-password-heading-close-btn, .ui-widget-overlay").on('click',function(){
30600	                        $("button.ui-dialog-titlebar-close").click();
30601	                    });
30602	                }
30603	            }
30604			});
30605        } else {
30606            window.location.href = resetDialogUrl;
30607        }
30608	});
30609
30610    // Submit reset password form
30611    $(document).on('click.ResetPassword', '#request-password-send-btn, [name$="_requestpassword_send"]', function(event) {
30612    	if (!options || !options.Recaptcha || !options.Recaptcha.EnabledForPasswordReset) {
30613			resetPassword(event, $(this));
30614		}
30615	});
30616
30617    $(document).on('Account.ResetPassword', function (event, data) {
30618		resetPassword(event, null, true);
30619	});
30620}
30621
30622function resetPassword(event, $resetPasswordSendButton, isRecaptchaEvent) {
30623	if (!$resetPasswordSendButton) {
30624		$resetPasswordSendButton = $('#request-password-send-btn');
30625	}
30626
30627	var $resetPasswordSendForm = $resetPasswordSendButton.closest('form#PasswordResetForm');
30628	var isLoginDialog = $resetPasswordSendForm.attr('action').indexOf('loginDialog') >= 0;
30629
30630	// Try to prevent the form submission as quickly as possible before doing all of the other logic
30631	if (isLoginDialog || (SitePreferences.RESET_POPUP_ENABLED && dialog.isActive())) {
30632		event.preventDefault();
30633	}
30634
30635	validator.init();
30636	var isValidForm = $resetPasswordSendForm.valid();
30637
30638	if (isValidForm) {
30639		if (isLoginDialog) {
30640			$('body').loading(loadingIndicatorSettings);
30641			var dialogHtml = '<p style="padding: 10px;">Oops, something went wrong with Password Reset. Please try again later.</p>';
30642			var formData = $resetPasswordSendForm.serialize();
30643			formData += '&' + $resetPasswordSendButton.attr('name') + '=' + $resetPasswordSendButton.attr('name');
30644
30645			$.ajax({
30646				type: 'POST',
30647				url: $resetPasswordSendForm.attr('action'),
30648				data: formData,
30649				success: function(data, textStatus, jqXHR) {
30650					if (typeof data === 'object' && 'result' in data) {
30651						if (data.result == 'complete') {
30652							$('body').loading('destroy');
30653							// If reset password email was sent, show confirmation within the dialog
30654							if ('resetPasswordConfirmation' in data && data.resetPasswordConfirmation) {
30655								$('.login-dialog-reset-password').html(data.resetPasswordConfirmation);
30656								$('.login-dialog-reset-password').addClass('reset-password-show');
30657								passwordResetNoAccountCordialEvent('.login-dialog-reset-password');
30658							} else {
30659								$('body').loading('destroy');
30660								errorHandling(data, 'Reset Password - Error', dialogHtml);
30661							}
30662						} else {
30663							$('body').loading('destroy');
30664							errorHandling(data, 'Reset Password - Error', dialogHtml);
30665						}
30666					} else {
30667						$('body').loading('destroy');
30668						if (isLoginDialog) {
30669							errorHandling(data, 'Reset Password - Error', dialogHtml);
30670						} else {
30671							window.location.href = location.href;
30672						}
30673					}
30674				},
30675				error: function(jqXHR, textStatus, errorThrown) {
30676					$('body').loading('destroy');
30677					if (!isLoginDialog) {
30678						window.location.href = location.href;
30679					}
30680				}
30681			});
30682		} else if (SitePreferences.RESET_POPUP_ENABLED && dialog.isActive()) {
30683			dialog.submit($resetPasswordSendButton.attr('name'), function() {
30684				passwordResetNoAccountCordialEvent('.resetpwdpopup');
30685			});
30686		} else if (isRecaptchaEvent) {
30687			$resetPasswordSendForm.append('<input type="hidden" name="' + $resetPasswordSendButton.attr('name') + '" value="' + $resetPasswordSendButton.val() + '"/>')
30688			$resetPasswordSendForm.submit();
30689		}
30690	}
30691}
30692
30693module.exports = {
30694    init: function (optionsParameter) {
30695		options = optionsParameter;
30696
30697        initLoginEvents();
30698		if (options && options.Recaptcha && (options.Recaptcha.EnabledForAccountLogin || options.Recaptcha.EnabledForPasswordReset)) {
30699			$(document).on('Recaptcha.Loaded', function(event) {
30700				if (!loaded.Recaptcha) {
30701					loaded.Recaptcha = true;
30702					renderRecaptchaForLogin();
30703					renderRecaptchaForPasswordReset();
30704				}
30705			});
30706
30707			recaptcha.initialize();
30708			initializeRecaptchaForLogin();
30709			renderRecaptchaForPasswordReset();
30710		}
30711		initLoginButtons();
30712        initValidateRewardsAccount();
30713        initVerifyCodeSent();
30714        initResendValidationCode();
30715        initAddRulesForCodeInput();
30716        initResetPasswordEvents();
30717    }
30718};
30719},{"../../../app_storefront_core/cartridge/js/components/LoadingIndicator":25,"../../../app_storefront_core/cartridge/js/components/Recaptcha":29,"../../../app_storefront_core/cartridge/js/dialog":36,"../../../app_storefront_core/cartridge/js/loginDialog":44,"../../../app_storefront_whip/cartridge/js/util":107,"../../../app_storefront_whip/cartridge/js/validator":108,"../../../bc_sheplersbootbarn_core/cartridge/js/SiteConstants":120,"../../../bc_sheplersbootbarn_core/cartridge/js/util/GoogleTagManagerClient":123,"promise":134}],110:[function(require,module,exports){
30720module.exports={"NY":{"005":true,"063":true,"100":true,"101":true,"102":true,"103":true,"104":true,"105":true,"106":true,"107":true,"108":true,"109":true,"110":true,"111":true,"112":true,"113":true,"114":true,"115":true,"116":true,"117":true,"118":true,"119":true,"120":true,"121":true,"122":true,"123":true,"124":true,"125":true,"126":true,"127":true,"128":true,"129":true,"130":true,"131":true,"132":true,"133":true,"134":true,"135":true,"136":true,"137":true,"138":true,"139":true,"140":true,"141":true,"142":true,"143":true,"144":true,"145":true,"146":true,"147":true,"148":true,"149":true},"PR":{"006":true,"007":true,"008":true,"009":true},"VI":{"006":true,"007":true,"008":true,"009":true},"MA":{"010":true,"011":true,"012":true,"013":true,"014":true,"015":true,"016":true,"017":true,"018":true,"019":true,"020":true,"021":true,"022":true,"023":true,"024":true,"025":true,"026":true,"027":true,"055":true},"RI":{"028":true,"029":true},"NH":{"030":true,"031":true,"032":true,"033":true,"034":true,"035":true,"036":true,"037":true,"038":true,"039":true},"VT":{"035":true,"036":true,"037":true,"050":true,"051":true,"052":true,"053":true,"054":true,"056":true,"057":true,"058":true,"059":true},"ME":{"039":true,"040":true,"041":true,"042":true,"043":true,"044":true,"045":true,"046":true,"047":true,"048":true,"049":true},"CT":{"060":true,"061":true,"062":true,"063":true,"064":true,"065":true,"066":true,"067":true,"068":true,"069":true},"NJ":{"070":true,"071":true,"072":true,"073":true,"074":true,"075":true,"076":true,"077":true,"078":true,"079":true,"080":true,"081":true,"082":true,"083":true,"084":true,"085":true,"086":true,"087":true,"088":true,"089":true},"AE":{"090":true,"091":true,"092":true,"093":true,"094":true,"095":true,"096":true,"097":true,"098":true,"099":true},"PA":{"150":true,"151":true,"152":true,"153":true,"154":true,"155":true,"156":true,"157":true,"158":true,"159":true,"160":true,"161":true,"162":true,"163":true,"164":true,"165":true,"166":true,"167":true,"168":true,"169":true,"170":true,"171":true,"172":true,"173":true,"174":true,"175":true,"176":true,"177":true,"178":true,"179":true,"180":true,"181":true,"182":true,"183":true,"184":true,"185":true,"186":true,"187":true,"188":true,"189":true,"190":true,"191":true,"192":true,"193":true,"194":true,"195":true,"196":true,"197":true},"DE":{"197":true,"198":true,"199":true},"DC":{"200":true,"202":true,"203":true,"204":true,"205":true},"VA":{"201":true,"220":true,"221":true,"222":true,"223":true,"224":true,"225":true,"226":true,"227":true,"228":true,"229":true,"230":true,"231":true,"232":true,"233":true,"234":true,"235":true,"236":true,"237":true,"238":true,"239":true,"240":true,"241":true,"242":true,"243":true,"244":true,"245":true,"246":true},"MD":{"206":true,"207":true,"208":true,"209":true,"210":true,"211":true,"212":true,"214":true,"215":true,"216":true,"217":true,"218":true,"219":true,"267":true},"WV":{"246":true,"247":true,"248":true,"249":true,"250":true,"251":true,"252":true,"253":true,"254":true,"255":true,"256":true,"257":true,"258":true,"259":true,"260":true,"261":true,"262":true,"263":true,"264":true,"265":true,"266":true,"267":true,"268":true},"NC":{"270":true,"271":true,"272":true,"273":true,"274":true,"275":true,"276":true,"277":true,"278":true,"279":true,"280":true,"281":true,"282":true,"283":true,"284":true,"285":true,"286":true,"287":true,"288":true,"289":true,"297":true},"SC":{"290":true,"291":true,"292":true,"293":true,"294":true,"295":true,"296":true,"297":true,"298":true,"299":true},"GA":{"298":true,"299":true,"300":true,"301":true,"302":true,"303":true,"304":true,"305":true,"306":true,"307":true,"308":true,"309":true,"310":true,"311":true,"312":true,"313":true,"314":true,"315":true,"316":true,"317":true,"318":true,"319":true,"398":true,"399":true},"FL":{"320":true,"321":true,"322":true,"323":true,"324":true,"325":true,"326":true,"327":true,"328":true,"329":true,"330":true,"331":true,"332":true,"333":true,"334":true,"335":true,"336":true,"337":true,"338":true,"339":true,"341":true,"342":true,"344":true,"346":true,"347":true,"349":true},"AA":{"331":true,"340":true},"AP":{"340":true,"962":true,"963":true,"964":true,"965":true,"966":true},"AL":{"350":true,"351":true,"352":true,"354":true,"355":true,"356":true,"357":true,"358":true,"359":true,"360":true,"361":true,"362":true,"363":true,"364":true,"365":true,"366":true,"367":true,"368":true,"369":true},"MS":{"369":true,"370":true,"386":true,"387":true,"388":true,"389":true,"390":true,"391":true,"392":true,"393":true,"394":true,"395":true,"396":true,"397":true},"TN":{"370":true,"371":true,"372":true,"373":true,"374":true,"375":true,"376":true,"377":true,"378":true,"379":true,"380":true,"381":true,"382":true,"383":true,"384":true,"385":true,"386":true,"723":true},"KY":{"400":true,"401":true,"402":true,"403":true,"404":true,"405":true,"406":true,"407":true,"408":true,"409":true,"410":true,"411":true,"412":true,"413":true,"414":true,"415":true,"416":true,"417":true,"418":true,"420":true,"421":true,"422":true,"423":true,"424":true,"425":true,"426":true,"427":true,"471":true},"OH":{"410":true,"430":true,"431":true,"432":true,"433":true,"434":true,"435":true,"436":true,"437":true,"438":true,"439":true,"440":true,"441":true,"442":true,"443":true,"444":true,"445":true,"446":true,"447":true,"448":true,"449":true,"450":true,"451":true,"452":true,"453":true,"454":true,"455":true,"456":true,"457":true,"458":true,"459":true,"470":true,"472":true},"IN":{"424":true,"460":true,"461":true,"462":true,"463":true,"464":true,"465":true,"466":true,"467":true,"468":true,"469":true,"470":true,"471":true,"472":true,"473":true,"474":true,"475":true,"476":true,"477":true,"478":true,"479":true},"MI":{"480":true,"481":true,"482":true,"483":true,"484":true,"485":true,"486":true,"487":true,"488":true,"489":true,"490":true,"491":true,"492":true,"493":true,"494":true,"495":true,"496":true,"497":true,"498":true,"499":true},"IA":{"500":true,"501":true,"502":true,"503":true,"504":true,"505":true,"506":true,"507":true,"508":true,"509":true,"510":true,"511":true,"512":true,"513":true,"514":true,"515":true,"516":true,"520":true,"521":true,"522":true,"523":true,"524":true,"525":true,"526":true,"527":true,"528":true},"NE":{"515":true,"516":true,"680":true,"681":true,"683":true,"684":true,"685":true,"686":true,"687":true,"688":true,"689":true,"690":true,"691":true,"692":true,"693":true},"IL":{"527":true,"600":true,"601":true,"602":true,"603":true,"604":true,"605":true,"606":true,"607":true,"608":true,"609":true,"610":true,"611":true,"612":true,"613":true,"614":true,"615":true,"616":true,"617":true,"618":true,"619":true,"620":true,"622":true,"623":true,"624":true,"625":true,"626":true,"627":true,"628":true,"629":true,"634":true,"635":true},"WI":{"530":true,"531":true,"532":true,"534":true,"535":true,"537":true,"538":true,"539":true,"540":true,"541":true,"542":true,"543":true,"544":true,"545":true,"546":true,"547":true,"548":true,"549":true},"MN":{"540":true,"550":true,"551":true,"553":true,"554":true,"556":true,"557":true,"558":true,"559":true,"560":true,"561":true,"562":true,"563":true,"564":true,"565":true,"566":true,"567":true},"ND":{"567":true,"580":true,"581":true,"582":true,"583":true,"584":true,"585":true,"586":true,"587":true,"588":true},"SD":{"570":true,"571":true,"572":true,"573":true,"574":true,"575":true,"576":true,"577":true},"MT":{"590":true,"591":true,"592":true,"593":true,"594":true,"595":true,"596":true,"597":true,"598":true,"599":true},"MO":{"620":true,"622":true,"630":true,"631":true,"633":true,"634":true,"635":true,"636":true,"637":true,"638":true,"639":true,"640":true,"641":true,"644":true,"645":true,"646":true,"647":true,"648":true,"649":true,"650":true,"651":true,"652":true,"653":true,"654":true,"655":true,"656":true,"657":true,"658":true},"KS":{"660":true,"661":true,"662":true,"664":true,"665":true,"666":true,"667":true,"668":true,"669":true,"670":true,"671":true,"672":true,"673":true,"674":true,"675":true,"676":true,"677":true,"678":true,"679":true},"LA":{"700":true,"701":true,"703":true,"704":true,"705":true,"706":true,"707":true,"708":true,"710":true,"711":true,"712":true,"713":true,"714":true},"AR":{"716":true,"717":true,"718":true,"719":true,"720":true,"721":true,"722":true,"723":true,"724":true,"725":true,"726":true,"727":true,"728":true,"729":true},"OK":{"730":true,"731":true,"734":true,"735":true,"736":true,"737":true,"738":true,"739":true,"740":true,"741":true,"743":true,"744":true,"745":true,"746":true,"747":true,"748":true,"749":true},"TX":{"733":true,"739":true,"750":true,"751":true,"752":true,"753":true,"754":true,"755":true,"756":true,"757":true,"758":true,"759":true,"760":true,"761":true,"762":true,"763":true,"764":true,"765":true,"766":true,"767":true,"768":true,"769":true,"770":true,"771":true,"772":true,"773":true,"774":true,"775":true,"776":true,"777":true,"778":true,"779":true,"780":true,"781":true,"782":true,"783":true,"784":true,"785":true,"786":true,"787":true,"788":true,"789":true,"790":true,"791":true,"792":true,"793":true,"794":true,"795":true,"796":true,"797":true,"798":true,"799":true,"885":true},"CO":{"800":true,"801":true,"802":true,"803":true,"804":true,"805":true,"806":true,"807":true,"808":true,"809":true,"810":true,"811":true,"812":true,"813":true,"814":true,"815":true,"816":true},"WY":{"820":true,"821":true,"822":true,"823":true,"824":true,"825":true,"826":true,"827":true,"828":true,"829":true,"830":true,"831":true,"834":true},"ID":{"832":true,"833":true,"834":true,"835":true,"836":true,"837":true,"838":true,"979":true,"994":true},"WA":{"838":true,"980":true,"981":true,"982":true,"983":true,"984":true,"985":true,
30720"986":true,"988":true,"989":true,"990":true,"991":true,"992":true,"993":true,"994":true},"UT":{"840":true,"841":true,"842":true,"843":true,"844":true,"845":true,"846":true,"847":true},"AZ":{"850":true,"851":true,"852":true,"853":true,"855":true,"856":true,"857":true,"859":true,"860":true,"863":true,"864":true,"865":true},"NM":{"870":true,"871":true,"872":true,"873":true,"874":true,"875":true,"877":true,"878":true,"879":true,"880":true,"881":true,"882":true,"883":true,"884":true},"NV":{"877":true,"889":true,"890":true,"891":true,"893":true,"894":true,"895":true,"897":true,"898":true,"961":true},"CA":{"900":true,"901":true,"902":true,"903":true,"904":true,"905":true,"906":true,"907":true,"908":true,"910":true,"911":true,"912":true,"913":true,"914":true,"915":true,"916":true,"917":true,"918":true,"919":true,"920":true,"921":true,"922":true,"923":true,"924":true,"925":true,"926":true,"927":true,"928":true,"930":true,"931":true,"932":true,"933":true,"934":true,"935":true,"936":true,"937":true,"938":true,"939":true,"940":true,"941":true,"942":true,"943":true,"944":true,"945":true,"946":true,"947":true,"948":true,"949":true,"950":true,"951":true,"952":true,"953":true,"954":true,"955":true,"956":true,"957":true,"958":true,"959":true,"960":true,"961":true},"HI":{"967":true,"968":true},"AS":{"967":true},"GU":{"969":true},"MH":{"969":true},"OR":{"970":true,"971":true,"972":true,"973":true,"974":true,"975":true,"976":true,"977":true,"978":true,"979":true,"986":true},"AK":{"995":true,"996":true,"997":true,"998":true,"999":true}}
30721},{}],111:[function(require,module,exports){
30722module.exports={"100":"NY","101":"NY","102":"NY","103":"NY","104":"NY","105":"NY","106":"NY","107":"NY","108":"NY","109":"NY","110":"NY","111":"NY","112":"NY","113":"NY","114":"NY","115":"NY","116":"NY","117":"NY","118":"NY","119":"NY","120":"NY","121":"NY","122":"NY","123":"NY","124":"NY","125":"NY","126":"NY","127":"NY","128":"NY","129":"NY","130":"NY","131":"NY","132":"NY","133":"NY","134":"NY","135":"NY","136":"NY","137":"NY","138":"NY","139":"NY","140":"NY","141":"NY","142":"NY","143":"NY","144":"NY","145":"NY","146":"NY","147":"NY","148":"NY","149":"NY","150":"PA","151":"PA","152":"PA","153":"PA","154":"PA","155":"PA","156":"PA","157":"PA","158":"PA","159":"PA","160":"PA","161":"PA","162":"PA","163":"PA","164":"PA","165":"PA","166":"PA","167":"PA","168":"PA","169":"PA","170":"PA","171":"PA","172":"PA","173":"PA","174":"PA","175":"PA","176":"PA","177":"PA","178":"PA","179":"PA","180":"PA","181":"PA","182":"PA","183":"PA","184":"PA","185":"PA","186":"PA","187":"PA","188":"PA","189":"PA","190":"PA","191":"PA","192":"PA","193":"PA","194":"PA","195":"PA","196":"PA","197":"DE","198":"DE","199":"DE","200":"DC","201":"VA","202":"DC","203":"DC","204":"DC","205":"DC","206":"MD","207":"MD","208":"MD","209":"MD","210":"MD","211":"MD","212":"MD","214":"MD","215":"MD","216":"MD","217":"MD","218":"MD","219":"MD","220":"VA","221":"VA","222":"VA","223":"VA","224":"VA","225":"VA","226":"VA","227":"VA","228":"VA","229":"VA","230":"VA","231":"VA","232":"VA","233":"VA","234":"VA","235":"VA","236":"VA","237":"VA","238":"VA","239":"VA","240":"VA","241":"VA","242":"VA","243":"VA","244":"VA","245":"VA","246":"WV","247":"WV","248":"WV","249":"WV","250":"WV","251":"WV","252":"WV","253":"WV","254":"WV","255":"WV","256":"WV","257":"WV","258":"WV","259":"WV","260":"WV","261":"WV","262":"WV","263":"WV","264":"WV","265":"WV","266":"WV","267":"WV","268":"WV","270":"NC","271":"NC","272":"NC","273":"NC","274":"NC","275":"NC","276":"NC","277":"NC","278":"NC","279":"NC","280":"NC","281":"NC","282":"NC","283":"NC","284":"NC","285":"NC","286":"NC","287":"NC","288":"NC","289":"NC","290":"SC","291":"SC","292":"SC","293":"SC","294":"SC","295":"SC","296":"SC","297":"SC","298":"GA","299":"GA","300":"GA","301":"GA","302":"GA","303":"GA","304":"GA","305":"GA","306":"GA","307":"GA","308":"GA","309":"GA","310":"GA","311":"GA","312":"GA","313":"GA","314":"GA","315":"GA","316":"GA","317":"GA","318":"GA","319":"GA","320":"FL","321":"FL","322":"FL","323":"FL","324":"FL","325":"FL","326":"FL","327":"FL","328":"FL","329":"FL","330":"FL","331":"AA","332":"FL","333":"FL","334":"FL","335":"FL","336":"FL","337":"FL","338":"FL","339":"FL","340":"AP","341":"FL","342":"FL","344":"FL","346":"FL","347":"FL","349":"FL","350":"AL","351":"AL","352":"AL","354":"AL","355":"AL","356":"AL","357":"AL","358":"AL","359":"AL","360":"AL","361":"AL","362":"AL","363":"AL","364":"AL","365":"AL","366":"AL","367":"AL","368":"AL","369":"MS","370":"TN","371":"TN","372":"TN","373":"TN","374":"TN","375":"TN","376":"TN","377":"TN","378":"TN","379":"TN","380":"TN","381":"TN","382":"TN","383":"TN","384":"TN","385":"TN","386":"TN","387":"MS","388":"MS","389":"MS","390":"MS","391":"MS","392":"MS","393":"MS","394":"MS","395":"MS","396":"MS","397":"MS","398":"GA","399":"GA","400":"KY","401":"KY","402":"KY","403":"KY","404":"KY","405":"KY","406":"KY","407":"KY","408":"KY","409":"KY","410":"OH","411":"KY","412":"KY","413":"KY","414":"KY","415":"KY","416":"KY","417":"KY","418":"KY","420":"KY","421":"KY","422":"KY","423":"KY","424":"IN","425":"KY","426":"KY","427":"KY","430":"OH","431":"OH","432":"OH","433":"OH","434":"OH","435":"OH","436":"OH","437":"OH","438":"OH","439":"OH","440":"OH","441":"OH","442":"OH","443":"OH","444":"OH","445":"OH","446":"OH","447":"OH","448":"OH","449":"OH","450":"OH","451":"OH","452":"OH","453":"OH","454":"OH","455":"OH","456":"OH","457":"OH","458":"OH","459":"OH","460":"IN","461":"IN","462":"IN","463":"IN","464":"IN","465":"IN","466":"IN","467":"IN","468":"IN","469":"IN","470":"IN","471":"IN","472":"IN","473":"IN","474":"IN","475":"IN","476":"IN","477":"IN","478":"IN","479":"IN","480":"MI","481":"MI","482":"MI","483":"MI","484":"MI","485":"MI","486":"MI","487":"MI","488":"MI","489":"MI","490":"MI","491":"MI","492":"MI","493":"MI","494":"MI","495":"MI","496":"MI","497":"MI","498":"MI","499":"MI","500":"IA","501":"IA","502":"IA","503":"IA","504":"IA","505":"IA","506":"IA","507":"IA","508":"IA","509":"IA","510":"IA","511":"IA","512":"IA","513":"IA","514":"IA","515":"NE","516":"NE","520":"IA","521":"IA","522":"IA","523":"IA","524":"IA","525":"IA","526":"IA","527":"IL","528":"IA","530":"WI","531":"WI","532":"WI","534":"WI","535":"WI","537":"WI","538":"WI","539":"WI","540":"MN","541":"WI","542":"WI","543":"WI","544":"WI","545":"WI","546":"WI","547":"WI","548":"WI","549":"WI","550":"MN","551":"MN","553":"MN","554":"MN","556":"MN","557":"MN","558":"MN","559":"MN","560":"MN","561":"MN","562":"MN","563":"MN","564":"MN","565":"MN","566":"MN","567":"ND","570":"SD","571":"SD","572":"SD","573":"SD","574":"SD","575":"SD","576":"SD","577":"SD","580":"ND","581":"ND","582":"ND","583":"ND","584":"ND","585":"ND","586":"ND","587":"ND","588":"ND","590":"MT","591":"MT","592":"MT","593":"MT","594":"MT","595":"MT","596":"MT","597":"MT","598":"MT","599":"MT","600":"IL","601":"IL","602":"IL","603":"IL","604":"IL","605":"IL","606":"IL","607":"IL","608":"IL","609":"IL","610":"IL","611":"IL","612":"IL","613":"IL","614":"IL","615":"IL","616":"IL","617":"IL","618":"IL","619":"IL","620":"MO","622":"MO","623":"IL","624":"IL","625":"IL","626":"IL","627":"IL","628":"IL","629":"IL","630":"MO","631":"MO","633":"MO","634":"MO","635":"MO","636":"MO","637":"MO","638":"MO","639":"MO","640":"MO","641":"MO","644":"MO","645":"MO","646":"MO","647":"MO","648":"MO","649":"MO","650":"MO","651":"MO","652":"MO","653":"MO","654":"MO","655":"MO","656":"MO","657":"MO","658":"MO","660":"KS","661":"KS","662":"KS","664":"KS","665":"KS","666":"KS","667":"KS","668":"KS","669":"KS","670":"KS","671":"KS","672":"KS","673":"KS","674":"KS","675":"KS","676":"KS","677":"KS","678":"KS","679":"KS","680":"NE","681":"NE","683":"NE","684":"NE","685":"NE","686":"NE","687":"NE","688":"NE","689":"NE","690":"NE","691":"NE","692":"NE","693":"NE","700":"LA","701":"LA","703":"LA","704":"LA","705":"LA","706":"LA","707":"LA","708":"LA","710":"LA","711":"LA","712":"LA","713":"LA","714":"LA","716":"AR","717":"AR","718":"AR","719":"AR","720":"AR","721":"AR","722":"AR","723":"AR","724":"AR","725":"AR","726":"AR","727":"AR","728":"AR","729":"AR","730":"OK","731":"OK","733":"TX","734":"OK","735":"OK","736":"OK","737":"OK","738":"OK","739":"TX","740":"OK","741":"OK","743":"OK","744":"OK","745":"OK","746":"OK","747":"OK","748":"OK","749":"OK","750":"TX","751":"TX","752":"TX","753":"TX","754":"TX","755":"TX","756":"TX","757":"TX","758":"TX","759":"TX","760":"TX","761":"TX","762":"TX","763":"TX","764":"TX","765":"TX","766":"TX","767":"TX","768":"TX","769":"TX","770":"TX","771":"TX","772":"TX","773":"TX","774":"TX","775":"TX","776":"TX","777":"TX","778":"TX","779":"TX","780":"TX","781":"TX","782":"TX","783":"TX","784":"TX","785":"TX","786":"TX","787":"TX","788":"TX","789":"TX","790":"TX","791":"TX","792":"TX","793":"TX","794":"TX","795":"TX","796":"TX","797":"TX","798":"TX","799":"TX","800":"CO","801":"CO","802":"CO","803":"CO","804":"CO","805":"CO","806":"CO","807":"CO","808":"CO","809":"CO","810":"CO","811":"CO","812":"CO","813":"CO","814":"CO","815":"CO","816":"CO","820":"WY","821":"WY","822":"WY","823":"WY","824":"WY","825":"WY","826":"WY","827":"WY","828":"WY","829":"WY","830":"WY","831":"WY","832":"ID","833":"ID","834":"ID","835":"ID","836":"ID","837":"ID","838":"WA","840":"UT","841":"UT","842":"UT","843":"UT","844":"UT","845":"UT","846":"UT","847":"UT","850":"AZ","851":"AZ","852":"AZ","853":"AZ","855":"AZ","856":"AZ","857":"AZ","859":"AZ","860":"AZ","863":"AZ","864":"AZ","865":"AZ","870":"NM","871":"NM","872":"NM","873":"NM","874":"NM","875":"NM","877":"NV","878":"NM","879":"NM","880":"NM","881":"NM","882":"NM","883":"NM","884":"NM","885":"TX","889":"NV","890":"NV","891":"NV","893":"NV","894"
30722:"NV","895":"NV","897":"NV","898":"NV","900":"CA","901":"CA","902":"CA","903":"CA","904":"CA","905":"CA","906":"CA","907":"CA","908":"CA","910":"CA","911":"CA","912":"CA","913":"CA","914":"CA","915":"CA","916":"CA","917":"CA","918":"CA","919":"CA","920":"CA","921":"CA","922":"CA","923":"CA","924":"CA","925":"CA","926":"CA","927":"CA","928":"CA","930":"CA","931":"CA","932":"CA","933":"CA","934":"CA","935":"CA","936":"CA","937":"CA","938":"CA","939":"CA","940":"CA","941":"CA","942":"CA","943":"CA","944":"CA","945":"CA","946":"CA","947":"CA","948":"CA","949":"CA","950":"CA","951":"CA","952":"CA","953":"CA","954":"CA","955":"CA","956":"CA","957":"CA","958":"CA","959":"CA","960":"CA","961":"CA","962":"AP","963":"AP","964":"AP","965":"AP","966":"AP","967":"AS","968":"HI","969":"MH","970":"OR","971":"OR","972":"OR","973":"OR","974":"OR","975":"OR","976":"OR","977":"OR","978":"OR","979":"OR","980":"WA","981":"WA","982":"WA","983":"WA","984":"WA","985":"WA","986":"OR","988":"WA","989":"WA","990":"WA","991":"WA","992":"WA","993":"WA","994":"WA","995":"AK","996":"AK","997":"AK","998":"AK","999":"AK","005":"NY","063":"CT","006":"VI","007":"VI","008":"VI","009":"VI","010":"MA","011":"MA","012":"MA","013":"MA","014":"MA","015":"MA","016":"MA","017":"MA","018":"MA","019":"MA","020":"MA","021":"MA","022":"MA","023":"MA","024":"MA","025":"MA","026":"MA","027":"MA","055":"MA","028":"RI","029":"RI","030":"NH","031":"NH","032":"NH","033":"NH","034":"NH","035":"VT","036":"VT","037":"VT","038":"NH","039":"ME","050":"VT","051":"VT","052":"VT","053":"VT","054":"VT","056":"VT","057":"VT","058":"VT","059":"VT","040":"ME","041":"ME","042":"ME","043":"ME","044":"ME","045":"ME","046":"ME","047":"ME","048":"ME","049":"ME","060":"CT","061":"CT","062":"CT","064":"CT","065":"CT","066":"CT","067":"CT","068":"CT","069":"CT","070":"NJ","071":"NJ","072":"NJ","073":"NJ","074":"NJ","075":"NJ","076":"NJ","077":"NJ","078":"NJ","079":"NJ","080":"NJ","081":"NJ","082":"NJ","083":"NJ","084":"NJ","085":"NJ","086":"NJ","087":"NJ","088":"NJ","089":"NJ","090":"AE","091":"AE","092":"AE","093":"AE","094":"AE","095":"AE","096":"AE","097":"AE","098":"AE","099":"AE"}
30723},{}],112:[function(require,module,exports){
30724'use strict';
30725
30726exports.VerifyAddress = verifyAddress;
30727exports.AutocompleteAddress = autocompleteAddress;
30728exports.SetAddressFromAddressVerificationDialog = setAddressFromAddressVerificationDialog;
30729exports.BuildAddressObjectFromCheckoutForm = buildAddressObjectFromCheckoutForm;
30730exports.BuildAddressObjectFromAddressAutocompleteSuggestion = buildAddressObjectFromAddressAutocompleteSuggestion;
30731exports.BuildAddressObjectFromAddressVerifyResponse = buildAddressObjectFromAddressVerifyResponse;
30732exports.CompareAddressObjects = compareAddressObjects;
30733
30734var dataBuffer = {};
30735
30736function verifyAddress($form, options) {
30737	return new Promise(function(resolve, reject) {
30738		var addressVerificationPayload = {
30739			primary_line: $form.find('input[name$="_address1"]').val() || '',
30740			secondary_line: $form.find('input[name$="_address2"]').val() || '',
30741			city: $form.find('input[name$="_city"]').val() || '',
30742			state: $form.find('select[name$="_state"]').val() || ''
30743		};
30744		if ($form.find('input[name$="_postal"]').length) {
30745			addressVerificationPayload.zip_code = $form.find('input[name$="_postal"]').val();
30746		} else if ($form.find('input[name$="_postalCode"]').length) {
30747			addressVerificationPayload.zip_code = $form.find('input[name$="_postalCode"]').val();
30748		}
30749		addressVerificationPayload[options.CSRFToken.Name] = options.CSRFToken.Token;
30750
30751		$.ajax({
30752			method: 'POST',
30753			url: Urls.Lob.AddressVerify,
30754			contentType: 'application/json',
30755			data: JSON.stringify(addressVerificationPayload),
30756			dataType: 'json',
30757			success: function(data) {
30758				if (!data || !data.ServiceSuccessfullyCalled) {
30759					resolve(data);
30760				} else {
30761					data.Status = 'deliverable';
30762					data.Match = true;
30763					if (data.InquiryResult) {
30764						data.AddressOriginal = buildAddressObjectFromCheckoutForm($form);
30765						data.AddressVerified = buildAddressObjectFromAddressVerifyResponse(data);
30766						data.AddressComparison = compareAddressObjects(data.AddressOriginal, data.AddressVerified);
30767						data.Match = data.AddressComparison.Match;
30768						data.Status = data.InquiryResult.deliverability;
30769					}
30770
30771					if (data.Status == 'deliverable' && data.Match) {
30772						resolve(data);
30773					} else {
30774						reject(data);
30775					}
30776				}
30777			},
30778			error: function(data) {
30779				resolve(data);
30780			},
30781		});
30782	});
30783}
30784
30785function autocompleteAddress($form, $field, options) {
30786	return new Promise(function(resolve, reject) {
30787		var addressAutocompletePayload = {
30788			address_prefix: $form.find('input[name$="_address1"]').val() || '',
30789			city: $form.find('input[name$="_city"]').val() || '',
30790			state: $form.find('select[name$="_state"]').val() || '',
30791			zip_code: $form.find('input[name$="postal"]').val() || $form.find('input[name$="postalCode"]').val() || ''
30792		};
30793		addressAutocompletePayload[options.CSRFToken.Name] = options.CSRFToken.Token;
30794
30795		if (dataBuffer && dataBuffer['AutocompleteAddressAjax'] && dataBuffer['AutocompleteAddressAjax'].readyState !== 4) {
30796			dataBuffer['AutocompleteAddressAjax'].abort();
30797		}
30798
30799		dataBuffer['AutocompleteAddressAjax'] = $.ajax({
30800			method: 'POST',
30801			url: Urls.Lob.AddressAutocomplete,
30802			contentType: 'application/json',
30803			data: JSON.stringify(addressAutocompletePayload),
30804			dataType: 'json',
30805			success: function(data) {
30806				if (!data || !data.ServiceSuccessfullyCalled) {
30807					resolve(data);
30808				} else {
30809					data.$form = $form;
30810					data.$field = $field;
30811
30812					resolve(data);
30813				}
30814			}
30815		})
30816	});
30817}
30818
30819function setAddressFromAddressVerificationDialog($form, addressData) {
30820	if (addressData.FirstName) {
30821		$form.find('input[name$="_firstName"]').val(addressData.FirstName);
30822	}
30823	if (addressData.LastName) {
30824		$form.find('input[name$="_lastName"]').val(addressData.LastName);
30825	}
30826	if (addressData.PrimaryLine) {
30827		$form.find('input[name$="_address1"]').val(addressData.PrimaryLine);
30828		$form.find('input[name$="_address2"]').val(addressData.SecondaryLine);
30829		$form.find('input[name$="_city"]').val(addressData.City);
30830		$form.find('select[name$="_state"]').val(addressData.StateCode);
30831		if ($form.find('input[name$="_postal"]').length) {
30832			$form.find('input[name$="_postal"]').val(addressData.PostalCode);
30833		} else if ($form.find('input[name$="_postalCode"]').length) {
30834			$form.find('input[name$="_postalCode"]').val(addressData.PostalCode);
30835		}
30836		if (addressData.CountryCode) {
30837			$form.find('select[name$="_countryCode"]').val(addressData.CountryCode);
30838		}
30839	}
30840	if (addressData.Phone) {
30841		$form.find('input[name$="phone"]').val(addressData.Phone);
30842	}
30843	if (addressData.Email) {
30844		$form.find('input[name$="emailAddress"]').val(addressData.Email);
30845	}
30846}
30847
30848function buildAddressObjectFromCheckoutForm($form) {
30849	var addressObject = {};
30850	
30851	addressObject.PrimaryLine = $form.find('input[name$="_address1"]').val();
30852	addressObject.SecondaryLine = $form.find('input[name$="_address2"]').val();
30853	addressObject.City = $form.find('input[name$="_city"]').val();
30854	addressObject.StateCode = $form.find('select[id$="_state"]').val();
30855	if ($form.find('input[name$="_postal"]').length) {
30856		addressObject.PostalCode = $form.find('input[name$="_postal"]').val();
30857	} else if ($form.find('input[name$="_postalCode"]').length) {
30858		addressObject.PostalCode = $form.find('input[name$="_postalCode"]').val();
30859	}
30860
30861	return addressObject;
30862}
30863
30864function buildAddressObjectFromAddressAutocompleteSuggestion(data) {
30865	var addressObject = {};
30866
30867	if (data) {
30868		addressObject.PrimaryLine = data.primary_line || '';
30869		addressObject.SecondaryLine = data.secondary_line || '';
30870		addressObject.StateCode = data.state || '';
30871		addressObject.PostalCode = data.zip_code || '';
30872		addressObject.City = data.city || '';
30873	}
30874
30875	return addressObject;
30876}
30877
30878function buildAddressObjectFromAddressVerifyResponse(data) {
30879	var addressObject = {};
30880
30881	if (data && data.InquiryResult) {
30882		addressObject.PrimaryLine = data.InquiryResult.primary_line || '';
30883		addressObject.SecondaryLine = data.InquiryResult.secondary_line || '';
30884		addressObject.StateCode = data.InquiryResult.components.state || '';
30885		addressObject.PostalCode = data.InquiryResult.components.zip_code || '';
30886		addressObject.City = data.InquiryResult.components.city || '';
30887	}
30888
30889	return addressObject;
30890}
30891
30892function compareAddressObjects(addressOriginal, addressVerified) {
30893	var addressObject = {};
30894
30895	if (addressOriginal && addressVerified) {
30896		addressObject.PrimaryLine = compareAddressElement(addressOriginal.PrimaryLine, addressVerified.PrimaryLine);
30897		addressObject.SecondaryLine = compareAddressElement(addressOriginal.SecondaryLine, addressVerified.SecondaryLine);
30898		addressObject.StateCode = compareAddressElement(addressOriginal.StateCode, addressVerified.StateCode);
30899		addressObject.PostalCode = compareAddressElement(addressOriginal.PostalCode, addressVerified.PostalCode);
30900		addressObject.City = compareAddressElement(addressOriginal.City, addressVerified.City);
30901	}
30902
30903	var fieldsDifferent = [];
30904	for (var addressElement in addressObject) {
30905		if (!addressObject[addressElement]) {
30906			fieldsDifferent[fieldsDifferent.length] = addressElement;
30907		}
30908	}
30909	addressObject.FieldsDifferent = fieldsDifferent;
30910	addressObject.Match = fieldsDifferent.length == 0;
30911
30912	return addressObject;
30913}
30914
30915function compareAddressElement(addressElementOriginal, addressElementVerified) {
30916	addressElementOriginal = addressElementOriginal || '';
30917	addressElementVerified = addressElementVerified || '';
30918
30919	return addressElementOriginal.trim().toLowerCase() == addressElementVerified.trim().toLowerCase();
30920}
30921},{}],113:[function(require,module,exports){
30922'use strict';
30923
30924exports.ResponseCodes = {
30925	AlreadyInBasket: 'ALREADY_IN_BASKET'
30926}
30927
30928exports.User = {
30929	SelectedCustomerShippingMethod: 'selectedCustomerShippingMethod',
30930	SelectedStoreShippingMethod: 'selectedStoreShippingMethod'
30931}
30932
30933exports.ShippingMethods = {
30934	Bopis: 'Bopis',
30935	LocalDelivery: 'LocalDelivery',
30936	ShipToStore: 'Ship-to-Store',
30937	ShipToStorePickup: 'My_Store_Free_Shipping',
30938	ShipToStorePickupForWhip: 'WHIP FREE Ship-to-store',
30939	Virtual: 'Virtual'
30940}
30941},{}],114:[function(require,module,exports){
30942'use strict';
30943
30944exports.Events = {
30945	Cart: {
30946		AddToCart: {
30947			Name: 'AddToCart'
30948		},
30949		RemoveFromCart: {
30950			Name: 'RemoveFromCart'
30951		}
30952	}
30953}
30954
30955exports.EventTypes = {
30956	Minus: 'minus',
30957	Plus: 'plus'
30958}
30959},{}],115:[function(require,module,exports){
30960'use strict';
30961
30962exports.GetPosition = getPosition;
30963
30964function getPosition() {
30965	return new Promise(function(resolve) {
30966		if (navigator.geolocation) {
30967			var geolocationSuccess = function(position) {	
30968				resolve(position);
30969			};
30970
30971			var geolocationError = function() {
30972				resolve();
30973			};
30974
30975			navigator.geolocation.getCurrentPosition(geolocationSuccess, geolocationError);
30976		} else {
30977			resolve();
30978		}
30979	});
30980}
30981},{}],116:[function(require,module,exports){
30982'use strict';
30983
30984exports.SlickSlider = {
30985	Options: {
30986		Single: {
30987			accessibility:  true,
30988			infinite: 		true,
30989			dots: 			false,
30990			arrows: 		false,
30991			slidesToShow: 	1,
30992			slidesToScroll: 1,
30993			responsive: 	[
30994				{
30995					breakpoint: 767,
30996					settings: 	{
30997						dots: 	true,
30998						arrows: true
30999					}
31000				},
31001				{
31002					breakpoint: 1200
31003				}
31004			]
31005		},
31006		Thumbnail: {
31007			accessibility:  true,
31008			draggable: false,
31009			infinite: false,
31010			focusOnSelect: 	false,
31011			dots: false,
31012			arrows: false,
31013			slidesToShow: 7,
31014			slidesToScroll: 7,
31015			responsive: 	[
31016				{
31017					breakpoint: 767
31018				},
31019				{
31020					breakpoint: 1200,
31021					settings: {
31022						slidesToShow: 4,
31023						slidesToScroll: 4
31024					}
31025				}
31026			]
31027		},
31028		CategoryLanding: {
31029			accessibility:  true,
31030			draggable: true,
31031			infinite: true,
31032			focusOnSelect: 	false,
31033			dots: false,
31034			arrows: true,
31035			slidesToShow: 4,
31036			slidesToScroll: 4,
31037			responsive: 	[
31038				{
31039					breakpoint: 767
31040				},
31041				{
31042					breakpoint: 1200,
31043					settings: {
31044						slidesToShow: 3,
31045						slidesToScroll: 3
31046					}
31047				}
31048			]
31049		}
31050	}
31051}
31052},{}],117:[function(require,module,exports){
31053'use strict';
31054
31055exports.PaymentMethods = {
31056	AllianceData: 'Alliance_Data',
31057	ApplePay: 'DW_APPLE_PAY',
31058	BillMeLater: 'BML',
31059	CreditCard: 'CREDIT_CARD',
31060	GiftCertificate: 'GIFT_CERTIFICATE',
31061	PayPal: 'PayPal',
31062	Globale: 'GLOBALE',
31063	RewardsCredit: 'REWARDS_CREDIT',
31064	LoyaltyCredit: 'LOYALTY_CREDIT',
31065	StoredValueSolutions: 'SVS_Gift_Card',
31066	Affirm: 'Affirm',
31067	PaymentVoucher: 'B2B_Payment_Voucher',
31068	ShopPay: 'ShopPay',
31069	WorkforceVoucher: 'Workforce_Voucher'
31070};
31071
31072exports.CreditCards = {
31073	AmericanExpress: 'Amex',
31074	BootBarnCredit: 'Boot Barn Credit Card',
31075	Discover: 'Discover',
31076	JCB: 'JCB',
31077	Maestro: 'Maestro',
31078	Mastercard: 'Master',
31079	MastercardSeries2: 'Master Series 2',
31080	Visa: 'Visa'
31081};
31082},{}],118:[function(require,module,exports){
31083'use strict';
31084
31085exports.RefinementsUIVersions = {
31086	Updated: 'Updated',
31087	Legacy: 'Legacy'
31088}
31089},{}],119:[function(require,module,exports){
31090'use strict';
31091
31092module.exports = {
31093	Validation: {
31094		Email: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
31095		OrderNumber: /^[a-zA-Z]{0,6}[-._]{0,1}[0-9]{6,12}$/,
31096		PostalCode: /^[a-zA-Z0-9][a-zA-Z0-9\- ]{0,10}[a-zA-Z0-9]$/,
31097		PostOfficeBox: /((((p[\s\.]?)\s?[o\s][\.]?)\s?)|(post\s?office\s?)|(postal\s?)|(post\s?))((box|bin|b\.?)?\s?((num|number|no|#)\.?)?\s?\d+)/i,
31098		ZipCode: /^[0-9]{5}(-[0-9]{4})*$/,
31099		RequiredExcludingPunctuation: /^[#]*[^_\W]+/,
31100		GiftCardNumberLength: /(\d)/g,
31101		GiftCardNumberValues: /^[-\s0-9]*$/,
31102	}
31103}
31104
31105// Email pattern is from W3C spec, same that jQuery Validate uses: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
31106// Postal Code pattern is from https://stackoverflow.com/questions/578406/what-is-the-ultimate-postal-code-and-zip-regex
31107// PO Box pattern is modified from https://stackoverflow.com/questions/5680050/po-box-regular-expression-validation
31108},{}],120:[function(require,module,exports){
31109'use strict';
31110
31111exports.AddressTypes = {
31112	Customer: 'CustomerAddress',
31113	Store: 'StoreAddress'
31114}
31115
31116exports.CountryCodes = {
31117	UnitedStates: 'US',
31118	Canada: 'CA'
31119}
31120
31121exports.StateCodes = {
31122	NewYork: 'NY'
31123}
31124
31125exports.KeyCodes = {
31126	Enter: 13,
31127	Tab: 9,
31128	ArrowLeft: 37,
31129	ArrowUp: 38,
31130	ArrowRight: 39,
31131	ArrowDown: 40
31132}
31133
31134exports.Responsive = {
31135	Widths: {
31136		Mobile: 767,
31137		Tablet: 1024
31138	}
31139}
31140
31141exports.SiteIds = {
31142	BootBarnUS: 'bootbarn_us',
31143	SheplersUS: 'sheplers_us',
31144	CountryOutfittersUS: 'country_outfitters_us',
31145	BootBarnRspUS: 'bootbarn_rsp_us',
31146	BootBarnPayments: 'bootbarn_payments',
31147	BootBarnB2BUS: 'bootbarn_b2b_us',
31148	Rangefinder: 'rangefinder',
31149	BootBarnWorkforceUS: 'bootbarn_workforce_us'
31150}
31151
31152exports.ProductRecommendationSystems = {
31153	Einstein: 'Einstein',
31154	Relevant: 'Relevant'
31155}
31156},{}],121:[function(require,module,exports){
31157'use strict';
31158
31159exports.Lodash = {
31160	Settings: {
31161		interpolate: /{{([\s\S]+?)}}/g
31162	}
31163}
31164},{}],122:[function(require,module,exports){
31165'use strict';
31166
31167exports.Settings = {
31168	Global: {
31169	    errorClass: 'error',
31170	    errorElement: 'span',
31171	    onkeyup: false,
31172	    errorPlacement: function (error, element) {
31173			var $validationErrorContainer = element.closest('form').find('.validation-error-container');
31174			if ($validationErrorContainer.length > 0) {
31175				error.appendTo($validationErrorContainer);
31176	    	} else if (element.attr('error-container')) {
31177	    		error.appendTo($(element.attr('error-container')));
31178	    	} else {
31179	    		error.appendTo(element.parent());
31180	    	}
31181	    }
31182	},
31183	Checkout: {
31184	    errorPlacement: function(error, element) {
31185	    	if (element.attr('error-container')) {
31186	    		error.appendTo($(element.attr('error-container')));
31187	    	} else {
31188	    		error.appendTo(element.parent());
31189	    	}
31190
31191			var $validationErrorContainer = element.closest('form').find('.validation-error-container');
31192			if ($validationErrorContainer.length > 0) {
31193				var $element = $(element);
31194				var $elementValidationError = $validationErrorContainer.find('.validation-error[field="' + $element.attr('id') + '"]');
31195				if ($elementValidationError.length <= 0) {
31196					var validationErrorText = $('<span class="validation-error" field="' + $element.attr('id') + '" />').text($element.closest('.formfield').find('label span').text() + ' is required.');
31197					$validationErrorContainer.append(validationErrorText);
31198				}
31199			}
31200	    },
31201		onfocusout: function(element) {
31202			if (!this.checkable(element)) {
31203				var isElementValid = this.element(element);
31204				var $element = $(element);
31205				var $validationErrorContainer = $element.closest('form').find('.validation-error-container');
31206				var $elementValidationError = $validationErrorContainer.find('.validation-error[field="' + $element.attr('id') + '"]');
31207				if (isElementValid && $elementValidationError.length > 0) {
31208					$elementValidationError.remove();
31209				} else if (!isElementValid && $elementValidationError.length <= 0) {
31210					var validationErrorText = $('<span class="validation-error" field="' + $element.attr('id') + '" />').text($element.closest('.formfield').find('label span').text() + ' is required.');
31211					$validationErrorContainer.append(validationErrorText);
31212				}
31213			}
31214		}
31215	}
31216}
31217
31218exports.Rules = {
31219	RequiredExcludingPunctuation: {
31220		requiredExcludingPunctuation: true
31221	},
31222	ValidateAddressForPostOfficeBox: {
31223		postOfficeBox: true
31224	},
31225	ValidateAddressForPostOfficeBoxOnly: {
31226		postOfficeBoxOnly: true
31227	},
31228	Email: {
31229		email: true,
31230		minlength: 1
31231	},
31232	OrderNumber: {
31233		orderNumber: true
31234	},
31235	PostalCode: {
31236		postalCode: true
31237	},
31238	PostalCodeAndStateCode: {
31239		postalCode: true,
31240		zipCodeForStateCode: true
31241	},
31242	ZipCode: {
31243		zipCode: true
31244	},
31245	ZipCodeAndStateCode: {
31246		zipCode: true,
31247		zipCodeForStateCode: true
31248	},
31249	StateCodeForCaliforniaStateLine: {
31250		stateCodeForCaliforniaStateLine: true
31251	},
31252	GiftCard: {
31253		Number: {
31254			required: true,
31255			giftCardNumberLength: true,
31256			giftCardNumberValues: true
31257		},
31258		Pin: {
31259			required: true,
31260			digits: true,
31261			minlength: 4,
31262			maxlength: 8,
31263			messages: {
31264				minlength: $.validator.format(Resources.Validation.Messages.Numbers.MinimumLength),
31265				maxlength: $.validator.format(Resources.Validation.Messages.Numbers.MaximumLength)
31266			}
31267		}
31268	},
31269	RewardsCredit: {
31270		Number: {
31271			required: true,
31272			digits: true,
31273			minlength: 16,
31274			maxlength: 16,
31275			messages: {
31276				minlength: $.validator.format(Resources.Validation.Messages.Numbers.MinimumLength),
31277				maxlength: $.validator.format(Resources.Validation.Messages.Numbers.MaximumLength)
31278			}
31279		}
31280	},
31281	LoyaltyCredit: {
31282		Number: {
31283			required: true,
31284			digits: true,
31285			minlength: 1,
31286			maxlength: 10,
31287			messages: {
31288				minlength: $.validator.format(Resources.Validation.Messages.Numbers.MinimumLength),
31289				maxlength: $.validator.format(Resources.Validation.Messages.Numbers.MaximumLength)
31290			}
31291		}
31292	},
31293	PaymentVoucher: {
31294		Number: {
31295			required: true,
31296			digits: false,
31297			minlength: 1,
31298			maxlength: 28,
31299			messages: {
31300				minlength: $.validator.format(Resources.Validation.Messages.Numbers.MinimumLength),
31301				maxlength: $.validator.format(Resources.Validation.Messages.Numbers.MaximumLength)
31302			}
31303		}
31304	}
31305}
31306},{}],123:[function(require,module,exports){
31307'use strict';
31308
31309exports.PushDataLayerForPage = pushDataLayerForPage;
31310exports.PushDataLayerForDataLayerType = pushDataLayerForDataLayerType;
31311exports.PushDataLayerForEvent = pushDataLayerForEvent;
31312
31313var dataLayerBaseObjects = {
31314	UniversalAnalytics: {
31315		ViewPromotion: {
31316
31317		}
31318	},
31319	GoogleAnalytics4: {
31320		ViewPromotion: {
31321			event: 'view_promotion'
31322		}
31323	}
31324}
31325
31326function pushDataLayerForPage(page) {
31327	window.dataLayer = window.dataLayer || [];
31328
31329	if (page) {
31330		if (page.options && page.options.GoogleTagManager && page.options.GoogleTagManager.EnableGoogleAnalytics4) {
31331			pushDataLayerForPageForGoogleAnalytics4(page);
31332		}
31333		pushDataLayerForPageForUniversalAnalytics(page);
31334	}
31335}
31336
31337function pushDataLayerForPageForGoogleAnalytics4(page) {
31338	window.dataLayer = window.dataLayer || [];
31339
31340	if (page) {
31341		if (page.options && page.options.GoogleTagManager && page.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4) {
31342			var dataLayer;
31343			if (page.ns == 'orderconfirmation') {
31344				dataLayer = page.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4;
31345				dataLayer.items = getProductDataLayer('data-product-ga4-json');
31346				pushDataLayer(dataLayer);
31347				$(document).trigger('DataLayer.OrderComplete', {
31348					dataLayer: dataLayer,
31349					options: page.options
31350				});
31351			} else {
31352				// This should be activated when UA is removed.
31353				// $.ajax({
31354				// 	url: Urls.GoogleTagManager.GetDataLayer,
31355				// 	type: 'POST',
31356				// 	data: {
31357				// 		DataLayerType: 'General',
31358				// 		DataLayerVersion: 'GoogleAnalytics4'
31359				// 	},
31360				// 	dataType: 'json',
31361				// 	success: function(data) {
31362				// 		dataLayer = $.extend(page.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4, data);
31363				// 		window.dataLayer.push(dataLayer);
31364				// 	},
31365				// 	error: function() {
31366						
31367				// 	}
31368				// });
31369			}
31370		}
31371		
31372		if (page.ns != 'orderconfirmation') {
31373			var payload = {};
31374
31375			var dataLayerPageType;
31376			if (page.options && page.options.GoogleTagManager && page.options.GoogleTagManager.PageType) {
31377				dataLayerPageType = page.options.GoogleTagManager.PageType;
31378			} else {
31379				dataLayerPageType = page.ns;
31380			}		
31381			payload.Page = dataLayerPageType;	
31382
31383			if (page.options && page.options.GoogleTagManager) {
31384				payload.Options = JSON.stringify(page.options.GoogleTagManager);
31385				payload.DataLayerVersion = 'GoogleAnalytics4';
31386			}
31387			
31388			$.ajax({
31389				url: Urls.GoogleTagManager.GetDataLayer,
31390				type: 'POST',
31391				data: payload,
31392				dataType: 'json',
31393				success: function(data) {
31394					if (page.options && page.options.GoogleTagManager && page.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4) {
31395						data = $.extend(page.options.GoogleTagManager.DataLayerCacheForGoogleAnalytics4, data);
31396					}
31397					if (page.ns == 'product') {
31398						if (!data.value && data.items && data.items.length > 0) {
31399							data.currency = data.items[0].currency;
31400							data.value = data.items[0].price;
31401						}
31402					} else if (page.ns == 'search') {
31403						data.items = getProductDataLayer('data-product-ga4-json', { Container: $('.search-result-content') });
31404					} else if (page.ns == 'cart' || page.ns == 'onepcheckout_groups') {
31405						data.items = getProductDataLayer('data-product-ga4-json');
31406						if (!data.value && data.items && data.items.length > 0) {
31407							var value = 0;
31408							for (var itemKey in data.items) {
31409								var item = data.items[itemKey];
31410								value += item.price;
31411							}
31412							data.value = value;
31413						}
31414					}
31415					pushDataLayer(data);
31416				},
31417				error: function() {
31418					
31419				}
31420			});
31421		}
31422	}
31423}
31424
31425function pushDataLayerForPageForUniversalAnalytics(page) {
31426	window.dataLayer = window.dataLayer || [];
31427
31428	if (page) {
31429		if (page.options && page.options.GoogleTagManager && page.options.GoogleTagManager.DataLayerCache) {
31430			var dataLayer;
31431			if (page.ns == 'orderconfirmation') {
31432				dataLayer = page.options.GoogleTagManager.DataLayerCache;
31433				dataLayer.ecommerce = dataLayer.ecommerce || {};
31434				dataLayer.ecommerce.purchase = dataLayer.ecommerce.purchase || {};
31435				dataLayer.ecommerce.purchase.products = getProductDataLayer();
31436				pushDataLayer(dataLayer);
31437			} else {
31438				$.ajax({
31439					url: Urls.GoogleTagManager.GetDataLayer,
31440					type: 'POST',
31441					data: {
31442						DataLayerType: 'General',
31443						DataLayerVersion: 'UniversalAnalytics'
31444					},
31445					dataType: 'json',
31446					success: function(data) {
31447						dataLayer = $.extend(page.options.GoogleTagManager.DataLayerCache, data);
31448						pushDataLayer(dataLayer);
31449					},
31450					error: function() {
31451						
31452					}
31453				});
31454			}
31455		}
31456		
31457		if (page.ns != 'orderconfirmation') {
31458			var payload = {
31459				Page: page.ns
31460			}
31461			
31462			if (page.options && page.options.GoogleTagManager) {
31463				payload.Options = JSON.stringify(page.options.GoogleTagManager);
31464				payload.DataLayerVersion = 'UniversalAnalytics';
31465			}
31466			
31467			$.ajax({
31468				url: Urls.GoogleTagManager.GetDataLayer,
31469				type: 'POST',
31470				data: payload,
31471				dataType: 'json',
31472				success: function(data) {
31473					if (page.ns == 'search') {
31474						data.ecommerce = data.ecommerce || {};
31475						data.ecommerce.impressions = getProductDataLayer(null, { Container: $('.search-result-content') });
31476					} else if (page.ns == 'product') {
31477						data.ecommerce = data.ecommerce || {};
31478						data.ecommerce.detail = data.ecommerce.detail || {};
31479						data.ecommerce.detail.products = getProductDataLayer();
31480					} else if (page.ns == 'cart' || page.ns == 'onepcheckout_groups') {
31481						data.ecommerce = data.ecommerce || {};
31482						data.ecommerce.cartContents = getProductDataLayer();
31483					}
31484					pushDataLayer(data);
31485				},
31486				error: function() {
31487					
31488				}
31489			});
31490		}
31491	}
31492}
31493
31494function pushDataLayerForDataLayerType(dataLayerType, dataObject, options) {
31495	var dataObject = dataObject || {};
31496
31497	return new Promise(function(resolve, reject) {
31498		var ajaxCompleteStatus = {
31499			GoogleAnalytics4: '',
31500			UniversalAnalytics: ''
31501		}
31502
31503		var ajaxComplete = function() {
31504			if (ajaxCompleteStatus.UniversalAnalytics == 'success' && ajaxCompleteStatus.GoogleAnalytics4 == 'success') {
31505				resolve();
31506			} else if (ajaxCompleteStatus.UniversalAnalytics && ajaxCompleteStatus.GoogleAnalytics4 && (ajaxCompleteStatus.UniversalAnalytics == 'error' || ajaxCompleteStatus.GoogleAnalytics4 == 'error')) {
31507				reject();
31508			}
31509		}
31510
31511		if (dataLayerType != 'Event' && dataLayerType != 'ViewPromotion') {
31512			pushDataLayerForDataLayerTypeForDataLayerVersion(dataLayerType, dataObject.UniversalAnalytics || Object.assign({}, dataObject), 'UniversalAnalytics', options).then(function() {
31513				ajaxCompleteStatus.UniversalAnalytics = 'success';
31514				ajaxComplete();
31515			}).catch(function() {
31516				ajaxCompleteStatus.UniversalAnalytics = 'error';
31517				ajaxComplete();
31518			});
31519		}
31520		pushDataLayerForDataLayerTypeForDataLayerVersion(dataLayerType, dataObject.GoogleAnalytics4 || Object.assign({}, dataObject), 'GoogleAnalytics4', options).then(function() {
31521			ajaxCompleteStatus.GoogleAnalytics4 = 'success';
31522			ajaxComplete();
31523		}).catch(function() {
31524			ajaxCompleteStatus.GoogleAnalytics4 = 'error';
31525			ajaxComplete();
31526		});;
31527	});
31528}
31529
31530function pushDataLayerForDataLayerTypeForDataLayerVersion(dataLayerType, dataObject, dataLayerVersion, options) {
31531	var dataLayer;
31532	var dataLayerType = dataLayerType || 'General';
31533	var dataLayerVersion = dataLayerVersion || 'UniversalAnalytics';
31534	var dataObject = dataObject || {};
31535
31536	return new Promise(function(resolve, reject) {
31537		if (dataLayerType) {
31538			if (dataLayerBaseObjects[dataLayerVersion] && dataLayerBaseObjects[dataLayerVersion][dataLayerType]) {
31539				dataLayer = $.extend(dataLayerBaseObjects[dataLayerVersion][dataLayerType], dataObject);
31540				dataLayer = buildDataLayer(dataLayerType, dataLayerVersion, dataLayer, options);
31541				pushDataLayer(dataLayer);
31542				resolve(dataLayer);
31543			} else {
31544				var payload = {
31545					DataLayerType: dataLayerType,
31546					DataLayerVersion: dataLayerVersion
31547				}
31548				if (options) {
31549					payload.Options = JSON.stringify(options);
31550				}
31551				$.ajax({
31552					url: Urls.GoogleTagManager.GetDataLayer,
31553					type: 'POST',
31554					data: payload,
31555					dataType: 'json',
31556					success: function(data) {
31557						if (data && !$.isEmptyObject(data)) {
31558							dataLayer = $.extend(data, dataObject);
31559							dataLayer = buildDataLayer(dataLayerType, dataLayerVersion, dataLayer, options);
31560							pushDataLayer(dataLayer);
31561
31562							if (dataLayerType == 'ViewOrderConfirmation' && dataLayerVersion == 'GoogleAnalytics4') {
31563								$(document).trigger('DataLayer.OrderComplete', {
31564									dataLayer: dataLayer,
31565									options: options
31566								});
31567							}
31568						}
31569						resolve(dataLayer);
31570					},
31571					error: function() {
31572						reject();
31573					}
31574				});
31575			}
31576		} else {
31577			reject();
31578		}
31579	});
31580}
31581
31582function pushDataLayerForEvent(eventName, dataObject, dataLayerType, dataLayerVersion) {
31583	var dataObject = dataObject || {};
31584
31585	return new Promise(function(resolve, reject) {
31586		if (eventName) {
31587			$.ajax({
31588				url: Urls.GoogleTagManager.GetDataLayer,
31589				type: 'POST',
31590				data: {
31591					DataLayerType: dataLayerType || 'General',
31592					DataLayerVersion: dataLayerVersion || 'UniversalAnalytics'
31593				},
31594				dataType: 'json',
31595				success: function(data) {
31596					var dataLayer = $.extend(dataObject, data);
31597					dataLayer.event = eventName;
31598					pushDataLayer(dataLayer);
31599					resolve(dataLayer);
31600				},
31601				error: function() {
31602					reject();
31603				}
31604			});
31605		} else {
31606			reject();
31607		}
31608	});
31609}
31610
31611function decorateDataLayerWithProductData(dataLayer, options) {
31612	var universalAnalyticsKey = options && options.UniversalAnalyticsKey ? options.UniversalAnalyticsKey : '';
31613
31614	if (options && options.DecorateDataLayerWithProductDataKey) {
31615		dataLayer[options.DecorateDataLayerWithProductDataKey] = getProductDataLayer('data-product-ga4-json', options);
31616	} else if (options && options.DataLayerVersion == 'GoogleAnalytics4') {
31617		dataLayer.items = getProductDataLayer('data-product-ga4-json', options);
31618	} else {
31619		dataLayer.ecommerce = dataLayer.ecommerce || {};
31620		dataLayer.ecommerce[universalAnalyticsKey] = dataLayer.ecommerce[universalAnalyticsKey] || {};
31621		dataLayer.ecommerce[universalAnalyticsKey].products = getProductDataLayer(null, options);
31622	}
31623}
31624
31625function getProductDataLayer(attributeIdParameter, options) {
31626	var dataLayerProducts = window.dataLayerProducts || [];
31627	var attributeId = attributeIdParameter || 'data-product-gtm-json';
31628	var container = options && options.Container ? options.Container : '';
31629	var containerSelector = options && options.ContainerSelector ? options.ContainerSelector : '';
31630	var filterSelector = options && options.FilterSelector ? options.FilterSelector : '';
31631
31632	var $productCards;
31633	if (container) {
31634		$productCards = container.find('[' + attributeId + ']' + filterSelector);
31635	} else if (containerSelector) {
31636		$productCards = $(containerSelector).find('[' + attributeId + ']' + filterSelector);
31637	} else {
31638		$productCards = $('[' + attributeId + ']' + filterSelector);
31639	}
31640	$productCards.each(function(index, element) {
31641		var $element = $(element);
31642
31643		var slickIndex = null;
31644		var isValid = true;
31645		if ($element.closest('[data-slick-index]').length > 0) {
31646			slickIndex = parseInt($element.closest('[data-slick-index]').data('slick-index'), 10) + 1;
31647			if (slickIndex < 1) {
31648				isValid = false;
31649			}
31650		}
31651
31652		if (isValid) {
31653			var jsonObject = JSON.parse($element.attr(attributeId));
31654			if (attributeId == 'data-product-gtm-json') {
31655				jsonObject.position = index;
31656			} else {
31657				jsonObject.index = index;
31658			}
31659			dataLayerProducts.push(jsonObject);
31660		}
31661	});
31662		
31663	return dataLayerProducts;
31664}
31665
31666function buildDataLayer(dataLayerType, dataLayerVersion, dataLayer, options) {
31667	if (dataLayerType == 'AddPaymentInfo') {
31668		decorateDataLayerWithProductData(dataLayer, {
31669			DataLayerVersion: dataLayerVersion,
31670			ContainerSelector: options && options.ContainerSelector ? options.ContainerSelector : ''
31671		});
31672	} else if (dataLayerType == 'AddShippingInfo') {
31673		decorateDataLayerWithProductData(dataLayer, {
31674			DataLayerVersion: dataLayerVersion,
31675			ContainerSelector: options && options.ContainerSelector ? options.ContainerSelector : ''
31676		});
31677	} else if (dataLayerType == 'AddToCart') {
31678		if ((dataLayer.event == 'add_to_cart' && (!dataLayer.items || dataLayer.items.length == 0)) || (dataLayer.event == 'addToCart' && (!dataLayer.ecommerce || !dataLayer.ecommerce || !dataLayer.ecommerce.add || !dataLayer.ecommerce.add.products || dataLayer.ecommerce.add.products.length == 0))) {
31679			decorateDataLayerWithProductData(dataLayer, {
31680				DataLayerVersion: dataLayerVersion,
31681				ContainerSelector: options && options.ContainerSelector ? options.ContainerSelector : '',
31682				FilterSelector: '.pdp-browse',
31683				UniversalAnalyticsKey: 'add'
31684			});
31685		}
31686		if (dataLayer.items && dataLayer.items.length > 0) {
31687			if (!dataLayer.items[0].quantity) {
31688				dataLayer.items[0].quantity = 1;
31689			}
31690			dataLayer.currency = dataLayer.items[0].currency;
31691			dataLayer.value = dataLayer.items[0].price;
31692		}
31693	} else if (dataLayerType == 'RemoveFromCart') {
31694		if (dataLayer.items && dataLayer.items.length > 0) {
31695			if (!dataLayer.items[0].quantity) {
31696				dataLayer.items[0].quantity = 1;
31697			}
31698			dataLayer.currency = dataLayer.items[0].currency;
31699			dataLayer.value = dataLayer.items[0].price;
31700		}
31701	} else if (dataLayerType == 'SelectPromotion') {
31702		decorateDataLayerWithProductData(dataLayer, {
31703			DataLayerVersion: dataLayerVersion,
31704			Container: options && options.Container ? options.Container : ''
31705		});
31706	} else if (dataLayerType == 'ViewItem') {
31707			decorateDataLayerWithProductData(dataLayer, {
31708				DataLayerVersion: dataLayerVersion,
31709				ContainerSelector: options && options.ContainerSelector ? options.ContainerSelector : '',
31710				FilterSelector: '.pdp-browse',								
31711				UniversalAnalyticsKey: 'detail'
31712			});
31713	} else if (dataLayerType == 'ViewPromotion') {
31714		decorateDataLayerWithProductData(dataLayer, $.extend({
31715			DataLayerVersion: dataLayerVersion
31716		}, options));
31717	} else if (options && options.DecorateDataLayerWithProductData) {
31718		decorateDataLayerWithProductData(dataLayer, $.extend({
31719			DataLayerVersion: dataLayerVersion
31720		}, options));
31721	}
31722
31723	return dataLayer;
31724}
31725
31726function pushDataLayer(dataLayer) {
31727	var isCartPage = pageContext && pageContext.options && pageContext.options.isCartPage;
31728	var isCheckoutPage = pageContext && pageContext.options && pageContext.options.isCheckoutPage;
31729	var isConfirmationPage = pageContext && pageContext.options && pageContext.options.isConfirmationPage;
31730	var reactDataLayerPages = pageContext && pageContext.options && pageContext.options.MobileApp && pageContext.options.MobileApp.ReactDataLayerPages ? pageContext.options.MobileApp.ReactDataLayerPages : [];
31731	var sendReactEventForCart = isCartPage && reactDataLayerPages.indexOf('Cart') > -1;
31732	var sendReactEventForCheckout = isCheckoutPage && reactDataLayerPages.indexOf('Checkout') > -1;
31733	var sendReactEventForConfirmation = isConfirmationPage && reactDataLayerPages.indexOf('Confirmation') > -1;
31734
31735	if ('ReactNativeWebView' in window && (sendReactEventForCart || sendReactEventForCheckout || sendReactEventForConfirmation)) {
31736		triggerReactWebviewEvent(dataLayer.event, dataLayer);
31737	} else {
31738		window.dataLayer.push(dataLayer);
31739	}
31740}
31741
31742function triggerReactWebviewEvent(action, dataLayer) {
31743	if ('ReactNativeWebView' in window) {
31744		var message =  {
31745			action: action
31746		}
31747		if (dataLayer) {
31748			message.dataLayer = dataLayer;
31749		}
31750
31751		window.ReactNativeWebView.postMessage(JSON.stringify(message));
31752	}
31753}
31754},{}],124:[function(require,module,exports){
31755'use strict';
31756
31757var RegexValidationPatterns = require('../../js/RegexPatterns').Validation;
31758var StateToZipCodeMap = require('../../data/StateToZipCodeMap.json');
31759
31760exports.ValidateRequiredExcludingPunctuation = validateRequiredExcludingPunctuation;
31761exports.ValidateOrderNumber = validateOrderNumber;
31762exports.ValidateAddressForPostOfficeBox = validateAddressForPostOfficeBox;
31763exports.ValidateAddressForPostOfficeBoxOnly = validateAddressForPostOfficeBoxOnly;
31764exports.ValidatePostalCode = validatePostalCode;
31765exports.ValidateZipCode = validateZipCode;
31766exports.ValidateZipCodePrefixForStateCode = validateZipCodePrefixForStateCode;
31767exports.ValidateZipCodePrefixForStateCodeValues = validateZipCodePrefixForStateCodeValues;
31768exports.ValidateStateCodeForCaliforniaStateLine = validateStateCodeForCaliforniaStateLine;
31769exports.ValidateGiftCardNumberLength = validateGiftCardNumberLength;
31770exports.ValidateGiftCardNumberValues = validateGiftCardNumberValues;
31771
31772function validateRequiredExcludingPunctuation(value, element) {
31773    return RegexValidationPatterns.RequiredExcludingPunctuation.test((value + '').trim());
31774};
31775
31776function validateOrderNumber(value, element) {
31777    return RegexValidationPatterns.OrderNumber.test((value + '').trim());
31778};
31779
31780function validateAddressForPostOfficeBox(value, element) {
31781	return !RegexValidationPatterns.PostOfficeBox.test((value + '').trim());
31782}
31783
31784function validateAddressForPostOfficeBoxOnly(value, element) {
31785	var address1 = $(element).closest('form').find('input[name$="_address1"]').val();
31786	var address2 = $(element).closest('form').find('input[name$="_address2"]').val();
31787
31788	return (!RegexValidationPatterns.PostOfficeBox.test((address1 + '').trim()) && !RegexValidationPatterns.PostOfficeBox.test((address2 + '').trim())) || (!address1 || !address2);
31789}
31790
31791function validatePostalCode(value, element) {
31792    return RegexValidationPatterns.PostalCode.test((value + '').trim());
31793};
31794
31795function validateZipCode(value, element) {
31796    return RegexValidationPatterns.ZipCode.test((value + '').trim());
31797};
31798
31799function validateZipCodePrefixForStateCode(value, element) {
31800	var zipCodePrefix = value.substr(0,3);
31801	var stateCode = $(element).closest('form').find('select[name$="_states_state"]').val().toUpperCase();
31802	if (stateCode && StateToZipCodeMap && StateToZipCodeMap[stateCode]) {
31803		return StateToZipCodeMap[stateCode][zipCodePrefix];
31804	}
31805
31806    return true;
31807};
31808
31809function validateZipCodePrefixForStateCodeValues(zipCode, stateCode) {
31810	var zipCodePrefix = zipCode.substr(0,3);
31811	if (StateToZipCodeMap && StateToZipCodeMap[stateCode]) {
31812		return StateToZipCodeMap[stateCode][zipCodePrefix];
31813	}
31814
31815    return true;
31816};
31817
31818function validateStateCodeForCaliforniaStateLine(value, element) {
31819	var stateCodeForStore = $(element).closest('[store-state]').attr('store-state');
31820
31821	if ((value != 'CA' && stateCodeForStore != 'CA') || value == stateCodeForStore) {
31822		return true;
31823	}
31824
31825	return false;
31826};
31827
31828function validateGiftCardNumberLength(value, element) {
31829	var digits = (value + '').trim().match(RegexValidationPatterns.GiftCardNumberLength);
31830	return digits && digits.length == 19;
31831};
31832
31833function validateGiftCardNumberValues(value, element) {
31834	return RegexValidationPatterns.GiftCardNumberValues.test((value + '').trim());
31835};
31836},{"../../data/StateToZipCodeMap.json":110,"../../js/RegexPatterns":119}],125:[function(require,module,exports){
31837'use strict';
31838
31839exports.SlickSlider = {
31840	Options: {
31841		Single: {
31842			infinite: 		true,
31843			dots: 			false,
31844			arrows: 		false,
31845			slidesToShow: 	1,
31846			slidesToScroll: 1,
31847			responsive: 	[
31848				{
31849					breakpoint: 767,
31850					settings: 	{
31851						dots: 	true,
31852						arrows: true
31853					}
31854				},
31855				{
31856					breakpoint: 1200
31857				}
31858			]
31859		},
31860		Thumbnail: {
31861			draggable: true,
31862			infinite: false,
31863			focusOnSelect: 	false,
31864			dots: false,
31865			arrows: false,
31866			slidesToShow: 7,
31867			slidesToScroll: 7,
31868			responsive: 	[
31869				{
31870					breakpoint: 767
31871				},
31872				{
31873					breakpoint: 1200,
31874					settings: {
31875						slidesToShow: 4,
31876						slidesToScroll: 4
31877					}
31878				}
31879			]
31880		}/*,
31881		CategoryLanding: {
31882			draggable: true,
31883			infinite: true,
31884			focusOnSelect: 	false,
31885			dots: false,
31886			arrows: true,
31887			slidesToShow: 4,
31888			slidesToScroll: 4,
31889			responsive: 	[
31890				{
31891					breakpoint: 767
31892				},
31893				{
31894					breakpoint: 1200,
31895					settings: {
31896						slidesToShow: 3,
31897						slidesToScroll: 3
31898					}
31899				}
31900			]
31901		}*/
31902	}
31903}
31904},{}],126:[function(require,module,exports){
31905/* Global Variables */
31906var helper = require('../helpers/shoppayHelper');
31907var isBuyNow = window.shoppayClientRefs ? window.shoppayClientRefs.constants.isBuyNow : null;
31908var isAATest = window.shoppayClientRefs && window.shoppayClientRefs.preferences.shoppayAATest ? window.shoppayClientRefs.preferences.shoppayAATest : false;
31909var session;
31910
31911$(document).ready(function () {
31912    if (window.ShopPay) {
31913        initShopPayConfig();
31914		if (window.shoppayClientRefs.preferences.shoppayEmailListenerEnabled && $('.shopify-email-listener').length) {
31915			initShopPayEmailRecognition();
31916		}
31917        var readyOnPageLoad = helper.isReadyToOrderOnPageLoad();
31918        if (readyOnPageLoad) {
31919			initShopPayButton();
31920            var pageLoadData = helper.getInitProductData();
31921            helper.setInitProductData(pageLoadData); // updates global prod data.
31922        }
31923
31924        /*  The below code triggers if a product is a Buy Now item, but is not ready to order on page load
31925            (ex: required product attributes like color or size are not yet chosen). Here, a watcher is set
31926            to capture user interactions when product attributes are selected. Helper scripts will be triggered
31927            by these interactions to determine if the item is ready to order when all required attributes are
31928            selected.
31929        */
31930        if (isBuyNow && !readyOnPageLoad) {
31931            $(document).on('ProductDetailPage.Variant.Updated', initBuyNow); // receives the Event & Response
31932        } else {
31933            session = initShopPaySession();
31934        }
31935    }
31936
31937	$(document).on('Cart.UpdateSuccess', function() {
31938		if (window.ShopPay && !isBuyNow) {
31939			if (!session) {
31940				session = initShopPaySession();
31941			} else {
31942				var paymentRequestResponse = helper.buildPaymentRequest(session);
31943				var responseJSON =  paymentRequestResponse ? paymentRequestResponse.responseJSON : null;
31944	
31945				if (responseJSON) {
31946					helper.shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"));
31947				}
31948	
31949				if (responseJSON && !responseJSON.error && responseJSON.paymentRequest !== null) {
31950					session.close();
31951					session = initShopPaySession();
31952				}
31953			}
31954		}
31955	});
31956});
31957
31958function initShopPayConfig() {
31959    window.ShopPay.PaymentRequest.configure({
31960        shopId: window.shoppayClientRefs.preferences.shoppayStoreId,
31961        clientId: window.shoppayClientRefs.preferences.shoppayClientId,
31962        debug: window.shoppayClientRefs.preferences.shoppayModalDebugEnabled
31963    });
31964}
31965
31966function initShopPayButton() {
31967	var paymentSelectors = ['#shop-pay-button-container', '#shop-pay-button-container-mobile', '#shop-pay-button-container-desktop']
31968	for (var paymentSelectorIndex in paymentSelectors) {
31969		var paymentSelector = paymentSelectors[paymentSelectorIndex];
31970		if ($(paymentSelector).length > 0) {
31971			window.ShopPay.PaymentRequest.createButton({buyWith: isBuyNow}).render(paymentSelector);
31972			helper.shoppayMutationObserver(paymentSelector);
31973		}
31974	}
31975}
31976
31977
31978function initBuyNow(event) {
31979	if (!session) {
31980		session = initShopPaySession();
31981	}
31982	helper.setInitProductData({
31983		pid: $('input#pid').val(),
31984		quantity: 1
31985	});
31986	initShopPayButton();
31987}
31988
31989function initShopPayEmailRecognition() {
31990    /*  If your checkout is not built with SFRA or you have customized and removed the 'email-guest'
31991        id on the email input you will need to update the id value for emailInputId
31992
31993        Note: A 1.5 second delay has been added before attaching to the email trigger to the email field to
31994        accommodate users who may have incorporated browser autofills. The pop-up will appear if the field
31995        is autofilled with a recognized email address
31996    */
31997    setTimeout(function() {
31998		$('.shopify-email-listener:visible').each(function() {
31999			window.ShopPay.PaymentRequest.createLogin({emailInputId: $(this).attr('id')}).render('#shop-pay-login-container');
32000		});
32001    }, 1500);
32002}
32003
32004$('body').on('cart:update product:afterAddToCart promotion:success', function () {
32005    /*  Only interested in cart updates on Cart page (cart updates are not triggered in checkout). Buy Now already
32006        has a separate event handler for changes to attribute selections
32007    */
32008    if (window.ShopPay && !isBuyNow) {
32009        if (!session) {
32010            session = initShopPaySession();
32011        } else {
32012            var paymentRequestResponse = helper.buildPaymentRequest(session);
32013            var responseJSON =  paymentRequestResponse ? paymentRequestResponse.responseJSON : null;
32014
32015            if (responseJSON) {
32016                helper.shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"));
32017            }
32018
32019            if (responseJSON && !responseJSON.error && responseJSON.paymentRequest !== null) {
32020                session.close();
32021                session = initShopPaySession();
32022            }
32023        }
32024    }
32025});
32026
32027
32028function initShopPaySession(paymentRequestInput, readyToOrder) {
32029    var paymentRequest;
32030    var paymentRequestResponse;
32031    var responseJSON;
32032
32033    if (isBuyNow && paymentRequestInput) {
32034        paymentRequest = paymentRequestInput;
32035    } else if (isBuyNow && !paymentRequestInput) {
32036        var productData = helper.getInitProductData();
32037        if (productData) {
32038            paymentRequestResponse = helper.createResponse(productData, window.shoppayClientRefs.urls.BuyNowData);
32039            if (paymentRequestResponse.exception || paymentRequestResponse.error){
32040                /*  No need to close any session because a session does not exist at this point (has not yet been
32041                    initiated). Return to exit function - don't reload the page in case there is a page rendering
32042                    issue (will result in infinite reload loop).
32043                */
32044                return;
32045            }
32046            paymentRequest = paymentRequestResponse.paymentRequest;
32047            responseJSON = paymentRequestResponse ? paymentRequestResponse : null;
32048        }
32049    } else {
32050        paymentRequestResponse = helper.buildPaymentRequest(session);
32051        responseJSON = paymentRequestResponse ? paymentRequestResponse.responseJSON : null;
32052    }
32053
32054    if (paymentRequest || (responseJSON && !responseJSON.error)) {
32055        var initialPaymentRequest = responseJSON && responseJSON.paymentRequest ? window.ShopPay.PaymentRequest.build(responseJSON.paymentRequest) : window.ShopPay.PaymentRequest.build(paymentRequest);
32056        // Enable BuyNow Button Click on PDP if Product is Ready To Order
32057        helper.shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"), readyToOrder);
32058
32059        var shoppaySession = window.ShopPay.PaymentRequest.createSession({
32060            paymentRequest: initialPaymentRequest
32061        });
32062
32063        if (shoppaySession) {
32064            helper.setSessionListeners(shoppaySession);
32065            $('body').off('product:afterAttributeSelect', initBuyNow);
32066
32067            $('body').on('product:afterAttributeSelect', function(e, response) {
32068                var responseProduct = response.data.product;
32069                if (isBuyNow && responseProduct.buyNow) {
32070                    helper.setInitProductData({
32071                        pid: responseProduct.id,
32072                        quantity: responseProduct.selectedQuantity,
32073                        options: responseProduct.options
32074                    });
32075                    if (shoppaySession) {
32076                        shoppaySession.close();
32077                        let updatedPaymentRequest = window.ShopPay.PaymentRequest.build(responseProduct.buyNow);
32078                        shoppaySession = window.ShopPay.PaymentRequest.createSession({
32079                            paymentRequest: updatedPaymentRequest
32080                        });
32081
32082                        helper.setSessionListeners(shoppaySession);
32083                    }
32084
32085                } else {
32086                    $.ajax({
32087                        url: helper.getUrlWithCsrfToken(window.shoppayClientRefs.urls.GetCartSummary),
32088                        type: 'GET',
32089                        contentType: 'application/json',
32090                        async: false,
32091                        success: function (data) {
32092                            if (!data.error) {
32093                                if (shoppaySession) {
32094                                    shoppaySession.close();
32095                                }
32096                                let paymentRequest = window.ShopPay.PaymentRequest.build(response.product.buyNow);
32097                                shoppaySession = window.ShopPay.PaymentRequest.createSession({
32098                                    paymentRequest: paymentRequest
32099                                });
32100                                helper.setSessionListeners(shoppaySession);
32101                            }
32102                        },
32103                        error: function (err) {
32104                            if (err.responseJSON || err.status !== 200) {
32105                                session.close();
32106                                // modal hasn't opened yet so "windowclosed" listener won't fire
32107                                window.location.reload();
32108                                return;
32109                            }
32110                        }
32111                    });
32112                }
32113            });
32114        }
32115
32116        return shoppaySession;
32117    }
32118}
32119
32120module.exports = {
32121    initShopPaySession: initShopPaySession
32122};
32123},{"../helpers/shoppayHelper":127}],127:[function(require,module,exports){
32124/* eslint-disable no-unused-vars */
32125/* Global Variables */
32126let checkoutUrl = null;
32127let observer;
32128let orderConfirmationData;
32129let productData = {};
32130let reloadOnClose = true;
32131let sourceIdentifier = null;
32132const technicalErrorMsg = window.shoppayClientRefs ? window.shoppayClientRefs.constants.technicalError : null;
32133let token = null;
32134
32135/**
32136 * Add csrf token param to url
32137 * @param {string} url - source url
32138 * @param {string} basketId - UUID of the temporary basket in the session, if applicable
32139 * @returns {string} - url with csrf_token param
32140 */
32141function getUrlWithCsrfToken(url, basketId) {
32142    const urlInstance = new URL(url, location.origin);
32143
32144    urlInstance.searchParams.append('csrf_token', getCsrfToken());
32145
32146    if (basketId) {
32147        urlInstance.searchParams.append('basketId', basketId);
32148    }
32149
32150    return urlInstance.toString();
32151}
32152
32153/**
32154 * Get CSRF Token
32155 * @returns {string} - csrf token value
32156 */
32157function getCsrfToken() {
32158    let $element = document.querySelector('[data-csrf-token]');
32159
32160    if ($element && $element.attributes['data-csrf-token'] && $element.attributes['data-csrf-token'].value) {
32161        return $element.attributes['data-csrf-token'].value;
32162    }
32163
32164    return '';
32165}
32166
32167/**
32168 * Get Init Product Data (page load and captures data available on page load
32168)
32169 * @returns {Object} - productData with the following structure: { pid: prodId, quantity: selectedQuantity, options: selectedOptions }
32170 */
32171function getInitProductData() {
32172    let productData = null;
32173    let $element = document.querySelector('[data-buy-now-init]');
32174    if ($element && $element.attributes['data-buy-now-init'] && $element.attributes['data-buy-now-init'].value) {
32175        productData = JSON.parse($element.attributes['data-buy-now-init'].value);
32176    }
32177    return productData;
32178}
32179
32180/**
32181 * Set Init Product Data
32182 * @param {Object} data - accepts product data obj & updates global productData variable: { pid: prodId, quantity: selectedQuantity, options: selectedOptions }
32183 */
32184function setInitProductData(data) {
32185    productData = data;
32186}
32187
32188/**
32189 * Sets Up ShopPay listener Events
32190 * @param {Object} session - Accepts the Shop Pay session object and establishes necessary event listeners on the session.
32191 */
32192function setSessionListeners(session) {
32193    session.addEventListener("sessionrequested", function (ev) {
32194        let sessionPaymentRequest;
32195        if (window.shoppayClientRefs.constants.isBuyNow) {
32196            $.ajax({
32197                url: getUrlWithCsrfToken(window.shoppayClientRefs.urls.PrepareBasket),
32198                method: 'POST',
32199                async: false,
32200                data: JSON.stringify(productData),
32201                contentType: 'application/json',
32202                success: function (data) {
32203                    if (!data.error) {
32204                        sessionPaymentRequest = data.paymentRequest;
32205                        sourceIdentifier = data.basketId;
32206                    } else {
32207                        /*  session.completeSessionRequest() does not take an errors array as input so just return
32208                            and use default modal error handling.
32209                        */
32210                        return;
32211                    }
32212                },
32213                error: function (err) {
32214                    /*  session.completeSessionRequest() does not take an errors array as input so just
32215                        destroy the session and return.
32216                    */
32217                    setTimeout(function() {
32218                        session.close();
32219                        // Event listeners have loaded so window.location.reload() will be called in windowclosed
32220                    }, 2000);
32221                    return;
32222                }
32223            });
32224        } else {
32225            sessionPaymentRequest = session.paymentRequest;
32226        }
32227
32228        let requestData = {
32229            paymentRequest: sessionPaymentRequest
32230        };
32231        if (window.shoppayClientRefs.constants.isBuyNow) {
32232            requestData.basketId = sourceIdentifier;
32233        }
32234
32235        $.ajax({
32236            url: getUrlWithCsrfToken(window.shoppayClientRefs.urls.BeginSession),
32237            method: 'POST',
32238            async: false,
32239            data: JSON.stringify(requestData),
32240            contentType: 'application/json',
32241            success: function (data) {
32242                if (!data.error) {
32243                    token = data.token;
32244                    checkoutUrl = data.checkoutUrl;
32245                    sourceIdentifier = data.sourceIdentifier;
32246                    session.completeSessionRequest({token, checkoutUrl, sourceIdentifier});
32247                } else {
32248                    let errorMsg = technicalErrorMsg;
32249                    if (data.errorMsg) {
32250                        errorMsg = data.errorMsg;
32251                    }
32252                    /*  session.completeSessionRequest() does not take an errors array as input so just return
32253                        and use default modal error handling.
32254                    */
32255                    return;
32256                }
32257            },
32258            error: function (err) {
32259                /*  session.completeSessionRequest() does not take an errors array as input so just
32260                    destroy the session and return.
32261                */
32262                setTimeout(function() {
32263                    session.close();
32264                    // Event listeners have loaded so window.location.reload() will be called in windowclosed
32265                }, 2000);
32266                return;
32267            }
32268
32269        });
32270    });
32271
32272    session.addEventListener("discountcodechanged", function(ev) {
32273        const currentPaymentRequest = session.paymentRequest;
32274        let requestData = {
32275            discountCodes: ev.discountCodes
32276        };
32277        if (window.shoppayClientRefs.constants.isBuyNow) {
32278            requestData.basketId = sourceIdentifier;
32279        }
32280
32281        let responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.DiscountCodeChanged);
32282        if (responseJSON.exception){
32283            session.completeDiscountCodeChange({
32284                errors: [
32285                    {
32286                        type: "discountCodeError",
32287                        message: technicalErrorMsg
32288                    }
32289                ]
32290            });
32291            setTimeout(function() {
32292                session.close();
32293                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32294            }, 2000);
32295            return;
32296        } else if (responseJSON.error) {
32297            let errorMsg = technicalErrorMsg;
32298            if (responseJSON.errorMsg) {
32299                errorMsg = responseJSON.errorMsg;
32300            }
32301            session.completeDiscountCodeChange({
32302                errors: [
32303                    {
32304                        type: "discountCodeError",
32305                        message: errorMsg
32306                    }
32307                ]
32308            });
32309            return;
32310        }
32311        const { deliveryMethods, discountCodes, lineItems, shippingLines, subtotal, discounts, totalShippingPrice, totalTax, total } = responseJSON.paymentRequest;
32312
32313		var paymentRequestObject = {};
32314		for (var propertyKey in currentPaymentRequest) {
32315			paymentRequestObject[propertyKey] = currentPaymentRequest[propertyKey];
32316		}
32317
32318        let updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, {
32319            // ...currentPaymentRequest,
32320            discountCodes: discountCodes,
32321            lineItems: lineItems,
32322            shippingLines: shippingLines,
32323            deliveryMethods: deliveryMethods,
32324            subtotal: subtotal,
32325            discounts: discounts,
32326            totalTax: totalTax,
32327            total: total
32328        }));
32329        if (totalShippingPrice) {
32330            updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(updatedPaymentRequest, {
32331                // ...updatedPaymentRequest,
32332                totalShippingPrice: totalShippingPrice
32333            }));
32334        }
32335
32336        session.completeDiscountCodeChange({ updatedPaymentRequest: updatedPaymentRequest });
32337    });
32338
32339    session.addEventListener("deliverymethodchanged", function(ev) {
32340        const currentPaymentRequest = session.paymentRequest;
32341        let requestData = {
32342            deliveryMethod: ev.deliveryMethod,
32343            paymentRequest: currentPaymentRequest,
32344        };
32345
32346        if (window.shoppayClientRefs.constants.isBuyNow) {
32347            requestData.basketId = sourceIdentifier;
32348        }
32349
32350        let responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.DeliveryMethodChanged);
32351        if (responseJSON.exception) {
32352            session.completeDeliveryMethodChange({
32353                errors: [
32354                    {
32355                        type: "shippingAddressError",
32356                        message: technicalErrorMsg
32357                    }
32358                ]
32359            });
32360            setTimeout(function() {
32361                session.close();
32362                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32363            }, 2000);
32364            return;
32365        } else if (responseJSON.error) {
32366            let errorMsg = technicalErrorMsg;
32367            if (responseJSON.errorMsg) {
32368                errorMsg = responseJSON.errorMsg;
32369            }
32370            const { shippingLines, totalShippingPrice, totalTax, total } = responseJSON.paymentRequest;
32371            const updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, {
32372                // ...currentPaymentRequest,
32373                shippingLines: shippingLines,
32374                totalShippingPrice: totalShippingPrice,
32375                totalTax: totalTax,
32376                total: total
32377            }));
32378
32379            session.completeDeliveryMethodChange({
32380                errors: [
32381                    {
32382                        type: "shippingAddressError",
32383                        message: errorMsg
32384                    }
32385                ],
32386                updatedPaymentRequest: updatedPaymentRequest
32387            });
32388            return;
32389        }
32390        const { shippingLines, totalShippingPrice, totalTax, total } = responseJSON.paymentRequest;
32391
32392        // Update the payment request based on the delivery method change and update the total accordingly
32393        const updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, {
32394            // ...currentPaymentRequest,
32395            shippingLines: shippingLines,
32396            totalShippingPrice: totalShippingPrice,
32397            totalTax: totalTax,
32398            total: total
32399        }));
32400
32401        session.completeDeliveryMethodChange({ updatedPaymentRequest: updatedPaymentRequest });
32402    });
32403
32404    session.addEventListener("shippingaddresschanged", function(ev) {
32405        const currentPaymentRequest = session.paymentRequest;
32406        if (!ev.shippingAddress) {
32407            /* This event is triggered with no shipping address when a customer clicks "change"
32408               to choose an alternate email address in the modal
32409            */
32410            session.completeShippingAddressChange({ updatedPaymentRequest: currentPaymentRequest });
32411            return;
32412        }
32413        let requestData = {
32414            shippingAddress: ev.shippingAddress,
32415            paymentRequest: currentPaymentRequest,
32416        };
32417
32418        if (window.shoppayClientRefs.constants.isBuyNow) {
32419            requestData.basketId = sourceIdentifier;
32420        }
32421
32422        let responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.ShippingAddressChanged);
32423        if (responseJSON.exception) {
32424            session.completeShippingAddressChange({
32425                errors: [
32426                    {
32427                        type: "shippingAddressError",
32428                        message: technicalErrorMsg
32429                    }
32430                ]
32431            });
32432            setTimeout(function() {
32433                session.close();
32434                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32435            }, 2000);
32436            return;
32437        } else if (responseJSON.error) {
32438            let errorMsg = technicalErrorMsg;
32439            if (responseJSON.errorMsg) {
32440                errorMsg = responseJSON.errorMsg;
32441            }
32442            session.completeShippingAddressChange({
32443                errors: [
32444                    {
32445                        type: "shippingAddressError",
32446                        message: errorMsg
32447                    }
32448                ]
32449            });
32450            return;
32451        }
32452        const { deliveryMethods, shippingLines, totalShippingPrice, totalTax, total } = responseJSON.paymentRequest;
32453
32454        // Update the payment request based on the shipping address change
32455        const updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, {
32456            // ...currentPaymentRequest,
32457            deliveryMethods: deliveryMethods,
32458            shippingLines: shippingLines,
32459            totalShippingPrice: totalShippingPrice,
32460            totalTax: totalTax,
32461            total: total
32462        }));
32463
32464        session.completeShippingAddressChange({ updatedPaymentRequest: updatedPaymentRequest });
32465    });
32466
32467    session.addEventListener("paymentconfirmationrequested", function(ev) {
32468        const requestData = {
32469            token: session.token,
32470            paymentRequest: session.paymentRequest,
32471        };
32472
32473        if (window.shoppayClientRefs.constants.isBuyNow) {
32474            requestData.basketId = sourceIdentifier;
32475        }
32476
32477        let responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.SubmitPayment);
32478        if (responseJSON.exception) {
32479            session.completePaymentConfirmationRequest({
32480                errors: [
32481                    {
32482                        type: "generalError",
32483                        message: technicalErrorMsg
32484                    }
32485                ]
32486            });
32487            setTimeout(function() {
32488                session.close();
32489                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32490            }, 2000);
32491            return;
32492        } else if (responseJSON.error) {
32493            let errorMsg = technicalErrorMsg;
32494            if (responseJSON.errorMsg) {
32495                errorMsg = responseJSON.errorMsg;
32496            }
32497            session.completePaymentConfirmationRequest({
32498                errors: [
32499                    {
32500                        type: "generalError",
32501                        message: errorMsg
32502                    }
32503                ]
32504            });
32505            return;
32506        }
32507
32508        orderConfirmationData = {
32509            orderID: responseJSON.orderID,
32510            orderToken: responseJSON.orderToken,
32511            continueUrl: responseJSON.continueUrl
32512        };
32513        session.completePaymentConfirmationRequest();
32514    });
32515
32516    session.addEventListener("paymentcomplete", function(ev) {
32517		var requestData = {
32518            token: session.token,
32519            paymentRequest: session.paymentRequest,
32520			event: ev
32521        };
32522        if (window.shoppayClientRefs.constants.isBuyNow) {
32523            requestData.basketId = sourceIdentifier;
32524        }
32525		var responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.PaymentComplete);
32526		if (ev.processingStatus.status === "completed") {
32527			reloadOnClose = false;
32528			session.close();
32529			var data = orderConfirmationData;
32530			var redirect = $('<form>').appendTo(document.body).attr({
32531				method: 'POST',
32532				action: data.continueUrl
32533			});
32534			$('<input>').appendTo(redirect).attr({
32535				name: 'orderID',
32536				value: data.orderID
32537			});
32538			$('<input>').appendTo(redirect).attr({
32539				name: 'orderToken',
32540				value: data.orderToken
32541			});
32542			redirect.submit();
32543		}
32544    });
32545
32546    session.addEventListener("windowclosed", function(ev) {
32547        // Reset global value to default
32548        // if (!reloadOnClose) {
32549        //     reloadOnClose = true;
32550        // } else {
32551        //     window.location.reload();
32552        // }
32553		var requestData = {
32554            token: session.token,
32555            paymentRequest: session.paymentRequest,
32556			event: ev
32557        };
32558        if (window.shoppayClientRefs.constants.isBuyNow) {
32559            requestData.basketId = sourceIdentifier;
32560        }
32561		var responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.WindowClosed);
32562		if (reloadOnClose) {
32563			$(document).trigger('Cart.Update');
32564		}
32565    });
32566
32567	session.addEventListener("deliverymethodtypechanged", function(ev) {
32568		var currentPaymentRequest = session.paymentRequest;
32569        var requestData = {
32570            deliveryMethodType: ev.deliveryMethodType,
32571            paymentRequest: currentPaymentRequest,
32572        };
32573
32574        if (window.shoppayClientRefs.constants.isBuyNow) {
32575            requestData.basketId = sourceIdentifier;
32576        }
32577
32578        var responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.DeliveryMethodTypeChanged);
32579		var updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, responseJSON.paymentRequest));
32580        if (responseJSON.exception) {
32581            session.completeDeliveryMethodTypeChange({
32582                errors: [
32583                    {
32584                        type: "shippingAddressError",
32585                        message: technicalErrorMsg
32586                    }
32587                ]
32588            });
32589            setTimeout(function() {
32590                session.close();
32591                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32592            }, 2000);
32593            return;
32594        } else if (responseJSON.error) {
32595            var errorMsg = technicalErrorMsg;
32596            if (responseJSON.errorMsg) {
32597                errorMsg = responseJSON.errorMsg;
32598            }
32599            session.completeDeliveryMethodTypeChange({
32600                errors: [
32601                    {
32602                        type: "shippingAddressError",
32603                        message: errorMsg
32604                    }
32605                ],
32606                updatedPaymentRequest: updatedPaymentRequest
32607            });
32608            return;
32609        }
32610		session.completeDeliveryMethodTypeChange({ updatedPaymentRequest: updatedPaymentRequest });
32611    });
32612
32613	session.addEventListener("pickuplocationchanged", function(ev) {
32614		var currentPaymentRequest = session.paymentRequest;
32615        var requestData = {
32616            pickupLocation: ev.pickupLocation,
32617            paymentRequest: currentPaymentRequest,
32618        };
32619
32620        if (window.shoppayClientRefs.constants.isBuyNow) {
32621            requestData.basketId = sourceIdentifier;
32622        }
32623
32624        var responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.PickupLocationChanged);
32625		var updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, responseJSON.paymentRequest));
32626        if (responseJSON.exception) {
32627            session.completePickupLocationChange({
32628                errors: [
32629                    {
32630                        type: "shippingAddressError",
32631                        message: technicalErrorMsg
32632                    }
32633                ]
32634            });
32635            setTimeout(function() {
32636                session.close();
32637                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32638            }, 2000);
32639            return;
32640        } else if (responseJSON.error) {
32641            var errorMsg = technicalErrorMsg;
32642            if (responseJSON.errorMsg) {
32643                errorMsg = responseJSON.errorMsg;
32644            }
32645            session.completePickupLocationChange({
32646                errors: [
32647                    {
32648                        type: "shippingAddressError",
32649                        message: errorMsg
32650                    }
32651                ],
32652                updatedPaymentRequest: updatedPaymentRequest
32653            });
32654            return;
32655        }
32656		session.completePickupLocationChange({ updatedPaymentRequest: updatedPaymentRequest });
32657    });
32658
32659	session.addEventListener("pickuplocationfilterchanged", function(ev) {
32660		var currentPaymentRequest = session.paymentRequest;
32661        var requestData = {
32662            buyerLocation: ev.buyerLocation,
32663            paymentRequest: currentPaymentRequest,
32664        };
32665
32666        if (window.shoppayClientRefs.constants.isBuyNow) {
32667            requestData.basketId = sourceIdentifier;
32668        }
32669
32670        var responseJSON = createResponse(requestData, window.shoppayClientRefs.urls.PickupLocationFilterChanged);
32671		var updatedPaymentRequest = window.ShopPay.PaymentRequest.build($.extend(currentPaymentRequest, responseJSON.paymentRequest));
32672        if (responseJSON.exception) {
32673            session.completePickupLocationFilterChange({
32674                errors: [
32675                    {
32676                        type: "shippingAddressError",
32677                        message: technicalErrorMsg
32678                    }
32679                ]
32680            });
32681            setTimeout(function() {
32682                session.close();
32683                // Event listeners have loaded so window.location.reload() will be called in windowclosed
32684            }, 2000);
32685            return;
32686        } else if (responseJSON.error) {
32687            var errorMsg = technicalErrorMsg;
32688            if (responseJSON.errorMsg) {
32689                errorMsg = responseJSON.errorMsg;
32690            }
32691            session.completePickupLocationFilterChange({
32692                errors: [
32693                    {
32694                        type: "shippingAddressError",
32695                        message: errorMsg
32696                    }
32697                ],
32698                updatedPaymentRequest: updatedPaymentRequest
32699            });
32700            return;
32701        }
32702		session.completePickupLocationFilterChange({ updatedPaymentRequest: updatedPaymentRequest });
32703    });
32704}
32705
32706/**
32707 * Enables & Disables Shop Pay's Buy Now button click based on whether the product is ready to order on the PDP
32708 */
32709function shoppayBtnDisabledStyle(elem, isReadyToOrder, forceDisable) {
32710    let readyToOrderPageLoad = isReadyToOrderOnPageLoad();
32711    let isBuyNow = window.shoppayClientRefs.constants.isBuyNow;
32712
32713    if (elem) {
32714        // An error occurred, disable the button
32715        if (!isBuyNow && forceDisable) {
32716            elem.style.pointerEvents = 'none';
32717            if (observer) {
32718                observer.disconnect();
32719            }
32720            return;
32721        }
32722        if (!isBuyNow || (isBuyNow && (isReadyToOrder || readyToOrderPageLoad))) {
32723            elem.style.pointerEvents = 'auto';
32724        } else {
32725            elem.style.pointerEvents = 'none';
32726        }
32727    }
32728}
32729
32730/**
32731 * Watches for when ShopPay button is first rendered to the page to then apply correct button styling depending on whether the basket is empty or not
32732 */
32733function shoppayMutationObserver(elemSelector) {
32734    observer = new MutationObserver((mutationsList, observer) => {
32735        const renderedShopPayElem = document.querySelector(elemSelector);
32736        if (renderedShopPayElem) {
32737            shoppayBtnDisabledStyle(renderedShopPayElem);
32738            observer.disconnect();
32739        }
32740    });
32741
32742    observer.observe(document.body, { childList: true, subtree: true });
32743}
32744
32745/**
32746 * Checks whether a Buy Now product is ready on page load or not (ex: simple products vs. variants with multiple options to choose from, etc.)
32747 */
32748function isReadyToOrderOnPageLoad() {
32749    let readyToOrder = false;
32750    let $element = document.querySelector('[data-ready-to-order]');
32751    if ($element && $element.attributes['data-ready-to-order'] && $element.attributes['data-ready-to-order'].value) {
32752        readyToOrder = $element.attributes['data-ready-to-order'].value === "true";
32753    }
32754    return readyToOrder;
32755}
32756
32757/**
32758 * Handles AJAX call to create / update the payment response needed for the ShopPay.PaymentRequest.build() method.
32759 * @param {Object} requestObj - a request object that contains relevant event data & session data.
32760 * @param {string} controllerURL - String url of the targeted controller (based on the urls Obj set in shoppayGlobalRefs.js)
32761 * @returns {Object} responseJSON - an updated response object to be used in the build & on the ShopPay.PaymentRequest object.
32762 */
32763function createResponse (requestObj, controllerURL) {
32764    let responseJSON;
32765    $.ajax({
32766        url: getUrlWithCsrfToken(controllerURL),
32767        method: 'POST',
32768        async: false,
32769        data: JSON.stringify(requestObj),
32770        contentType: 'application/json',
32771        success: function(data) {
32772            responseJSON = data;
32773            responseJSON.exception = false;
32774        },
32775        error: function (err) {
32776            responseJSON = err.responseJSON ? err.responseJSON : {};
32777            responseJSON.exception = true;
32778        }
32779    });
32780
32781    return responseJSON;
32782}
32783
32784/**
32785 * Handles AJAX call to get the payment response.
32786 * @returns {Object} paymentResponse - an response object.
32787 */
32788function buildPaymentRequest(session) {
32789    let token = document.querySelector('[data-csrf-token]');
32790    let response;
32791    if (token) {
32792        const paymentResponse = $.ajax({
32793            url: getUrlWithCsrfToken(window.shoppayClientRefs.urls.GetCartSummary),
32794            type: 'GET',
32795            contentType: 'application/json',
32796            async: false,
32797            success: function(data) {
32798                if (!data.error) {
32799                    response = {
32800                        responseJSON: data
32801                    };
32802                } else {
32803                    shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"), null, true);
32804                }
32805            },
32806            error: function(err) {
32807                if (session) {
32808                    /*  Return to exit function - don't reload the page in case there is a page rendering
32809                        issue (will result in infinite reload loop).
32810                    */
32811                    session.close();
32812                }
32813                shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"), null, true);
32814            }
32815        });
32816        return response;
32817    } else {
32818        /*  Return to exit function - don't reload the page in case there is a page rendering
32819            issue (will result in infinite reload loop).
32820        */
32821        if (session) {
32822            session.close();
32823        }
32824        shoppayBtnDisabledStyle(document.getElementById("shop-pay-button-container"), null, true);
32825    }
32826}
32827
32828module.exports = {
32829    buildPaymentRequest: buildPaymentRequest,
32830    createResponse: createResponse,
32831    getCsrfToken: getCsrfToken,
32832    getInitProductData: getInitProductData,
32833    getUrlWithCsrfToken: getUrlWithCsrfToken,
32834    isReadyToOrderOnPageLoad: isReadyToOrderOnPageLoad,
32835    productData: productData,
32836    setInitProductData: setInitProductData,
32837    setSessionListeners: setSessionListeners,
32838    shoppayBtnDisabledStyle: shoppayBtnDisabledStyle,
32839    shoppayMutationObserver: shoppayMutationObserver,
32840};
32841
32842},{}],128:[function(require,module,exports){
32843'use strict';
32844
32845exports.ServiceSuffixes = {
32846	ServiceSuite: '/ecomm/0.1',
32847	Authenticate: '/authenticate/root',
32848	PurchaseGiftCard: '/fulfillment/0.1/purchaseegift/'
32849};
32850
32851exports.MessagePrefixes = {
32852	NonSuccess: 'Stored Value Solutions returned a result other than OK and/or without data ',
32853	Error: 'Stored Value Solutions encountered and unexpected exception '
32854};
32855
32856exports.TransactionInformation = {
32857	Success: 'Successful Transaction - No Additional Information',
32858	Failure: {
32859		BalanceExceeded: 'Order Failure - Requested Amount Exceeded Gift Card Balance',
32860		HeldAmountReleased: 'Order Failure - Releasing Amount Held by Pre-Authorization'
32861	}
32862}
32863
32864exports.TransactionTypes = {
32865	BalanceInquiry: 'Balance Inquiry',
32866	PreAuthorization: 'Pre-Authorization',
32867	PreAuthorizationComplete: 'Pre-Authorization Complete'
32868}
32869
32870exports.ResponseParsers = {
32871	balanceInquiryResponse: {
32872		Path: '/int_svs/cartridge/scripts/BalanceInquiryResponseParser',
32873		BodyNode: 'balanceInquiryReturn'
32874	},
32875	preAuthResponse: {
32876		Path: '/int_svs/cartridge/scripts/PreAuthorizationResponseParser',
32877		BodyNode: 'preAuthReturn'
32878	},
32879	preAuthCompleteResponse: {
32880		Path: '/int_svs/cartridge/scripts/PreAuthorizationCompleteResponseParser',
32881		BodyNode: 'preAuthCompleteReturn'
32882	}
32883};
32884
32885exports.ReturnCodes = {
32886	Approval: '01',
32887	InactiveCard: '02',
32888	InvalidCardNumber: '03',
32889	InvalidTransactionCode: '04',
32890	InsufficientFunds: '05',
32891	NoPreviousAuthorizations: '06',
32892	InvalidMessage: '07',
32893	NoCardFound: '08',
32894	InsufficentFundsDueToOutstandingPreAuthorization: '09',
32895	DenialNoPreviousAuthorization: '10',
32896	NoAuthorizationNumber: '11',
32897	InvalidAuthorizationNumber: '12',
32898	MaximumSingleRechargeExceeded: '13',
32899	MaximumWOrkingBalanceExceeded: '14',
32900	HostUnavailable: '15',
32901	InvalidCardStatus: '16',
32902	UnknownDealerStoreCodeSpecialEdit: '17',
32903	MaximumNumberOfRechargesExceeded: '18',
32904	InvalidCardVerificationValueOrSecondarySecurityCode: '19',
32905	InvalidPinNumberOrPinLocked: '20',
32906	CardAlreadyIssued: '21',
32907	CardNotIssued: '22',
32908	CardAlreadyUsed: '23',
32909	ManualTransactionNotAllowed: '24',
32910	MagStripeReadNotValid: '25',
32911	TransactionTypeUnknown: '26',
32912	InvalidTenderType: '27',
32913	InvalidCustomerType: '28',
32914	MaxNumbherOfRedemptionExceeded: '30',
32915	InvalidCurrencyCode: '31',
32916	InvalidServerId: '32',
32917	FrozenCardOrUnknown: '33',
32918	InvalidAmount: '34',
32919	TransactionRejectedInvalidTransactionPromotionWideOrInvalidForOriginatingMerchantAndStore: '36',
32920	TransactionRejectedInvalidMerchantAndStoreCombinationForPromotion: '37',
32921	TransactionRejectedExceededMaximumNumberOfPromotionCards: '38',
32922	TransactionDeclinedAmountLessThanMinimum: '42',
32923	FailureInRetrievingData: '99',
32924	RateLimitExceeded: '999'
32925};
32926
32927exports.ReturnDescriptionMessagesForReturnCodes = {
32928	'03': 'Invalid Card or PIN Number',
32929	'08': 'Invalid Card or PIN Number',
32930	'20': 'Invalid Card or PIN Number'
32931}
32932
32933exports.GiftCardTypes = {
32934	eGift: 'eGift',
32935	Physical: 'Physical',
32936	NotApplicable: 'NotApplicable'
32937};
32938
32939exports.OrderTypes = {
32940	Interactive: 'I',
32941	Web: 'W'
32942};
32943},{}],129:[function(require,module,exports){
32944"use strict";
32945
32946// rawAsap provides everything we need except exception management.
32947var rawAsap = require("./raw");
32948// RawTasks are recycled to reduce GC churn.
32949var freeTasks = [];
32950// We queue errors to ensure they are thrown in right order (FIFO).
32951// Array-as-queue is good enough here, since we are just dealing with exceptions.
32952var pendingErrors = [];
32953var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);
32954
32955function throwFirstError() {
32956    if (pendingErrors.length) {
32957        throw pendingErrors.shift();
32958    }
32959}
32960
32961/**
32962 * Calls a task as soon as possible after returning, in its own event, with priority
32963 * over other events like animation, reflow, and repaint. An error thrown from an
32964 * event will not interrupt, nor even substantially slow down the processing of
32965 * other events, but will be rather postponed to a lower priority event.
32966 * @param {{call}} task A callable object, typically a function that takes no
32967 * arguments.
32968 */
32969module.exports = asap;
32970function asap(task) {
32971    var rawTask;
32972    if (freeTasks.length) {
32973        rawTask = freeTasks.pop();
32974    } else {
32975        rawTask = new RawTask();
32976    }
32977    rawTask.task = task;
32978    rawAsap(rawTask);
32979}
32980
32981// We wrap tasks with recyclable task objects.  A task object implements
32982// `call`, just like a function.
32983function RawTask() {
32984    this.task = null;
32985}
32986
32987// The sole purpose of wrapping the task is to catch the exception and recycle
32988// the task object after its single use.
32989RawTask.prototype.call = function () {
32990    try {
32991        this.task.call();
32992    } catch (error) {
32993        if (asap.onerror) {
32994            // This hook exists purely for testing purposes.
32995            // Its name will be periodically randomized to break any code that
32996            // depends on its existence.
32997            asap.onerror(error);
32998        } else {
32999            // In a web browser, exceptions are not fatal. However, to avoid
33000            // slowing down the queue of pending tasks, we rethrow the error in a
33001            // lower priority turn.
33002            pendingErrors.push(error);
33003            requestErrorThrow();
33004        }
33005    } finally {
33006        this.task = null;
33007        freeTasks[freeTasks.length] = this;
33008    }
33009};
33010
33011},{"./raw":130}],130:[function(require,module,exports){
33012(function (global){(function (){
33013"use strict";
33014
33015// Use the fastest means possible to execute a task in its own turn, with
33016// priority over other events including IO, animation, reflow, and redraw
33017// events in browsers.
33018//
33019// An exception thrown by a task will permanently interrupt the processing of
33020// subsequent tasks. The higher level `asap` function ensures that if an
33021// exception is thrown by a task, that the task queue will continue flushing as
33022// soon as possible, but if you use `rawAsap` directly, you are responsible to
33023// either ensure that no exceptions are thrown from your task, or to manually
33024// call `rawAsap.requestFlush` if an exception is thrown.
33025module.exports = rawAsap;
33026function rawAsap(task) {
33027    if (!queue.length) {
33028        requestFlush();
33029        flushing = true;
33030    }
33031    // Equivalent to push, but avoids a function call.
33032    queue[queue.length] = task;
33033}
33034
33035var queue = [];
33036// Once a flush has been requested, no further calls to `requestFlush` are
33037// necessary until the next `flush` completes.
33038var flushing = false;
33039// `requestFlush` is an implementation-specific method that attempts to kick
33040// off a `flush` event as quickly as possible. `flush` will attempt to exhaust
33041// the event queue before yielding to the browser's own event loop.
33042var requestFlush;
33043// The position of the next task to execute in the task queue. This is
33044// preserved between calls to `flush` so that it can be resumed if
33045// a task throws an exception.
33046var index = 0;
33047// If a task schedules additional tasks recursively, the task queue can grow
33048// unbounded. To prevent memory exhaustion, the task queue will periodically
33049// truncate already-completed tasks.
33050var capacity = 1024;
33051
33052// The flush function processes all tasks that have been scheduled with
33053// `rawAsap` unless and until one of those tasks throws an exception.
33054// If a task throws an exception, `flush` ensures that its state will remain
33055// consistent and will resume where it left off when called again.
33056// However, `flush` does not make any arrangements to be called again if an
33057// exception is thrown.
33058function flush() {
33059    while (index < queue.length) {
33060        var currentIndex = index;
33061        // Advance the index before calling the task. This ensures that we will
33062        // begin flushing on the next task the task throws an error.
33063        index = index + 1;
33064        queue[currentIndex].call();
33065        // Prevent leaking memory for long chains of recursive calls to `asap`.
33066        // If we call `asap` within tasks scheduled by `asap`, the queue will
33067        // grow, but to avoid an O(n) walk for every task we execute, we don't
33068        // shift tasks off the queue after they have been executed.
33069        // Instead, we periodically shift 1024 tasks off the queue.
33070        if (index > capacity) {
33071            // Manually shift all values starting at the index back to the
33072            // beginning of the queue.
33073            for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
33074                queue[scan] = queue[scan + index];
33075            }
33076            queue.length -= index;
33077            index = 0;
33078        }
33079    }
33080    queue.length = 0;
33081    index = 0;
33082    flushing = false;
33083}
33084
33085// `requestFlush` is implemented using a strategy based on data collected from
33086// every available SauceLabs Selenium web driver worker at time of writing.
33087// https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593
33088
33089// Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that
33090// have WebKitMutationObserver but not un-prefixed MutationObserver.
33091// Must use `global` or `self` instead of `window` to work in both frames and web
33092// workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.
33093
33094/* globals self */
33095var scope = typeof global !== "undefined" ? global : self;
33096var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver;
33097
33098// MutationObservers are desirable because they have high priority and work
33099// reliably everywhere they are implemented.
33100// They are implemented in all modern browsers.
33101//
33102// - Android 4-4.3
33103// - Chrome 26-34
33104// - Firefox 14-29
33105// - Internet Explorer 11
33106// - iPad Safari 6-7.1
33107// - iPhone Safari 7-7.1
33108// - Safari 6-7
33109if (typeof BrowserMutationObserver === "function") {
33110    requestFlush = makeRequestCallFromMutationObserver(flush);
33111
33112// MessageChannels are desirable because they give direct access to the HTML
33113// task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera
33114// 11-12, and in web workers in many engines.
33115// Although message channels yield to any queued rendering and IO tasks, they
33116// would be better than imposing the 4ms delay of timers.
33117// However, they do not work reliably in Internet Explorer or Safari.
33118
33119// Internet Explorer 10 is the only browser that has setImmediate but does
33120// not have MutationObservers.
33121// Although setImmediate yields to the browser's renderer, it would be
33122// preferrable to falling back to setTimeout since it does not have
33123// the minimum 4ms penalty.
33124// Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (
33124and
33125// Desktop to a lesser extent) that renders both setImmediate and
33126// MessageChannel useless for the purposes of ASAP.
33127// https://github.com/kriskowal/q/issues/396
33128
33129// Timers are implemented universally.
33130// We fall back to timers in workers in most engines, and in foreground
33131// contexts in the following browsers.
33132// However, note that even this simple case requires nuances to operate in a
33133// broad spectrum of browsers.
33134//
33135// - Firefox 3-13
33136// - Internet Explorer 6-9
33137// - iPad Safari 4.3
33138// - Lynx 2.8.7
33139} else {
33140    requestFlush = makeRequestCallFromTimer(flush);
33141}
33142
33143// `requestFlush` requests that the high priority event queue be flushed as
33144// soon as possible.
33145// This is useful to prevent an error thrown in a task from stalling the event
33146// queue if the exception handled by Node.js’s
33147// `process.on("uncaughtException")` or by a domain.
33148rawAsap.requestFlush = requestFlush;
33149
33150// To request a high priority event, we induce a mutation observer by toggling
33151// the text of a text node between "1" and "-1".
33152function makeRequestCallFromMutationObserver(callback) {
33153    var toggle = 1;
33154    var observer = new BrowserMutationObserver(callback);
33155    var node = document.createTextNode("");
33156    observer.observe(node, {characterData: true});
33157    return function requestCall() {
33158        toggle = -toggle;
33159        node.data = toggle;
33160    };
33161}
33162
33163// The message channel technique was discovered by Malte Ubl and was the
33164// original foundation for this library.
33165// http://www.nonblocking.io/2011/06/windownexttick.html
33166
33167// Safari 6.0.5 (at least) intermittently fails to create message ports on a
33168// page's first load. Thankfully, this version of Safari supports
33169// MutationObservers, so we don't need to fall back in that case.
33170
33171// function makeRequestCallFromMessageChannel(callback) {
33172//     var channel = new MessageChannel();
33173//     channel.port1.onmessage = callback;
33174//     return function requestCall() {
33175//         channel.port2.postMessage(0);
33176//     };
33177// }
33178
33179// For reasons explained above, we are also unable to use `setImmediate`
33180// under any circumstances.
33181// Even if we were, there is another bug in Internet Explorer 10.
33182// It is not sufficient to assign `setImmediate` to `requestFlush` because
33183// `setImmediate` must be called *by name* and therefore must be wrapped in a
33184// closure.
33185// Never forget.
33186
33187// function makeRequestCallFromSetImmediate(callback) {
33188//     return function requestCall() {
33189//         setImmediate(callback);
33190//     };
33191// }
33192
33193// Safari 6.0 has a problem where timers will get lost while the user is
33194// scrolling. This problem does not impact ASAP because Safari 6.0 supports
33195// mutation observers, so that implementation is used instead.
33196// However, if we ever elect to use timers in Safari, the prevalent work-around
33197// is to add a scroll event listener that calls for a flush.
33198
33199// `setTimeout` does not call the passed callback if the delay is less than
33200// approximately 7 in web workers in Firefox 8 through 18, and sometimes not
33201// even then.
33202
33203function makeRequestCallFromTimer(callback) {
33204    return function requestCall() {
33205        // We dispatch a timeout with a specified delay of 0 for engines that
33206        // can reliably accommodate that request. This will usually be snapped
33207        // to a 4 milisecond delay, but once we're flushing, there's no delay
33208        // between events.
33209        var timeoutHandle = setTimeout(handleTimer, 0);
33210        // However, since this timer gets frequently dropped in Firefox
33211        // workers, we enlist an interval handle that will try to fire
33212        // an event 20 times per second until it succeeds.
33213        var intervalHandle = setInterval(handleTimer, 50);
33214
33215        function handleTimer() {
33216            // Whichever timer succeeds will cancel both timers and
33217            // execute the callback.
33218            clearTimeout(timeoutHandle);
33219            clearInterval(intervalHandle);
33220            callback();
33221        }
33222    };
33223}
33224
33225// This is for `asap.js` only.
33226// Its name will be periodically randomized to break any code that depends on
33227// its existence.
33228rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;
33229
33230// ASAP was originally a nextTick shim included in Q. This was factored out
33231// into this ASAP package. It was later adapted to RSVP which made further
33232// amendments. These decisions, particularly to marginalize MessageChannel and
33233// to capture the MutationObserver implementation in a closure, were integrated
33234// back into ASAP proper.
33235// https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js
33236
33237}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
33238
33239},{}],131:[function(require,module,exports){
33240/*!
33241 * eventie v1.0.6
33242 * event binding helper
33243 *   eventie.bind( elem, 'click', myFn )
33244 *   eventie.unbind( elem, 'click', myFn )
33245 * MIT license
33246 */
33247
33248/*jshint browser: true, undef: true, unused: true */
33249/*global define: false, module: false */
33250
33251( function( window ) {
33252
33253'use strict';
33254
33255var docElem = document.documentElement;
33256
33257var bind = function() {};
33258
33259function getIEEvent( obj ) {
33260  var event = window.event;
33261  // add event.target
33262  event.target = event.target || event.srcElement || obj;
33263  return event;
33264}
33265
33266if ( docElem.addEventListener ) {
33267  bind = function( obj, type, fn ) {
33268    obj.addEventListener( type, fn, false );
33269  };
33270} else if ( docElem.attachEvent ) {
33271  bind = function( obj, type, fn ) {
33272    obj[ type + fn ] = fn.handleEvent ?
33273      function() {
33274        var event = getIEEvent( obj );
33275        fn.handleEvent.call( fn, event );
33276      } :
33277      function() {
33278        var event = getIEEvent( obj );
33279        fn.call( obj, event );
33280      };
33281    obj.attachEvent( "on" + type, obj[ type + fn ] );
33282  };
33283}
33284
33285var unbind = function() {};
33286
33287if ( docElem.removeEventListener ) {
33288  unbind = function( obj, type, fn ) {
33289    obj.removeEventListener( type, fn, false );
33290  };
33291} else if ( docElem.detachEvent ) {
33292  unbind = function( obj, type, fn ) {
33293    obj.detachEvent( "on" + type, obj[ type + fn ] );
33294    try {
33295      delete obj[ type + fn ];
33296    } catch ( err ) {
33297      // can't delete window object properties
33298      obj[ type + fn ] = undefined;
33299    }
33300  };
33301}
33302
33303var eventie = {
33304  bind: bind,
33305  unbind: unbind
33306};
33307
33308// ----- module definition ----- //
33309
33310if ( typeof define === 'function' && define.amd ) {
33311  // AMD
33312  define( eventie );
33313} else if ( typeof exports === 'object' ) {
33314  // CommonJS
33315  module.exports = eventie;
33316} else {
33317  // browser global
33318  window.eventie = eventie;
33319}
33320
33321})( window );
33322
33323},{}
33323],132:[function(require,module,exports){
33324/*!
33325 * imagesLoaded v3.2.0
33326 * JavaScript is all like "You images are done yet or what?"
33327 * MIT License
33328 */
33329
33330( function( window, factory ) { 'use strict';
33331  // universal module definition
33332
33333  /*global define: false, module: false, require: false */
33334
33335  if ( typeof define == 'function' && define.amd ) {
33336    // AMD
33337    define( [
33338      'eventEmitter/EventEmitter',
33339      'eventie/eventie'
33340    ], function( EventEmitter, eventie ) {
33341      return factory( window, EventEmitter, eventie );
33342    });
33343  } else if ( typeof module == 'object' && module.exports ) {
33344    // CommonJS
33345    module.exports = factory(
33346      window,
33347      require('wolfy87-eventemitter'),
33348      require('eventie')
33349    );
33350  } else {
33351    // browser global
33352    window.imagesLoaded = factory(
33353      window,
33354      window.EventEmitter,
33355      window.eventie
33356    );
33357  }
33358
33359})( window,
33360
33361// --------------------------  factory -------------------------- //
33362
33363function factory( window, EventEmitter, eventie ) {
33364
33365'use strict';
33366
33367var $ = window.jQuery;
33368var console = window.console;
33369
33370// -------------------------- helpers -------------------------- //
33371
33372// extend objects
33373function extend( a, b ) {
33374  for ( var prop in b ) {
33375    a[ prop ] = b[ prop ];
33376  }
33377  return a;
33378}
33379
33380var objToString = Object.prototype.toString;
33381function isArray( obj ) {
33382  return objToString.call( obj ) == '[object Array]';
33383}
33384
33385// turn element or nodeList into an array
33386function makeArray( obj ) {
33387  var ary = [];
33388  if ( isArray( obj ) ) {
33389    // use object if already an array
33390    ary = obj;
33391  } else if ( typeof obj.length == 'number' ) {
33392    // convert nodeList to array
33393    for ( var i=0; i < obj.length; i++ ) {
33394      ary.push( obj[i] );
33395    }
33396  } else {
33397    // array of single index
33398    ary.push( obj );
33399  }
33400  return ary;
33401}
33402
33403  // -------------------------- imagesLoaded -------------------------- //
33404
33405  /**
33406   * @param {Array, Element, NodeList, String} elem
33407   * @param {Object or Function} options - if function, use as callback
33408   * @param {Function} onAlways - callback function
33409   */
33410  function ImagesLoaded( elem, options, onAlways ) {
33411    // coerce ImagesLoaded() without new, to be new ImagesLoaded()
33412    if ( !( this instanceof ImagesLoaded ) ) {
33413      return new ImagesLoaded( elem, options, onAlways );
33414    }
33415    // use elem as selector string
33416    if ( typeof elem == 'string' ) {
33417      elem = document.querySelectorAll( elem );
33418    }
33419
33420    this.elements = makeArray( elem );
33421    this.options = extend( {}, this.options );
33422
33423    if ( typeof options == 'function' ) {
33424      onAlways = options;
33425    } else {
33426      extend( this.options, options );
33427    }
33428
33429    if ( onAlways ) {
33430      this.on( 'always', onAlways );
33431    }
33432
33433    this.getImages();
33434
33435    if ( $ ) {
33436      // add jQuery Deferred object
33437      this.jqDeferred = new $.Deferred();
33438    }
33439
33440    // HACK check async to allow time to bind listeners
33441    var _this = this;
33442    setTimeout( function() {
33443      _this.check();
33444    });
33445  }
33446
33447  ImagesLoaded.prototype = new EventEmitter();
33448
33449  ImagesLoaded.prototype.options = {};
33450
33451  ImagesLoaded.prototype.getImages = function() {
33452    this.images = [];
33453
33454    // filter & find items if we have an item selector
33455    for ( var i=0; i < this.elements.length; i++ ) {
33456      var elem = this.elements[i];
33457      this.addElementImages( elem );
33458    }
33459  };
33460
33461  /**
33462   * @param {Node} element
33463   */
33464  ImagesLoaded.prototype.addElementImages = function( elem ) {
33465    // filter siblings
33466    if ( elem.nodeName == 'IMG' ) {
33467      this.addImage( elem );
33468    }
33469    // get background image on element
33470    if ( this.options.background === true ) {
33471      this.addElementBackgroundImages( elem );
33472    }
33473
33474    // find children
33475    // no non-element nodes, #143
33476    var nodeType = elem.nodeType;
33477    if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
33478      return;
33479    }
33480    var childImgs = elem.querySelectorAll('img');
33481    // concat childElems to filterFound array
33482    for ( var i=0; i < childImgs.length; i++ ) {
33483      var img = childImgs[i];
33484      this.addImage( img );
33485    }
33486
33487    // get child background images
33488    if ( typeof this.options.background == 'string' ) {
33489      var children = elem.querySelectorAll( this.options.background );
33490      for ( i=0; i < children.length; i++ ) {
33491        var child = children[i];
33492        this.addElementBackgroundImages( child );
33493      }
33494    }
33495  };
33496
33497  var elementNodeTypes = {
33498    1: true,
33499    9: true,
33500    11: true
33501  };
33502
33503  ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
33504    var style = getStyle( elem );
33505    // get url inside url("...")
33506    var reURL = /url\(['"]*([^'"\)]+)['"]*\)/gi;
33507    var matches = reURL.exec( style.backgroundImage );
33508    while ( matches !== null ) {
33509      var url = matches && matches[1];
33510      if ( url ) {
33511        this.addBackground( url, elem );
33512      }
33513      matches = reURL.exec( style.backgroundImage );
33514    }
33515  };
33516
33517  // IE8
33518  var getStyle = window.getComputedStyle || function( elem ) {
33519    return elem.currentStyle;
33520  };
33521
33522  /**
33523   * @param {Image} img
33524   */
33525  ImagesLoaded.prototype.addImage = function( img ) {
33526    var loadingImage = new LoadingImage( img );
33527    this.images.push( loadingImage );
33528  };
33529
33530  ImagesLoaded.prototype.addBackground = function( url, elem ) {
33531    var background = new Background( url, elem );
33532    this.images.push( background );
33533  };
33534
33535  ImagesLoaded.prototype.check = function() {
33536    var _this = this;
33537    this.progressedCount = 0;
33538    this.hasAnyBroken = false;
33539    // complete if no images
33540    if ( !this.images.length ) {
33541      this.complete();
33542      return;
33543    }
33544
33545    function onProgress( image, elem, message ) {
33546      // HACK - Chrome triggers event before object properties have changed. #83
33547      setTimeout( function() {
33548        _this.progress( image, elem, message );
33549      });
33550    }
33551
33552    for ( var i=0; i < this.images.length; i++ ) {
33553      var loadingImage = this.images[i];
33554      loadingImage.once( 'progress', onProgress );
33555      loadingImage.check();
33556    }
33557  };
33558
33559  ImagesLoaded.prototype.progress = function( image, elem, message ) {
33560    this.progressedCount++;
33561    this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
33562    // progress event
33563    this.emit( 'progress', this, image, elem );
33564    if ( this.jqDeferred && this.jqDeferred.notify ) {
33565      this.jqDeferred.notify( this, image );
33566    }
33567    // check if completed
33568    if ( this.progressedCount == this.images.length ) {
33569      this.complete();
33570    }
33571
33572    if ( this.options.debug && console ) {
33573      console.log( 'progress: ' + message, image, elem );
33574    }
33575  };
33576
33577  ImagesLoaded.prototype.complete = function() {
33578    var eventName = this.hasAnyBroken ? 'fail' : 'done';
33579    this.isComplete = true;
33580    this.emit( eventName, this );
33581    this.emit( 'always', this );
33582    if ( this.jqDeferred ) {
33583      var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
33584      this.jqDeferred[ jqMethod ]( this );
33585    }
33586  };
33587
33588  // --------------------------  -------------------------- //
33589
33590  function LoadingImage( img ) {
33591    this.img = img;
33592  }
33593
33594  LoadingImage.prototype = new EventEmitter();
33595
33596  LoadingImage.prototype.check = function() {
33597    // If complete is true and browser supports natural sizes,
33598    // try to check for image status manually.
33599    var isComplete = this.getIsImageComplete();
33600    if ( isComplete ) {
33601      // report based on naturalWidth
33602      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
33603      return;
33604    }
33605
33606    // If none of the checks above matched, simulate loading on detached element.
33607    this.proxyImage = new Image();
33608    eventie.bind( this.proxyImage, 'load', this );
33609    eventie.bind( this.proxyImage, 'error', this );
33610    // bind to image as well for Firefox. #191
33611    eventie.bind( this.img, 'load', this );
33612    eventie.bind( this.img, 'error', this );
33613    this.proxyImage.src = this.img.src;
33614  };
33615
33616  LoadingImage.prototype.getIsImageComplete = function() {
33617    return this.img.complete && this.img.naturalWidth !== undefined;
33618  };
33619
33620  LoadingImage.prototype.confirm = function( isLoaded, message ) {
33621    this.isLoaded = isLoaded;
33622    this.emit( 'progress', this, this.img, message );
33623  };
33624
33625  // ----- events ----- //
33626
33627  // trigger specified handler for event type
33628  LoadingImage.prototype.handleEvent = function( event ) {
33629    var method = 'on' + event.type;
33630    if ( this[ method ] ) {
33631      this[ method ]( event );
33632    }
33633  };
33634
33635  LoadingImage.prototype.onload = function() {
33636    this.confirm( true, 'onload' );
33637    this.unbindEvents();
33638  };
33639
33640  LoadingImage.prototype.onerror = function() {
33641    this.confirm( false, 'onerror' );
33642    this.unbindEvents();
33643  };
33644
33645  LoadingImage.prototype.unbindEvents = function() {
33646    eventie.unbind( this.proxyImage, 'load', this );
33647    eventie.unbind( this.proxyImage, 'error', this );
33648    eventie.unbind( this.img, 'load', this );
33649    eventie.unbind( this.img, 'error', this );
33650  };
33651
33652  // -------------------------- Background -------------------------- //
33653
33654  function Background( url, element ) {
33655    this.url = url;
33656    this.element = element;
33657    this.img = new Image();
33658  }
33659
33660  // inherit LoadingImage prototype
33661  Background.prototype = new LoadingImage();
33662
33663  Background.prototype.check = function() {
33664    eventie.bind( this.img, 'load', this );
33665    eventie.bind( this.img, 'error', this );
33666    this.img.src = this.url;
33667    // check if image is already complete
33668    var isComplete = this.getIsImageComplete();
33669    if ( isComplete ) {
33670      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
33671      this.unbindEvents();
33672    }
33673  };
33674
33675  Background.prototype.unbindEvents = function() {
33676    eventie.unbind( this.img, 'load', this );
33677    eventie.unbind( this.img, 'error', this );
33678  };
33679
33680  Background.prototype.confirm = function( isLoaded, message ) {
33681    this.isLoaded = isLoaded;
33682    this.emit( 'progress', this, this.element, message );
33683  };
33684
33685  // -------------------------- jQuery -------------------------- //
33686
33687  ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
33688    jQuery = jQuery || window.jQuery;
33689    if ( !jQuery ) {
33690      return;
33691    }
33692    // set local variable
33693    $ = jQuery;
33694    // $().imagesLoaded()
33695    $.fn.imagesLoaded = function( options, callback ) {
33696      var instance = new ImagesLoaded( this, options, callback );
33697      return instance.jqDeferred.promise( $(this) );
33698    };
33699  };
33700  // try making plugin
33701  ImagesLoaded.makeJQueryPlugin();
33702
33703  // --------------------------  -------------------------- //
33704
33705  return ImagesLoaded;
33706
33707});
33708
33709},{"eventie":131,"wolfy87-eventemitter":142}],133:[function(require,module,exports){
33710(function (global){(function (){
33711/**
33712 * @license
33713 * lodash 3.10.1 (Custom Build) <https://lodash.com/>
33714 * Build: `lodash modern -d -o ./index.js`
33715 * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
33716 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
33717 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
33718 * Available under MIT license <https://lodash.com/license>
33719 */
33720;(function() {
33721
33722  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
33723  var undefined;
33724
33725  /** Used as the semantic version number. */
33726  var VERSION = '3.10.1';
33727
33728  /** Used to compose bitmasks for wrapper metadata. */
33729  var BIND_FLAG = 1,
33730      BIND_KEY_FLAG = 2,
33731      CURRY_BOUND_FLAG = 4,
33732      CURRY_FLAG = 8,
33733      CURRY_RIGHT_FLAG = 16,
33734      PARTIAL_FLAG = 32,
33735      PARTIAL_RIGHT_FLAG = 64,
33736      ARY_FLAG = 128,
33737      REARG_FLAG = 256;
33738
33739  /** Used as default options for `_.trunc`. */
33740  var DEFAULT_TRUNC_LENGTH = 30,
33741      DEFAULT_TRUNC_OMISSION = '...';
33742
33743  /** Used to detect when a function becomes hot. */
33744  var HOT_COUNT = 150,
33745      HOT_SPAN = 16;
33746
33747  /** Used as the size to enable large array optimizations. */
33748  var LARGE_ARRAY_SIZE = 200;
33749
33750  /** Used to indicate the type of lazy iteratees. */
33751  var LAZY_FILTER_FLAG = 1,
33752      LAZY_MAP_FLAG = 2;
33753
33754  /** Used as the `TypeError` message for "Functions" methods. */
33755  var FUNC_ERROR_TEXT = 'Expected a function';
33756
33757  /** Used as the internal argument placeholder. */
33758  var PLACEHOLDER = '__lodash_placeholder__';
33759
33760  /** `Object#toString` result references. */
33761  var argsTag = '[object Arguments]',
33762      arrayTag = '[object Array]',
33763      boolTag = '[object Boolean]',
33764      dateTag = '[object Date]',
33765      errorTag = '[object Error]',
33766      funcTag = '[object Function]',
33767      mapTag = '[object Map]',
33768      numberTag = '[object Number]',
33769      objectTag = '[object Object]',
33770      regexpTag = '[object RegExp]',
33771      setTag = '[object Set]',
33772      stringTag = '[object String]',
33773      weakMapTag = '[object WeakMap]';
33774
33775  var arrayBufferTag = '[object ArrayBuffer]',
33776      float32Tag = '[object Float32Array]',
33777      float64Tag = '[object Float64Array]',
33778      int8Tag = '[object Int8Array]',
33779      int16Tag = '[object Int16Array]',
33780      int32Tag = '[object Int32Array]',
33781      uint8Tag = '[object Uint8Array]',
33782      uint8ClampedTag = '[object Uint8ClampedArray]',
33783      uint16Tag = '[object Uint16Array]',
33784      uint32Tag = '[object Uint32Array]';
33785
33786  /** Used to match empty string literals in compiled template source. */
33787  var reEmptyStringLeading = /\b__p \+= '';/g,
33788      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
33789      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
33790
33791  /** Used to match HTML entities and HTML characters. */
33792  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g,
33793      reUnescapedHtml = /[&<>"'`]/g,
33794      reHasEscapedHtml = RegExp(reEscapedHtml.source),
33795      reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
33796
33797  /** Used to match template delimiters. */
33798  var reEscape = /<%-([\s\S]+?)%>/g,
33799      reEvaluate = /<%([\s\S]+?)%>/g,
33800      reInterpolate = /<%=([\s\S]+?)%>/g;
33801
33802  /** Used to match property names within property paths. */
33803  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
33804      reIsPlainProp = /^\w*$/,
33805      rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
33806
33807  /**
33808   * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns)
33809   * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern).
33810   */
33811  var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,
33812      reHasRegExpChars = RegExp(reRegExpChars.source);
33813
33814  /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */
33815  var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g;
33816
33817  /** Used to match backslashes in property paths. */
33818  var reEscapeChar = /\\(\\)?/g;
33819
33820  /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */
33821  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
33822
33823  /** Used to match `RegExp` flags from their coerced string values. */
33824  var reFlags = /\w*$/;
33825
33826  /** Used to detect hexadecimal string values. */
33827  var reHasHexPrefix = /^0[xX]/;
33828
33829  /** Used to detect host constructors (Safari > 5). */
33830  var reIsHostCtor = /^\[object .+?Constructor\]$/;
33831
33832  /** Used to detect unsigned integer values. */
33833  var reIsUint = /^\d+$/;
33834
33835  /** Used to match latin-1 supplementary letters (excluding mathematical operators). */
33836  var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
33837
33838  /** Used to ensure capturing order of template delimiters. */
33839  var reNoMatch = /($^)/;
33840
33841  /** Used to match unescaped characters in compiled string literals. */
33842  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
33843
33844  /** Used to match words to create compound words. */
33845  var reWords = (function() {
33846    var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]',
33847        lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+';
33848
33849    return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g');
33850  }());
33851
33852  /** Used to assign default `context` object properties. */
33853  var contextProps = [
33854    'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array',
33855    'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number',
33856    'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite',
33857    'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array',
33858    'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap'
33859  ];
33860
33861  /** Used to make template sourceURLs easier to identify. */
33862  var templateCounter = -1;
33863
33864  /** Used to identify `toStringTag` values of typed arrays. */
33865  var typedArrayTags = {};
33866  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
33867  typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
33868  typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
33869  typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
33870  typedArrayTags[uint32Tag] = true;
33871  typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
33872  typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
33873  typedArrayTags[dateTag] = typedArrayTags[errorTag] =
33874  typedArrayTags[funcTag] = typedArrayTags[mapTag] =
33875  typedArrayTags[numberTag] = typedArrayTags[objectTag] =
33876  typedArrayTags[regexpTag] = typedArrayTags[setTag] =
33877  typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
33878
33879  /** Used to identify `toStringTag` values supported by `_.clone`. */
33880  var cloneableTags = {};
33881  cloneableTags[argsTag] = cloneableTags[arrayTag] =
33882  cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
33883  cloneableTags[dateTag] = cloneableTags[float32Tag] =
33884  cloneableTags[float64Tag] = cloneableTags[int8Tag] =
33885  cloneableTags[int16Tag] = cloneableTags[int32Tag] =
33886  cloneableTags[numberTag] = cloneableTags[objectTag] =
33887  cloneableTags[regexpTag] = cloneableTags[stringTag] =
33888  cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
33889  cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
33890  cloneableTags[errorTag] = cloneableTags[funcTag] =
33891  cloneableTags[mapTag] = cloneableTags[setTag] =
33892  cloneableTags[weakMapTag] = false;
33893
33894  /** Used to map latin-1 supplementary letters to basic latin letters. */
33895  var deburredLetters = {
33896    '\xc0': 'A',  '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
33897    '\xe0': 'a',  '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
33898    '\xc7': 'C',  '\xe7': 'c',
33899    '\xd0': 'D',  '\xf0': 'd',
33900    '\xc8': 'E',  '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
33901    '\xe8': 'e',  '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
33902    '\xcC': 'I',  '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
33903    '\xeC': 'i',  '\xed': 'i', '\xee': 'i', '\xef': 'i',
33904    '\xd1': 'N',  '\xf1': 'n',
33905    '\xd2': 'O',  '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
33906    '\xf2': 'o',  '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
33907    '\xd9': 'U',  '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
33908    '\xf9': 'u',  '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
33909    '\xdd': 'Y',  '\xfd': 'y', '\xff': 'y',
33910    '\xc6': 'Ae', '\xe6': 'ae',
33911    '\xde': 'Th', '\xfe': 'th',
33912    '\xdf': 'ss'
33913  };
33914
33915  /** Used to map characters to HTML entities. */
33916  var htmlEscapes = {
33917    '&': '&amp;',
33918    '<': '&lt;',
33919    '>': '&gt;',
33920    '"': '&quot;',
33921    "'": '&#39;',
33922    '`': '&#96;'
33923  };
33924
33925  /** Used to map HTML entities to characters. */
33926  var htmlUnescapes = {
33927    '&amp;': '&',
33928    '&lt;': '<',
33929    '&gt;': '>',
33930    '&quot;': '"',
33931    '&#39;': "'",
33932    '&#96;': '`'
33933  };
33934
33935  /** Used to determine if values are of the language type `Object`. */
33936  var objectTypes = {
33937    'function': true,
33938    'object': true
33939  };
33940
33941  /** Used to escape characters for inclusion in compiled regexes. */
33942  var regexpEscapes = {
33943    '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34',
33944    '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39',
33945    'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46',
33946    'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66',
33947    'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78'
33948  };
33949
33950  /** Used to escape characters for inclusion in compiled string literals. */
33951  var stringEscapes = {
33952    '\\': '\\',
33953    "'": "'",
33954    '\n': 'n',
33955    '\r': 'r',
33956    '\u2028': 'u2028',
33957    '\u2029': 'u2029'
33958  };
33959
33960  /** Detect free variable `exports`. */
33961  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
33962
33963  /** Detect free variable `module`. */
33964  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
33965
33966  /** Detect free variable `global` from Node.js. */
33967  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
33968
33969  /** Detect free variable `self`. */
33970  var freeSelf = objectTypes[typeof self] && self && self.Object && self;
33971
33972  /** Detect free variable `window`. */
33973  var freeWindow = objectTypes[typeof window] && window && window.Object && window;
33974
33975  /** Detect the popular CommonJS extension `module.exports`. */
33976  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
33977
33978  /**
33979   * Used as a reference to the global object.
33980   *
33981   * The `this` value is used if it's the global object to avoid Greasemonkey's
33982   * restricted `window` object, otherwise the `window` object is used.
33983   */
33984  var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
33985
33986  /*--------------------------------------------------------------------------*/
33987
33988  /**
33989   * The base implementation of `compareAscending` which compares values and
33990   * sorts them in ascending order without guaranteeing a stable sort.
33991   *
33992   * @private
33993   * @param {*} value The value to compare.
33994   * @param {*} other The other value to compare.
33995   * @returns {number} Returns the sort order indicator for `value`.
33996   */
33997  function baseCompareAscending(value, other) {
33998    if (value !== other) {
33999      var valIsNull = value === null,
34000          valIsUndef = value === undefined,
34001          valIsReflexive = value === value;
34002
34003      var othIsNull = other === null,
34004          othIsUndef = other === undefined,
34005          othIsReflexive = other === other;
34006
34007      if ((value >
34007 other && !othIsNull) || !valIsReflexive ||
34008          (valIsNull && !othIsUndef && othIsReflexive) ||
34009          (valIsUndef && othIsReflexive)) {
34010        return 1;
34011      }
34012      if ((value < other && !valIsNull) || !othIsReflexive ||
34013          (othIsNull && !valIsUndef && valIsReflexive) ||
34014          (othIsUndef && valIsReflexive)) {
34015        return -1;
34016      }
34017    }
34018    return 0;
34019  }
34020
34021  /**
34022   * The base implementation of `_.findIndex` and `_.findLastIndex` without
34023   * support for callback shorthands and `this` binding.
34024   *
34025   * @private
34026   * @param {Array} array The array to search.
34027   * @param {Function} predicate The function invoked per iteration.
34028   * @param {boolean} [fromRight] Specify iterating from right to left.
34029   * @returns {number} Returns the index of the matched value, else `-1`.
34030   */
34031  function baseFindIndex(array, predicate, fromRight) {
34032    var length = array.length,
34033        index = fromRight ? length : -1;
34034
34035    while ((fromRight ? index-- : ++index < length)) {
34036      if (predicate(array[index], index, array)) {
34037        return index;
34038      }
34039    }
34040    return -1;
34041  }
34042
34043  /**
34044   * The base implementation of `_.indexOf` without support for binary searches.
34045   *
34046   * @private
34047   * @param {Array} array The array to search.
34048   * @param {*} value The value to search for.
34049   * @param {number} fromIndex The index to search from.
34050   * @returns {number} Returns the index of the matched value, else `-1`.
34051   */
34052  function baseIndexOf(array, value, fromIndex) {
34053    if (value !== value) {
34054      return indexOfNaN(array, fromIndex);
34055    }
34056    var index = fromIndex - 1,
34057        length = array.length;
34058
34059    while (++index < length) {
34060      if (array[index] === value) {
34061        return index;
34062      }
34063    }
34064    return -1;
34065  }
34066
34067  /**
34068   * The base implementation of `_.isFunction` without support for environments
34069   * with incorrect `typeof` results.
34070   *
34071   * @private
34072   * @param {*} value The value to check.
34073   * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
34074   */
34075  function baseIsFunction(value) {
34076    // Avoid a Chakra JIT bug in compatibility modes of IE 11.
34077    // See https://github.com/jashkenas/underscore/issues/1621 for more details.
34078    return typeof value == 'function' || false;
34079  }
34080
34081  /**
34082   * Converts `value` to a string if it's not one. An empty string is returned
34083   * for `null` or `undefined` values.
34084   *
34085   * @private
34086   * @param {*} value The value to process.
34087   * @returns {string} Returns the string.
34088   */
34089  function baseToString(value) {
34090    return value == null ? '' : (value + '');
34091  }
34092
34093  /**
34094   * Used by `_.trim` and `_.trimLeft` to get the index of the first character
34095   * of `string` that is not found in `chars`.
34096   *
34097   * @private
34098   * @param {string} string The string to inspect.
34099   * @param {string} chars The characters to find.
34100   * @returns {number} Returns the index of the first character not found in `chars`.
34101   */
34102  function charsLeftIndex(string, chars) {
34103    var index = -1,
34104        length = string.length;
34105
34106    while (++index < length && chars.indexOf(string.charAt(index)) > -1) {}
34107    return index;
34108  }
34109
34110  /**
34111   * Used by `_.trim` and `_.trimRight` to get the index of the last character
34112   * of `string` that is not found in `chars`.
34113   *
34114   * @private
34115   * @param {string} string The string to inspect.
34116   * @param {string} chars The characters to find.
34117   * @returns {number} Returns the index of the last character not found in `chars`.
34118   */
34119  function charsRightIndex(string, chars) {
34120    var index = string.length;
34121
34122    while (index-- && chars.indexOf(string.charAt(index)) > -1) {}
34123    return index;
34124  }
34125
34126  /**
34127   * Used by `_.sortBy` to compare transformed elements of a collection and stable
34128   * sort them in ascending order.
34129   *
34130   * @private
34131   * @param {Object} object The object to compare.
34132   * @param {Object} other The other object to compare.
34133   * @returns {number} Returns the sort order indicator for `object`.
34134   */
34135  function compareAscending(object, other) {
34136    return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index);
34137  }
34138
34139  /**
34140   * Used by `_.sortByOrder` to compare multiple properties of a value to another
34141   * and stable sort them.
34142   *
34143   * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise,
34144   * a value is sorted in ascending order if its corresponding order is "asc", and
34145   * descending if "desc".
34146   *
34147   * @private
34148   * @param {Object} object The object to compare.
34149   * @param {Object} other The other object to compare.
34150   * @param {boolean[]} orders The order to sort by for each property.
34151   * @returns {number} Returns the sort order indicator for `object`.
34152   */
34153  function compareMultiple(object, other, orders) {
34154    var index = -1,
34155        objCriteria = object.criteria,
34156        othCriteria = other.criteria,
34157        length = objCriteria.length,
34158        ordersLength = orders.length;
34159
34160    while (++index < length) {
34161      var result = baseCompareAscending(objCriteria[index], othCriteria[index]);
34162      if (result) {
34163        if (index >= ordersLength) {
34164          return result;
34165        }
34166        var order = orders[index];
34167        return result * ((order === 'asc' || order === true) ? 1 : -1);
34168      }
34169    }
34170    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
34171    // that causes it, under certain circumstances, to provide the same value for
34172    // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
34173    // for more details.
34174    //
34175    // This also ensures a stable sort in V8 and other engines.
34176    // See https://code.google.com/p/v8/issues/detail?id=90 for more details.
34177    return object.index - other.index;
34178  }
34179
34180  /**
34181   * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.
34182   *
34183   * @private
34184   * @param {string} letter The matched letter to deburr.
34185   * @returns {string} Returns the deburred letter.
34186   */
34187  function deburrLetter(letter) {
34188    return deburredLetters[letter];
34189  }
34190
34191  /**
34192   * Used by `_.escape` to convert characters to HTML entities.
34193   *
34194   * @private
34195   * @param {string} chr The matched character to escape.
34196   * @returns {string} Returns the escaped character.
34197   */
34198  function escapeHtmlChar(chr) {
34199    return htmlEscapes[chr];
34200  }
34201
34202  /**
34203   * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes.
34204   *
34205   * @private
34206   * @param {string} chr The matched character to escape.
34207   * @param {string} leadingChar The capture group for a leading character.
34208   * @param {string} whitespaceChar The capture group for a whitespace character.
34209   * @returns {string} Returns the escaped character.
34210   */
34211  function escapeRegExpChar(chr, leadingChar, whitespaceChar) {
34212    if (leadingChar) {
34213      chr = regexpEscapes[chr];
34214    } else if (whitespaceChar) {
34215      chr = stringEscapes[chr];
34216    }
34217    return '\\' + chr;
34218  }
34219
34220  /**
34221   * Used by `_.template` to escape characters for inclusion in compiled string literals.
34222   *
34223   * @private
34224   * @param {string} chr The matched character to escape.
34225   * @returns {string} Returns the escaped character.
34226   */
34227  function escapeStringChar(chr) {
34228    return '\\' + stringEscapes[chr];
34229  }
34230
34231  /**
34232   * Gets the index at which the first occurrence of `NaN` is found in `array`.
34233   *
34234   * @private
34235   * @param {Array} array The array to search.
34236   * @param {number} fromIndex The index to search from.
34237   * @param {boolean} [fromRight] Specify iterating from right to left.
34238   * @returns {number} Returns the index of the matched `NaN`, else `-1`.
34239   */
34240  function indexOfNaN(array, fromIndex, fromRight) {
34241    var length = array.length,
34242        index = fromIndex + (fromRight ? 0 : -1);
34243
34244    while ((fromRight ? index-- : ++index < length)) {
34245      var other = array[index];
34246      if (other !== other) {
34247        return index;
34248      }
34249    }
34250    return -1;
34251  }
34252
34253  /**
34254   * Checks if `value` is object-like.
34255   *
34256   * @private
34257   * @param {*} value The value to check.
34258   * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
34259   */
34260  function isObjectLike(value) {
34261    return !!value && typeof value == 'object';
34262  }
34263
34264  /**
34265   * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a
34266   * character code is whitespace.
34267   *
34268   * @private
34269   * @param {number} charCode The character code to inspect.
34270   * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`.
34271   */
34272  function isSpace(charCode) {
34273    return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCo
34273de == 5760 || charCode == 6158 ||
34274      (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279)));
34275  }
34276
34277  /**
34278   * Replaces all `placeholder` elements in `array` with an internal placeholder
34279   * and returns an array of their indexes.
34280   *
34281   * @private
34282   * @param {Array} array The array to modify.
34283   * @param {*} placeholder The placeholder to replace.
34284   * @returns {Array} Returns the new array of placeholder indexes.
34285   */
34286  function replaceHolders(array, placeholder) {
34287    var index = -1,
34288        length = array.length,
34289        resIndex = -1,
34290        result = [];
34291
34292    while (++index < length) {
34293      if (array[index] === placeholder) {
34294        array[index] = PLACEHOLDER;
34295        result[++resIndex] = index;
34296      }
34297    }
34298    return result;
34299  }
34300
34301  /**
34302   * An implementation of `_.uniq` optimized for sorted arrays without support
34303   * for callback shorthands and `this` binding.
34304   *
34305   * @private
34306   * @param {Array} array The array to inspect.
34307   * @param {Function} [iteratee] The function invoked per iteration.
34308   * @returns {Array} Returns the new duplicate-value-free array.
34309   */
34310  function sortedUniq(array, iteratee) {
34311    var seen,
34312        index = -1,
34313        length = array.length,
34314        resIndex = -1,
34315        result = [];
34316
34317    while (++index < length) {
34318      var value = array[index],
34319          computed = iteratee ? iteratee(value, index, array) : value;
34320
34321      if (!index || seen !== computed) {
34322        seen = computed;
34323        result[++resIndex] = value;
34324      }
34325    }
34326    return result;
34327  }
34328
34329  /**
34330   * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace
34331   * character of `string`.
34332   *
34333   * @private
34334   * @param {string} string The string to inspect.
34335   * @returns {number} Returns the index of the first non-whitespace character.
34336   */
34337  function trimmedLeftIndex(string) {
34338    var index = -1,
34339        length = string.length;
34340
34341    while (++index < length && isSpace(string.charCodeAt(index))) {}
34342    return index;
34343  }
34344
34345  /**
34346   * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace
34347   * character of `string`.
34348   *
34349   * @private
34350   * @param {string} string The string to inspect.
34351   * @returns {number} Returns the index of the last non-whitespace character.
34352   */
34353  function trimmedRightIndex(string) {
34354    var index = string.length;
34355
34356    while (index-- && isSpace(string.charCodeAt(index))) {}
34357    return index;
34358  }
34359
34360  /**
34361   * Used by `_.unescape` to convert HTML entities to characters.
34362   *
34363   * @private
34364   * @param {string} chr The matched character to unescape.
34365   * @returns {string} Returns the unescaped character.
34366   */
34367  function unescapeHtmlChar(chr) {
34368    return htmlUnescapes[chr];
34369  }
34370
34371  /*--------------------------------------------------------------------------*/
34372
34373  /**
34374   * Create a new pristine `lodash` function using the given `context` object.
34375   *
34376   * @static
34377   * @memberOf _
34378   * @category Utility
34379   * @param {Object} [context=root] The context object.
34380   * @returns {Function} Returns a new `lodash` function.
34381   * @example
34382   *
34383   * _.mixin({ 'foo': _.constant('foo') });
34384   *
34385   * var lodash = _.runInContext();
34386   * lodash.mixin({ 'bar': lodash.constant('bar') });
34387   *
34388   * _.isFunction(_.foo);
34389   * // => true
34390   * _.isFunction(_.bar);
34391   * // => false
34392   *
34393   * lodash.isFunction(lodash.foo);
34394   * // => false
34395   * lodash.isFunction(lodash.bar);
34396   * // => true
34397   *
34398   * // using `context` to mock `Date#getTime` use in `_.now`
34399   * var mock = _.runInContext({
34400   *   'Date': function() {
34401   *     return { 'getTime': getTimeMock };
34402   *   }
34403   * });
34404   *
34405   * // or creating a suped-up `defer` in Node.js
34406   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
34407   */
34408  function runInContext(context) {
34409    // Avoid issues with some ES3 environments that attempt to use values, named
34410    // after built-in constructors like `Object`, for the creation of literals.
34411    // ES5 clears this up by stating that literals must use built-in constructors.
34412    // See https://es5.github.io/#x11.1.5 for more details.
34413    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
34414
34415    /** Native constructor references. */
34416    var Array = context.Array,
34417        Date = context.Date,
34418        Error = context.Error,
34419        Function = context.Function,
34420        Math = context.Math,
34421        Number = context.Number,
34422        Object = context.Object,
34423        RegExp = context.RegExp,
34424        String = context.String,
34425        TypeError = context.TypeError;
34426
34427    /** Used for native method references. */
34428    var arrayProto = Array.prototype,
34429        objectProto = Object.prototype,
34430        stringProto = String.prototype;
34431
34432    /** Used to resolve the decompiled source of functions. */
34433    var fnToString = Function.prototype.toString;
34434
34435    /** Used to check objects for own properties. */
34436    var hasOwnProperty = objectProto.hasOwnProperty;
34437
34438    /** Used to generate unique IDs. */
34439    var idCounter = 0;
34440
34441    /**
34442     * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
34443     * of values.
34444     */
34445    var objToString = objectProto.toString;
34446
34447    /** Used to restore the original `_` reference in `_.noConflict`. */
34448    var oldDash = root._;
34449
34450    /** Used to detect if a method is native. */
34451    var reIsNative = RegExp('^' +
34452      fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
34453      .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
34454    );
34455
34456    /** Native method references. */
34457    var ArrayBuffer = context.ArrayBuffer,
34458        clearTimeout = context.clearTimeout,
34459        parseFloat = context.parseFloat,
34460        pow = Math.pow,
34461        propertyIsEnumerable = objectProto.propertyIsEnumerable,
34462        Set = getNative(context, 'Set'),
34463        setTimeout = context.setTimeout,
34464        splice = arrayProto.splice,
34465        Uint8Array = context.Uint8Array,
34466        WeakMap = getNative(context, 'WeakMap');
34467
34468    /* Native method references for those with the same name as other `lodash` methods. */
34469    var nativeCeil = Math.ceil,
34470        nativeCreate = getNative(Object, 'create'),
34471        nativeFloor = Math.floor,
34472        nativeIsArray = getNative(Array, 'isArray'),
34473        nativeIsFinite = context.isFinite,
34474        nativeKeys = getNative(Object, 'keys'),
34475        nativeMax = Math.max,
34476        nativeMin = Math.min,
34477        nativeNow = getNative(Date, 'now'),
34478        nativeParseInt = context.parseInt,
34479        nativeRandom = Math.random;
34480
34481    /** Used as references for `-Infinity` and `Infinity`. */
34482    var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY,
34483        POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
34484
34485    /** Used as references for the maximum length and index of an array. */
34486    var MAX_ARRAY_LENGTH = 4294967295,
34487        MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
34488        HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
34489
34490    /**
34491     * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
34492     * of an array-like value.
34493     */
34494    var MAX_SAFE_INTEGER = 9007199254740991;
34495
34496    /** Used to store function metadata. */
34497    var metaMap = WeakMap && new WeakMap;
34498
34499    /** Used to lookup unminified function names. */
34500    var realNames = {};
34501
34502    /*------------------------------------------------------------------------*/
34503
34504    /**
34505     * Creates a `lodash` object which wraps `value` to enable implicit chaining.
34506     * Methods that operate on and return arrays, collections, and functions can
34507     * be chained together. Methods that retrieve a single value or may return a
34508     * primitive value will automatically end the chain returning the unwrapped
34509     * value. Explicit chaining may be enabled using `_.chain`. The execution of
34510     * chained methods is lazy, that is, execution is deferred until `_#value`
34511     * is implicitly or explicitly called.
34512     *
34513     * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
34514     * fusion is an optimization strategy which merge iteratee calls; this can help
34515     * to avoid the creation of intermediate data structures and greatly reduce the
34516     * number of iteratee executions.
34517     *
34518     * Chaining is supported in custom builds as long as the `_#value` method is
34519     * directly or indirectly included in the build.
34520     *
34521     * In addition to lodash methods, wrappers have `Array` and `String` methods.
34522     *
34523     * The wrapper `Array` methods are:
34524     * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
34525     * `splice`, and `unshift`
34526     *
34527     * The wrapper `String` methods are:
34528     * `replace` and `split`
34529     *
34530     * The wrapper methods that support shortcut fusion are:
34531     * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
34532     * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
34533     * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
34534     * and `where`
34535     *
34536     * The chainable wrapper methods are:
34537     * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
34538     * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
34539     * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`,
34540     * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`,
34541     * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`,
34542     * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
34543     * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
34544     * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`,
34545     * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`,
34546     * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`,
34547     * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`,
34548     * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`,
34549     * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`,
34550     * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`,
34551     * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`,
34552     * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`,
34553     * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
34554     *
34555     * The wrapper methods that are **not** chainable by default are:
34556     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`,
34557     * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`,
34558     * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`,
34559     * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`,
34560     * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
34561     * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`,
34562     * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`,
34563     * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`,
34564     * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`,
34565     * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`,
34566     * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`,
34567     * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`,
34568     * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`,
34569     * `unescape`, `uniqueId`, `value`, and `words`
34570     *
34571     * The wrapper method `sample` will return a wrapped value when `n` is provided,
34572     * otherwise an unwrapped value is returned.
34573     *
34574     * @name _
34575     * @constructor
34576     * @category Chain
34577     * @param {*} value The value to wrap in a `lodash` instance.
34578     * @returns {Object} Returns the new `lodash` wrapper instance.
34579     * @example
34580     *
34581     * var wrapped = _([1, 2, 3]);
34582     *
34583     * // returns an unwrapped value
34584     * wrapped.reduce(function(total, n) {
34585     *   return total + n;
34586     * });
34587     * // => 6
34588     *
34589     * // returns a wrapped value
34590     * var squares = wrapped.map(function(n) {
34591     *   return n * n;
34592     * });
34593     *
34594     * _.isArray(squares);
34595     * // => false
34596     *
34597     * _.isArray(squares.value());
34598     * // => true
34599     */
34600    function lodash(value) {
34601      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
34602        if (value instanceof LodashWrapper) {
34603          return value;
34604        }
34605        if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
34606          return wrapperClone(value);
34607        }
34608      }
34609      return new LodashWrapper(value);
34610    }
34611
34612    /**
34613     * The function whose prototype all chaining wrappers inherit from.
34614     *
34615     * @private
34616     */
34617    function baseLodash() {
34618      // No operation performed.
34619    }
34620
34621    /**
34622     * The base constructor for creating `lodash` wrapper objects.
34623     *
34624     * @private
34625     * @param {*} value The value to wrap.
34626     * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
34627     * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
34628     */
34629    function LodashWrapper(value, chainAll, actions) {
34630      this.__wrapped__ = value;
34631      this.__actions__ = actions || [];
34632      this.__chain__ = !!chainAll;
34633    }
34634
34635    /**
34636     * An object environment feature flags.
34637     *
34638     * @static
34639     * @memberOf _
34640     * @type Object
34641     */
34642    var support = lodash.support = {};
34643
34644    /**
34645     * By default, the template delimiters used by lodash are like those in
34646     * embedded Ruby (ERB). Change the following template settings to use
34647     * alternative delimiters.
34648     *
34649     * @static
34650     * @memberOf _
34651     * @type Object
34652     */
34653    lodash.templateSettings = {
34654
34655      /**
34656       * Used to detect `data` property values to be HTML-escaped.
34657       *
34658       * @memberOf _.templateSettings
34659       * @type RegExp
34660       */
34661      'escape': reEscape,
34662
34663      /**
34664       * Used to detect code to be evaluated.
34665       *
34666       * @memberOf _.templateSettings
34667       * @type RegExp
34668       */
34669      'evaluate': reEvaluate,
34670
34671      /**
34672       * Used to detect `data` property values to inject.
34673       *
34674       * @memberOf _.templateSettings
34675       * @type RegExp
34676       */
34677      'interpolate': reInterpolate,
34678
34679      /**
34680       * Used to reference the data object in the template text.
34681       *
34682       * @memberOf _.templateSettings
34683       * @type string
34684       */
34685      'variable': '',
34686
34687      /**
34688       * Used to import variables into the compiled template.
34689       *
34690       * @memberOf _.templateSettings
34691       * @type Object
34692       */
34693      'imports': {
34694
34695        /**
34696         * A reference to the `lodash` function.
34697         *
34698         * @memberOf _.templateSettings.imports
34699         * @type Function
34700         */
34701        '_': lodash
34702      }
34703    };
34704
34705    /*------------------------------------------------------------------------*/
34706
34707    /**
34708     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
34709     *
34710     * @private
34711     * @param {*} value The value to wrap.
34712     */
34713    function LazyWrapper(value) {
34714      this.__wrapped__ = value;
34715      this.__actions__ = [];
34716      this.__dir__ = 1;
34717      this.__filtered__ = false;
34718      this.__iteratees__ = [];
34719      this.__takeCount__ = POSITIVE_INFINITY;
34720      this.__views__ = [];
34721    }
34722
34723    /**
34724     * Creates a clone of the lazy wrapper object.
34725     *
34726     * @private
34727     * @name clone
34728     * @memberOf LazyWrapper
34729     * @returns {Object} Returns the cloned `LazyWrapper` object.
34730     */
34731    function lazyClone() {
34732      var result = new LazyWrapper(this.__wrapped__);
34733      result.__actions__ = arrayCopy(this.__actions__);
34734      result.__dir__ = this.__dir__;
34735      result.__filtered__ = this.__filtered__;
34736      result.__iteratees__ = arrayCopy(this.__iteratees__);
34737      result.__takeCount__ = this.__takeCount__;
34738      result.__views__ = arrayCopy(this.__views__);
34739      return result;
34740    }
34741
34742    /**
34743     * Reverses the direction of lazy iteration.
34744     *
34745     * @private
34746     * @name reverse
34747     * @memberOf LazyWrapper
34748     * @returns {Object} Returns the new reversed `LazyWrapper` object.
34749     */
34750    function lazyReverse() {
34751      if (this.__filtered__) {
34752        var result = new LazyWrapper(this);
34753        result.__dir__ = -1;
34754        result.__filtered__ = true;
34755      } else {
34756        result = this.clone();
34757        result.__dir__ *= -1;
34758      }
34759      return result;
34760    }
34761
34762    /**
34763     * Extracts the unwrapped value from its lazy wrapper.
34764     *
34765     * @private
34766     * @name value
34767     * @memberOf LazyWrapper
34768     * @returns {*} Returns the unwrapped value.
34769     */
34770    function lazyValue() {
34771      var array = this.__wrapped__.value(),
34772          dir = this.__dir__,
34773          isArr = isArray(array),
34774          isRight = dir < 0,
34775          arrLength = isArr ? array.length : 0,
34776          view = getView(0, arrLength, this.__views__),
34777          start = view.start,
34778          end = view.end,
34779          length = end - start,
34780          index = isRight ? end : (start - 1),
34781          iteratees = this.__iteratees__,
34782          iterLength = iteratees.length,
34783          resIndex = 0,
34784          takeCount = nativeMin(length, this.__takeCount__);
34785
34786      if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) {
34787        return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__);
34788      }
34789      var result = [];
34790
34791      outer:
34792      while (length-- && resIndex < takeCount) {
34793        index += dir;
34794
34795        var iterIndex = -1,
34796            value = array[index];
34797
34798        while (++iterIndex < iterLength) {
34799          var data = iteratees[iterIndex],
34800              iteratee = data.iteratee,
34801              type = data.type,
34802              computed = iteratee(value);
34803
34804          if (type == LAZY_MAP_FLAG) {
34805            value = computed;
34806          } else if (!computed) {
34807            if (type == LAZY_FILTER_FLAG) {
34808              continue outer;
34809            } else {
34810              break outer;
34811            }
34812          }
34813        }
34814        result[resIndex++] = value;
34815      }
34816      return result;
34817    }
34818
34819    /*------------------------------------------------------------------------*/
34820
34821    /**
34822     * Creates a cache object to store key/value pairs.
34823     *
34824     * @private
34825     * @static
34826     * @name Cache
34827     * @memberOf _.memoize
34828     */
34829    function MapCache() {
34830      this.__data__ = {};
34831    }
34832
34833    /**
34834     * Removes `key` and its value from the cache.
34835     *
34836     * @private
34837     * @name delete
34838     * @memberOf _.memoize.Cache
34839     * @param {string} key The key of the value to remove.
34840     * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`.
34841     */
34842    function mapDelete(key) {
34843      return this.has(key) && delete this.__data__[key];
34844    }
34845
34846    /**
34847     * Gets the cached value for `key`.
34848     *
34849     * @private
34850     * @name get
34851     * @memberOf _.memoize.Cache
34852     * @param {string} key The key of the value to get.
34853     * @returns {*} Returns the cached value.
34854     */
34855    function mapGet(key) {
34856      return key == '__proto__' ? undefined : this.__data__[key];
34857    }
34858
34859    /**
34860     * Checks if a cached value for `key` exists.
34861     *
34862     * @private
34863     * @name has
34864     * @memberOf _.memoize.Cache
34865     * @param {string} key The key of the entry to check.
34866     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
34867     */
34868    function mapHas(key) {
34869      return key != '__proto__' && hasOwnProperty.call(this.__data__, key);
34870    }
34871
34872    /**
34873     * Sets `value` to `key` of the cache.
34874     *
34875     * @private
34876     * @name set
34877     * @memberOf _.memoize.Cache
34878     * @param {string} key The key of the value to cache.
34879     * @param {*} value The value to cache.
34880     * @returns {Object} Returns the cache object.
34881     */
34882    function mapSet(key, value) {
34883      if (key != '__proto__') {
34884        this.__data__[key] = value;
34885      }
34886      return this;
34887    }
34888
34889    /*------------------------------------------------------------------------*/
34890
34891    /**
34892     *
34893     * Creates a cache object to store unique values.
34894     *
34895     * @private
34896     * @param {Array} [values] The values to cache.
34897     */
34898    function SetCache(values) {
34899      var length = values ? values.length : 0;
34900
34901      this.data = { 'hash': nativeCreate(null), 'set': new Set };
34902      while (length--) {
34903        this.push(values[length]);
34904      }
34905    }
34906
34907    /**
34908     * Checks if `value` is in `cache` mimicking the return signature of
34909     * `_.indexOf` by returning `0` if the value is found, else `-1`.
34910     *
34911     * @private
34912     * @param {Object} cache The cache to search.
34913     * @param {*} value The value to search for.
34914     * @returns {number} Returns `0` if `value` is found, else `-1`.
34915     */
34916    function cacheIndexOf(cache, value) {
34917      var data = cache.data,
34918          result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
34919
34920      return result ? 0 : -1;
34921    }
34922
34923    /**
34924     * Adds `value` to the cache.
34925     *
34926     * @private
34927     * @name push
34928     * @memberOf SetCache
34929     * @param {*} value The value to cache.
34930     */
34931    function cachePush(value) {
34932      var data = this.data;
34933      if (typeof value == 'string' || isObject(value)) {
34934        data.set.add(value);
34935      } else {
34936        data.hash[value] = true;
34937      }
34938    }
34939
34940    /*------------------------------------------------------------------------*/
34941
34942    /**
34943     * Creates a new array joining `array` with `other`.
34944     *
34945     * @private
34946     * @param {Array} array The array to join.
34947     * @param {Array} other The other array to join.
34948     * @returns {Array} Returns the new concatenated array.
34949     */
34950    function arrayConcat(array, other) {
34951      var index = -1,
34952          length = array.length,
34953          othIndex = -1,
34954          othLength = other.length,
34955          result = Array(length + othLength);
34956
34957      while (++index < length) {
34958        result[index] = array[index];
34959      }
34960      while (++othIndex < othLength) {
34961        result[index++] = other[othIndex];
34962      }
34963      return result;
34964    }
34965
34966    /**
34967     * Copies the values of `source` to `array`.
34968     *
34969     * @private
34970     * @param {Array} source The array to copy values from.
34971     * @param {Array} [array=[]] The array to copy values to.
34972     * @returns {Array} Returns `array`.
34973     */
34974    function arrayCopy(source, array) {
34975      var index = -1,
34976          length = source.length;
34977
34978      array || (array = Array(length));
34979      while (++index < length) {
34980        array[index] = source[index];
34981      }
34982      return array;
34983    }
34984
34985    /**
34986     * A specialized version of `_.forEach` for arrays without support for callback
34987     * shorthands and `this` binding.
34988     *
34989     * @private
34990     * @param {Array} array The array to iterate over.
34991     * @param {Function} iteratee The function invoked per iteration.
34992     * @returns {Array} Returns `array`.
34993     */
34994    function arrayEach(array, iteratee) {
34995      var index = -1,
34996          length = array.length;
34997
34998      while (++index < length) {
34999        if (iteratee(array[index], index, array) === false) {
35000          break;
35001        }
35002      }
35003      return array;
35004    }
35005
35006    /**
35007     * A specialized version of `_.forEachRight` for arrays without support for
35008     * callback shorthands and `this` binding.
35009     *
35010     * @private
35011     * @param {Array} array The array to iterate over.
35012     * @param {Function} iteratee The function invoked per iteration.
35013     * @returns {Array} Returns `array`.
35014     */
35015    function arrayEachRight(array, iteratee) {
35016      var length = array.length;
35017
35018      while (length--) {
35019        if (iteratee(array[length], length, array) === false) {
35020          break;
35021        }
35022      }
35023      return array;
35024    }
35025
35026    /**
35027     * A specialized version of `_.every` for arrays without support for callback
35028     * shorthands and `this` binding.
35029     *
35030     * @private
35031     * @param {Array} array The array to iterate over.
35032     * @param {Function} predicate The function invoked per iteration.
35033     * @returns {boolean} Returns `true` if all elements pass the predicate check,
35034     *  else `false`.
35035     */
35036    function arrayEvery(array, predicate) {
35037      var index = -1,
35038          length = array.length;
35039
35040      while (++index < length) {
35041        if (!predicate(array[index], index, array)) {
35042          return false;
35043        }
35044      }
35045      return true;
35046    }
35047
35048    /**
35049     * A specialized version of `baseExtremum` for arrays which invokes `iteratee`
35050     * with one argument: (value).
35051     *
35052     * @private
35053     * @param {Array} array The array to iterate over.
35054     * @param {Function} iteratee The function invoked per iteration.
35055     * @param {Function} comparator The function used to compare values.
35056     * @param {*} exValue The initial extremum value.
35057     * @returns {*} Returns the extremum value.
35058     */
35059    function arrayExtremum(array, iteratee, comparator, exValue) {
35060      var index = -1,
35061          length = array.length,
35062          computed = exValue,
35063          result = computed;
35064
35065      while (++index < length) {
35066        var value = array[index],
35067            current = +iteratee(value);
35068
35069        if (comparator(current, computed)) {
35070          computed = current;
35071          result = value;
35072        }
35073      }
35074      return result;
35075    }
35076
35077    /**
35078     * A specialized version of `_.filter` for arrays without support for callback
35079     * shorthands and `this` binding.
35080     *
35081     * @private
35082     * @param {Array} array The array to iterate over.
35083     * @param {Function} predicate The function invoked per iteration.
35084     * @returns {Array} Returns the new filtered array.
35085     */
35086    function arrayFilter(array, predicate) {
35087      var index = -1,
35088          length = array.length,
35089          resIndex = -1,
35090          result = [];
35091
35092      while (++index < length) {
35093        var value = array[index];
35094        if (predicate(value, index, array)) {
35095          result[++resIndex] = value;
35096        }
35097      }
35098      return result;
35099    }
35100
35101    /**
35102     * A specialized version of `_.map` for arrays without support for callback
35103     * shorthands and `this` binding.
35104     *
35105     * @private
35106     * @param {Array} array The array to iterate over.
35107     * @param {Function} iteratee The function invoked per iteration.
35108     * @returns {Array} Returns the new mapped array.
35109     */
35110    function arrayMap(array, iteratee) {
35111      var index = -1,
35112          length = array.length,
35113          result = Array(length);
35114
35115      while (++index < length) {
35116        result[index] = iteratee(array[index], index, array);
35117      }
35118      return result;
35119    }
35120
35121    /**
35122     * Appends the elements of `values` to `array`.
35123     *
35124     * @private
35125     * @param {Array} array The array to modify.
35126     * @param {Array} values The values to append.
35127     * @returns {Array} Returns `array`.
35128     */
35129    function arrayPush(array, values) {
35130      var index = -1,
35131          length = values.length,
35132          offset = array.length;
35133
35134      while (++index < length) {
35135        array[offset + index] = values[index];
35136      }
35137      return array;
35138    }
35139
35140    /**
35141     * A specialized version of `_.reduce` for arrays without support for callback
35142     * shorthands and `this` binding.
35143     *
35144     * @private
35145     * @param {Array} array The array to iterate over.
35146     * @param {Function} iteratee The function invoked per iteration.
35147     * @param {*} [accumulator] The initial value.
35148     * @param {boolean} [initFromArray] Specify using the first element of `array`
35149     *  as the initial value.
35150     * @returns {*} Returns the accumulated value.
35151     */
35152    function arrayReduce(array, iteratee, accumulator, initFromArray) {
35153      var index = -1,
35154          length = array.length;
35155
35156      if (initFromArray && length) {
35157        accumulator = array[++index];
35158      }
35159      while (++index < length) {
35160        accumulator = iteratee(accumulator, array[index], index, array);
35161      }
35162      return accumulator;
35163    }
35164
35165    /**
35166     * A specialized version of `_.reduceRight` for arrays without support for
35167     * callback shorthands and `this` binding.
35168     *
35169     * @private
35170     * @param {Array} array The array to iterate over.
35171     * @param {Function} iteratee The function invoked per iteration.
35172     * @param {*} [accumulator] The initial value.
35173     * @param {boolean} [initFromArray] Specify using the last element of `array`
35174     *  as the initial value.
35175     * @returns {*} Returns the accumulated value.
35176     */
35177    function arrayReduceRight(array, iteratee, accumulator, initFromArray) {
35178      var length = array.length;
35179      if (initFromArray && length) {
35180        accumulator = array[--length];
35181      }
35182      while (length--) {
35183        accumulator = iteratee(accumulator, array[length], length, array);
35184      }
35185      return accumulator;
35186    }
35187
35188    /**
35189     * A specialized version of `_.some` for arrays without support for callback
35190     * shorthands and `this` binding.
35191     *
35192     * @private
35193     * @param {Array} array The array to iterate over.
35194     * @param {Function} predicate The function invoked per iteration.
35195     * @returns {boolean} Returns `true` if any element passes the predicate check,
35196     *  else `false`.
35197     */
35198    function arraySome(array, predicate) {
35199      var index = -1,
35200          length = array.length;
35201
35202      while (++index < length) {
35203        if (predicate(array[index], index, array)) {
35204          return true;
35205        }
35206      }
35207      return false;
35208    }
35209
35210    /**
35211     * A specialized version of `_.sum` for arrays without support for callback
35212     * shorthands and `this` binding..
35213     *
35214     * @private
35215     * @param {Array} array The array to iterate over.
35216     * @param {Function} iteratee The function invoked per iteration.
35217     * @returns {number} Returns the sum.
35218     */
35219    function arraySum(array, iteratee) {
35220      var length = array.length,
35221          result = 0;
35222
35223      while (length--) {
35224        result += +iteratee(array[length]) || 0;
35225      }
35226      return result;
35227    }
35228
35229    /**
35230     * Used by `_.defaults` to customize its `_.assign` use.
35231     *
35232     * @private
35233     * @param {*} objectValue The destination object property value.
35234     * @param {*} sourceValue The source object property value.
35235     * @returns {*} Returns the value to assign to the destination object.
35236     */
35237    function assignDefaults(objectValue, sourceValue) {
35238      return objectValue === undefined ? sourceValue : objectValue;
35239    }
35240
35241    /**
35242     * Used by `_.template` to customize its `_.assign` use.
35243     *
35244     * **Note:** This function is like `assignDefaults` except that it ignores
35245     * inherited property values when checking if a property is `undefined`.
35246     *
35247     * @private
35248     * @param {*} objectValue The destination object property value.
35249     * @param {*} sourceValue The source object property value.
35250     * @param {string} key The key associated with the object and source values.
35251     * @param {Object} object The destination object.
35252     * @returns {*} Returns the value to assign to the destination object.
35253     */
35254    function assignOwnDefaults(objectValue, sourceValue, key, object) {
35255      return (objectValue === undefined || !hasOwnProperty.call(object, key))
35256        ? sourceValue
35257        : objectValue;
35258    }
35259
35260    /**
35261     * A specialized version of `_.assign` for customizing assigned values without
35262     * support for argument juggling, multiple sources, and `this` binding `customizer`
35263     * functions.
35264     *
35265     * @private
35266     * @param {Object} object The destination object.
35267     * @param {Object} source The source object.
35268     * @param {Function} customizer The function to customize assigned values.
35269     * @returns {Object} Returns `object`.
35270     */
35271    function assignWith(object, source, customizer) {
35272      var index = -1,
35273          props = keys(source),
35274          length = props.length;
35275
35276      while (++index < length) {
35277        var key = props[index],
35278            value = object[key],
35279            result = customizer(value, source[key], key, object, source);
35280
35281        if ((result === result ? (result !== value) : (value === value)) ||
35282            (value === undefined && !(key in object))) {
35283          object[key] = result;
35284        }
35285      }
35286      return object;
35287    }
35288
35289    /**
35290     * The base implementation of `_.assign` without support for argument juggling,
35291     * multiple sources, and `customizer` functions.
35292     *
35293     * @private
35294     * @param {Object} object The destination object.
35295     * @param {Object} source The source object.
35296     * @returns {Object} Returns `object`.
35297     */
35298    function baseAssign(object, source) {
35299      return source == null
35300        ? object
35301        : baseCopy(source, keys(source), object);
35302    }
35303
35304    /**
35305     * The base implementation of `_.at` without support for string collections
35306     * and individual key arguments.
35307     *
35308     * @private
35309     * @param {Array|Object} collection The collection to iterate over.
35310     * @param {number[]|string[]} props The property names or indexes of elements to pick.
35311     * @returns {Array} Returns the new array of picked elements.
35312     */
35313    function baseAt(collection, props) {
35314      var index = -1,
35315          isNil = collection == null,
35316          isArr = !isNil && isArrayLike(collection),
35317          length = isArr ? collection.length : 0,
35318          propsLength = props.length,
35319          result = Array(propsLength);
35320
35321      while(++index < propsLength) {
35322        var key = props[index];
35323        if (isArr) {
35324          result[index] = isIndex(key, length) ? collection[key] : undefined;
35325        } else {
35326          result[index] = isNil ? undefined : collection[key];
35327        }
35328      }
35329      return result;
35330    }
35331
35332    /**
35333     * Copies properties of `source` to `object`.
35334     *
35335     * @private
35336     * @param {Object} source The object to copy properties from.
35337     * @param {Array} props The property names to copy.
35338     * @param {Object} [object={}] The object to copy properties to.
35339     * @returns {Object} Returns `object`.
35340     */
35341    function baseCopy(source, props, object) {
35342      object || (object = {});
35343
35344      var index = -1,
35345          length = props.length;
35346
35347      while (++index < length) {
35348        var key = props[index];
35349        object[key] = source[key];
35350      }
35351      return object;
35352    }
35353
35354    /**
35355     * The base implementation of `_.callback` which supports specifying the
35356     * number of arguments to provide to `func`.
35357     *
35358     * @private
35359     * @param {*} [func=_.identity] The value to convert to a callback.
35360     * @param {*} [thisArg] The `this` binding of `func`.
35361     * @param {number} [argCount] The number of arguments to provide to `func`.
35362     * @returns {Function} Returns the callback.
35363     */
35364    function baseCallback(func, thisArg, argCount) {
35365      var type = typeof func;
35366      if (type == 'function') {
35367        return thisArg === undefined
35368          ? func
35369          : bindCallback(func, thisArg, argCount);
35370      }
35371      if (func == null) {
35372        return identity;
35373      }
35374      if (type == 'object') {
35375        return baseMatches(func);
35376      }
35377      return thisArg === undefined
35378        ? property(func)
35379        : baseMatchesProperty(func, thisArg);
35380    }
35381
35382    /**
35383     * The base implementation of `_.clone` without support for argument juggling
35384     * and `this` binding `customizer` functions.
35385     *
35386     * @private
35387     * @param {*} value The value to clone.
35388     * @param {boolean} [isDeep] Specify a deep clone.
35389     * @param {Function} [customizer] The function to customize cloning values.
35390     * @param {string} [key] The key of `value`.
35391     * @param {Object} [object] The object `value` belongs to.
35392     * @param {Array} [stackA=[]] Tracks traversed source objects.
35393     * @param {Array} [stackB=[]] Associates clones with source counterparts.
35394     * @returns {*} Returns the cloned value.
35395     */
35396    function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
35397      var result;
35398      if (customizer) {
35399        result = object ? customizer(value, key, object) : customizer(value);
35400      }
35401      if (result !== undefined) {
35402        return result;
35403      }
35404      if (!isObject(value)) {
35405        return value;
35406      }
35407      var isArr = isArray(value);
35408      if (isArr) {
35409        result = initCloneArray(value);
35410        if (!isDeep) {
35411          return arrayCopy(value, result);
35412        }
35413      } else {
35414        var tag = objToString.call(value),
35415            isFunc = tag == funcTag;
35416
35417        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
35418          result = initCloneObject(isFunc ? {} : value);
35419          if (!isDeep) {
35420            return baseAssign(result, value);
35421          }
35422        } else {
35423          return cloneableTags[tag]
35424            ? initCloneByTag(value, tag, isDeep)
35425            : (object ? value : {});
35426        }
35427      }
35428      // Check for circular references and return its corresponding clone.
35429      stackA || (stackA = []);
35430      stackB || (stackB = []);
35431
35432      var length = stackA.length;
35433      while (length--) {
35434        if (stackA[length] == value) {
35435          return stackB[length];
35436        }
35437      }
35438      // Add the source value to the stack of traversed objects and associate it with its clone.
35439      stackA.push(value);
35440      stackB.push(result);
35441
35442      // Recursively populate clone (susceptible to call stack limits).
35443      (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
35444        result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
35445      });
35446      return result;
35447    }
35448
35449    /**
35450     * The base implementation of `_.create` without support for assigning
35451     * properties to the created object.
35452     *
35453     * @private
35454     * @param {Object} prototype The object to inherit from.
35455     * @returns {Object} Returns the new object.
35456     */
35457    var baseCreate = (function() {
35458      function object() {}
35459      return function(prototype) {
35460        if (isObject(prototype)) {
35461          object.prototype = prototype;
35462          var result = new object;
35463          object.prototype = undefined;
35464        }
35465        return result || {};
35466      };
35467    }());
35468
35469    /**
35470     * The base implementation of `_.delay` and `_.defer` which accepts an index
35471     * of where to slice the arguments to provide to `func`.
35472     *
35473     * @private
35474     * @param {Function} func The function to delay.
35475     * @param {number} wait The number of milliseconds to delay invocation.
35476     * @param {Object} args The arguments provide to `func`.
35477     * @returns {number} Returns the timer id.
35478     */
35479    function baseDelay(func, wait, args) {
35480      if (typeof func != 'function') {
35481        throw new TypeError(FUNC_ERROR_TEXT);
35482      }
35483      return setTimeout(function() { func.apply(undefined, args); }, wait);
35484    }
35485
35486    /**
35487     * The base implementation of `_.difference` which accepts a single array
35488     * of values to exclude.
35489     *
35490     * @private
35491     * @param {Array} array The array to inspect.
35492     * @param {Array} values The values to exclude.
35493     * @returns {Array} Returns the new array of filtered values.
35494     */
35495    function baseDifference(array, values) {
35496      var length = array ? array.length : 0,
35497          result = [];
35498
35499      if (!length) {
35500        return result;
35501      }
35502      var index = -1,
35503          indexOf = getIndexOf(),
35504          isCommon = indexOf == baseIndexOf,
35505          cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null,
35506          valuesLength = values.length;
35507
35508      if (cache) {
35509        indexOf = cacheIndexOf;
35510        isCommon = false;
35511        values = cache;
35512      }
35513      outer:
35514      while (++index < length) {
35515        var value = array[index];
35516
35517        if (isCommon && value === value) {
35518          var valuesIndex = valuesLength;
35519          while (valuesIndex--) {
35520            if (values[valuesIndex] === value) {
35521              continue outer;
35522            }
35523          }
35524          result.push(value);
35525        }
35526        else if (indexOf(values, value, 0) < 0) {
35527          result.push(value);
35528        }
35529      }
35530      return result;
35531    }
35532
35533    /**
35534     * The base implementation of `_.forEach` without support for callback
35535     * shorthands and `this` binding.
35536     *
35537     * @private
35538     * @param {Array|Object|string} collection The collection to iterate over.
35539     * @param {Function} iteratee The function invoked per iteration.
35540     * @returns {Array|Object|string} Returns `collection`.
35541     */
35542    var baseEach = createBaseEach(baseForOwn);
35543
35544    /**
35545     * The base implementation of `_.forEachRight` without support for callback
35546     * shorthands and `this` binding.
35547     *
35548     * @private
35549     * @param {Array|Object|string} collection The collection to iterate over.
35550     * @param {Function} iteratee The function invoked per iteration.
35551     * @returns {Array|Object|string} Returns `collection`.
35552     */
35553    var baseEachRight = createBaseEach(baseForOwnRight, true);
35554
35555    /**
35556     * The base implementation of `_.every` without support for callback
35557     * shorthands and `this` binding.
35558     *
35559     * @private
35560     * @param {Array|Object|string} collection The collection to iterate over.
35561     * @param {Function} predicate The function invoked per iteration.
35562     * @returns {boolean} Returns `true` if all elements pass the predicate check,
35563     *  else `false`
35564     */
35565    function baseEvery(collection, predicate) {
35566      var result = true;
35567      baseEach(collection, function(value, index, collection) {
35568        result = !!predicate(value, index, collection);
35569        return result;
35570      });
35571      return result;
35572    }
35573
35574    /**
35575     * Gets the extremum value of `collection` invoking `iteratee` for each value
35576     * in `collection` to generate the criterion by which the value is ranked.
35577     * The `iteratee` is invoked with three arguments: (value, index|key, collection).
35578     *
35579     * @private
35580     * @param {Array|Object|string} collection The collection to iterate over.
35581     * @param {Function} iteratee The function invoked per iteration.
35582     * @param {Function} comparator The function used to compare values.
35583     * @param {*} exValue The initial extremum value.
35584     * @returns {*} Returns the extremum value.
35585     */
35586    function baseExtremum(collection, iteratee, comparator, exValue) {
35587      var computed = exValue,
35588          result = computed;
35589
35590      baseEach(collection, function(value, index, collection) {
35591        var current = +iteratee(value, index, collection);
35592        if (comparator(current, computed) || (current === exValue && current === result)) {
35593          computed = current;
35594          result = value;
35595        }
35596      });
35597      return result;
35598    }
35599
35600    /**
35601     * The base implementation of `_.fill` without an iteratee call guard.
35602     *
35603     * @private
35604     * @param {Array} array The array to fill.
35605     * @param {*} value The value to fill `array` with.
35606     * @param {number} [start=0] The start position.
35607     * @param {number} [end=array.length] The end position.
35608     * @returns {Array} Returns `array`.
35609     */
35610    function baseFill(array, value, start, end) {
35611      var length = array.length;
35612
35613      start = start == null ? 0 : (+start || 0);
35614      if (start < 0) {
35615        start = -start > length ? 0 : (length + start);
35616      }
35617      end = (end === undefined || end > length) ? length : (+end || 0);
35618      if (end < 0) {
35619        end += length;
35620      }
35621      length = start > end ? 0 : (end >>> 0);
35622      start >>>= 0;
35623
35624      while (start < length) {
35625        array[start++] = value;
35626      }
35627      return array;
35628    }
35629
35630    /**
35631     * The base implementation of `_.filter` without support for callback
35632     * shorthands and `this` binding.
35633     *
35634     * @private
35635     * @param {Array|Object|string} collection The collection to iterate over.
35636     * @param {Function} predicate The function invoked per iteration.
35637     * @returns {Array} Returns the new filtered array.
35638     */
35639    function baseFilter(collection, predicate) {
35640      var result = [];
35641      baseEach(collection, function(value, index, collection) {
35642        if (predicate(value, index, collection)) {
35643          result.push(value);
35644        }
35645      });
35646      return result;
35647    }
35648
35649    /**
35650     * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
35651     * without support for callback shorthands and `this` binding, which iterates
35652     * over `collection` using the provided `eachFunc`.
35653     *
35654     * @private
35655     * @param {Array|Object|string} collection The collection to search.
35656     * @param {Function} predicate The function invoked per iteration.
35657     * @param {Function} eachFunc The function to iterate over `collection`.
35658     * @param {boolean} [retKey] Specify returning the key of the found element
35659     *  instead of the element itself.
35660     * @returns {*} Returns the found element or its key, else `undefined`.
35661     */
35662    function baseFind(collection, predicate, eachFunc, retKey) {
35663      var result;
35664      eachFunc(collection, function(value, key, collection) {
35665        if (predicate(value, key, collection)) {
35666          result = retKey ? key : value;
35667          return false;
35668        }
35669      });
35670      return result;
35671    }
35672
35673    /**
35674     * The base implementation of `_.flatten` with added support for restricting
35675     * flattening and specifying the start index.
35676     *
35677     * @private
35678     * @param {Array} array The array to flatten.
35679     * @param {boolean} [isDeep] Specify a deep flatten.
35680     * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
35681     * @param {Array} [result=[]] The initial result value.
35682     * @returns {Array} Returns the new flattened array.
35683     */
35684    function baseFlatten(array, isDeep, isStrict, result) {
35685      result || (result = []);
35686
35687      var index = -1,
35688          length = array.length;
35689
35690      while (++index < length) {
35691        var value = array[index];
35692        if (isObjectLike(value) && isArrayLike(value) &&
35693            (isStrict || isArray(value) || isArguments(value))) {
35694          if (isDeep) {
35695            // Recursively flatten arrays (susceptible to call stack limits).
35696            baseFlatten(value, isDeep, isStrict, result);
35697          } else {
35698            arrayPush(result, value);
35699          }
35700        } else if (!isStrict) {
35701          result[result.length] = value;
35702        }
35703      }
35704      return result;
35705    }
35706
35707    /**
35708     * The base implementation of `baseForIn` and `baseForOwn` which iterates
35709     * over `object` properties returned by `keysFunc` invoking `iteratee` for
35710     * each property. Iteratee functions may exit iteration early by explicitly
35711     * returning `false`.
35712     *
35713     * @private
35714     * @param {Object} object The object to iterate over.
35715     * @param {Function} iteratee The function invoked per iteration.
35716     * @param {Function} keysFunc The function to get the keys of `object`.
35717     * @returns {Object} Returns `object`.
35718     */
35719    var baseFor = createBaseFor();
35720
35721    /**
35722     * This function is like `baseFor` except that it iterates over properties
35723     * in the opposite order.
35724     *
35725     * @private
35726     * @param {Object} object The object to iterate over.
35727     * @param {Function} iteratee The function invoked per iteration.
35728     * @param {Function} keysFunc The function to get the keys of `object`.
35729     * @returns {Object} Returns `object`.
35730     */
35731    var baseForRight = createBaseFor(true);
35732
35733    /**
35734     * The base implementation of `_.forIn` without support for callback
35735     * shorthands and `this` binding.
35736     *
35737     * @private
35738     * @param {Object} object The object to iterate over.
35739     * @param {Function} iteratee The function invoked per iteration.
35740     * @returns {Object} Returns `object`.
35741     */
35742    function baseForIn(object, iteratee) {
35743      return baseFor(object, iteratee, keysIn);
35744    }
35745
35746    /**
35747     * The base implementation of `_.forOwn` without support for callback
35748     * shorthands and `this` binding.
35749     *
35750     * @private
35751     * @param {Object} object The object to iterate over.
35752     * @param {Function} iteratee The function invoked per iteration.
35753     * @returns {Object} Returns `object`.
35754     */
35755    function baseForOwn(object, iteratee) {
35756      return baseFor(object, iteratee, keys);
35757    }
35758
35759    /**
35760     * The base implementation of `_.forOwnRight` without support for callback
35761     * shorthands and `this` binding.
35762     *
35763     * @private
35764     * @param {Object} object The object to iterate over.
35765     * @param {Function} iteratee The function invoked per iteration.
35766     * @returns {Object} Returns `object`.
35767     */
35768    function baseForOwnRight(object, iteratee) {
35769      return baseForRight(object, iteratee, keys);
35770    }
35771
35772    /**
35773     * The base implementation of `_.functions` which creates an array of
35774     * `object` function property names filtered from those provided.
35775     *
35776     * @private
35777     * @param {Object} object The object to inspect.
35778     * @param {Array} props The property names to filter.
35779     * @returns {Array} Returns the new array of filtered property names.
35780     */
35781    function baseFunctions(object, props) {
35782      var index = -1,
35783          length = props.length,
35784          resIndex = -1,
35785          result = [];
35786
35787      while (++index < length) {
35788        var key = props[index];
35789        if (isFunction(object[key])) {
35790          result[++resIndex] = key;
35791        }
35792      }
35793      return result;
35794    }
35795
35796    /**
35797     * The base implementation of `get` without support for string paths
35798     * and default values.
35799     *
35800     * @private
35801     * @param {Object} object The object to query.
35802     * @param {Array} path The path of the property to get.
35803     * @param {string} [pathKey] The key representation of path.
35804     * @returns {*} Returns the resolved value.
35805     */
35806    function baseGet(object, path, pathKey) {
35807      if (object == null) {
35808        return;
35809      }
35810      if (pathKey !== undefined && pathKey in toObject(object)) {
35811        path = [pathKey];
35812      }
35813      var index = 0,
35814          length = path.length;
35815
35816      while (object != null && index < length) {
35817        object = object[path[index++]];
35818      }
35819      return (index && index == length) ? object : undefined;
35820    }
35821
35822    /**
35823     * The base implementation of `_.isEqual` without support for `this` binding
35824     * `customizer` functions.
35825     *
35826     * @private
35827     * @param {*} value The value to compare.
35828     * @param {*} other The other value to compare.
35829     * @param {Function} [customizer] The function to customize comparing values.
35830     * @param {boolean} [isLoose] Specify performing partial comparisons.
35831     * @param {Array} [stackA] Tracks traversed `value` objects.
35832     * @param {Array} [stackB] Tracks traversed `other` objects.
35833     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
35834     */
35835    function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
35836      if (value === other) {
35837        return true;
35838      }
35839      if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
35840        return value !== value && other !== other;
35841      }
35842      return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
35843    }
35844
35845    /**
35846     * A specialized version of `baseIsEqual` for arrays and objects which performs
35847     * deep comparisons and tracks traversed objects enabling objects with circular
35848     * references to be compared.
35849     *
35850     * @private
35851     * @param {Object} object The object to compare.
35852     * @param {Object} other The other object to compare.
35853     * @param {Function} equalFunc The function to determine equivalents of values.
35854     * @param {Function} [customizer] The function to customize comparing objects.
35855     * @param {boolean} [isLoose] Specify performing partial comparisons.
35856     * @param {Array} [stackA=[]] Tracks traversed `value` objects.
35857     * @param {Array} [stackB=[]] Tracks traversed `other` objects.
35858     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
35859     */
35860    function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
35861      var objIsArr = isArray(object),
35862          othIsArr = isArray(other),
35863          objTag = arrayTag,
35864          othTag = arrayTag;
35865
35866      if (!objIsArr) {
35867        objTag = objToString.call(object);
35868        if (objTag == argsTag) {
35869          objTag = objectTag;
35870        } else if (objTag != objectTag) {
35871          objIsArr = isTypedArray(object);
35872        }
35873      }
35874      if (!othIsArr) {
35875        othTag = objToString.call(other);
35876        if (othTag == argsTag) {
35877          othTag = objectTag;
35878        } else if (othTag != objectTag) {
35879          othIsArr = isTypedArray(other);
35880        }
35881      }
35882      var objIsObj = objTag == objectTag,
35883          othIsObj = othTag == objectTag,
35884          isSameTag = objTag == othTag;
35885
35886      if (isSameTag && !(objIsArr || objIsObj)) {
35887        return equalByTag(object, other, objTag);
35888      }
35889      if (!isLoose) {
35890        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
35891            othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
35892
35893        if (objIsWrapped || othIsWrapped) {
35894          return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
35895        }
35896      }
35897      if (!isSameTag) {
35898        return false;
35899      }
35900      // Assume cyclic values are equal.
35901      // For more information on detecting circular references see https://es5.github.io/#JO.
35902      stackA || (stackA = []);
35903      stackB || (stackB = []);
35904
35905      var length = stackA.length;
35906      while (length--) {
35907        if (stackA[length] == object) {
35908          return stackB[length] == other;
35909        }
35910      }
35911      // Add `object` and `other` to the stack of traversed objects.
35912      stackA.push(object);
35913      stackB.push(other);
35914
35915      var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
35916
35917      stackA.pop();
35918      stackB.pop();
35919
35920      return result;
35921    }
35922
35923    /**
35924     * The base implementation of `_.isMatch` without support for callback
35925     * shorthands and `this` binding.
35926     *
35927     * @private
35928     * @param {Object} object The object to inspect.
35929     * @param {Array} matchData The propery names, values, and compare flags to match.
35930     * @param {Function} [customizer] The function to customize comparing objects.
35931     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
35932     */
35933    function baseIsMatch(object, matchData, customizer) {
35934      var index = matchData.length,
35935          length = index,
35936          noCustomizer = !customizer;
35937
35938      if (object == null) {
35939        return !length;
35940      }
35941      object = toObject(object);
35942      while (index--) {
35943        var data = matchData[index];
35944        if ((noCustomizer && data[2])
35945              ? data[1] !== object[data[0]]
35946              : !(data[0] in object)
35947            ) {
35948          return false;
35949        }
35950      }
35951      while (++index < length) {
35952        data = matchData[index];
35953        var key = data[0],
35954            objValue = object[key],
35955            srcValue = data[1];
35956
35957        if (noCustomizer && data[2]) {
35958          if (objValue === undefined && !(key in object)) {
35959            return false;
35960          }
35961        } else {
35962          var result = customizer ? customizer(objValue, srcValue, key) : undefined;
35963          if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
35964            return false;
35965          }
35966        }
35967      }
35968      return true;
35969    }
35970
35971    /**
35972     * The base implementation of `_.map` without support for callback shorthands
35973     * and `this` binding.
35974     *
35975     * @private
35976     * @param {Array|Object|string} collection The collection to iterate over.
35977     * @param {Function} iteratee The function invoked per iteration.
35978     * @returns {Array} Returns the new mapped array.
35979     */
35980    function baseMap(collection, iteratee) {
35981      var index = -1,
35982          result = isArrayLike(collection) ? Array(collection.length) : [];
35983
35984      baseEach(collection, function(value, key, collection) {
35985        result[++index] = iteratee(value, key, collection);
35986      });
35987      return result;
35988    }
35989
35990    /**
35991     * The base implementation of `_.matches` which does not clone `source`.
35992     *
35993     * @private
35994     * @param {Object} source The object of property values to match.
35995     * @returns {Function} Returns the new function.
35996     */
35997    function baseMatches(source) {
35998      var matchData = getMatchData(source);
35999      if (matchData.length == 1 && matchData[0][2]) {
36000        var key = matchData[0][0],
36001            value = matchData[0][1];
36002
36003        return function(object) {
36004          if (object == null) {
36005            return false;
36006          }
36007          return object[key] === value && (value !== undefined || (key in toObject(object)));
36008        };
36009      }
36010      return function(object) {
36011        return baseIsMatch(object, matchData);
36012      };
36013    }
36014
36015    /**
36016     * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
36017     *
36018     * @private
36019     * @param {string} path The path of the property to get.
36020     * @param {*} srcValue The value to compare.
36021     * @returns {Function} Returns the new function.
36022     */
36023    function baseMatchesProperty(path, srcValue) {
36024      var isArr = isArray(path),
36025          isCommon = isKey(path) && isStrictComparable(srcValue),
36026          pathKey = (path + '');
36027
36028      path = toPath(path);
36029      return function(object) {
36030        if (object == null) {
36031          return false;
36032        }
36033        var key = pathKey;
36034        object = toObject(object);
36035        if ((isArr || !isCommon) && !(key in object)) {
36036          object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
36037          if (object == null) {
36038            return false;
36039          }
36040          key = last(path);
36041          object = toObject(object);
36042        }
36043        return object[key] === srcValue
36044          ? (srcValue !== undefined || (key in object))
36045          : baseIsEqual(srcValue, object[key], undefined, true);
36046      };
36047    }
36048
36049    /**
36050     * The base implementation of `_.merge` without support for argument juggling,
36051     * multiple sources, and `this` binding `customizer` functions.
36052     *
36053     * @private
36054     * @param {Object} object The destination object.
36055     * @param {Object} source The source object.
36056     * @param {Function} [customizer] The function to customize merged values.
36057     * @param {Array} [stackA=[]] Tracks traversed source objects.
36058     * @param {Array} [stackB=[]] Associates values with source counterparts.
36059     * @returns {Object} Returns `object`.
36060     */
36061    function baseMerge(object, source, customizer, stackA, stackB) {
36062      if (!isObject(object)) {
36063        return object;
36064      }
36065      var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
36066          props = isSrcArr ? undefined : keys(source);
36067
36068      arrayEach(props || source, function(srcValue, key) {
36069        if (props) {
36070          key = srcValue;
36071          srcValue = source[key];
36072        }
36073        if (isObjectLike(srcValue)) {
36074          stackA || (stackA = []);
36075          stackB || (stackB = []);
36076          baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
36077        }
36078        else {
36079          var value = object[key],
36080              result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
36081              isCommon = result === undefined;
36082
36083          if (isCommon) {
36084            result = srcValue;
36085          }
36086          if ((result !== undefined || (isSrcArr && !(key in object))) &&
36087              (isCommon || (result === result ? (result !== value) : (value === value)))) {
36088            object[key] = result;
36089          }
36090        }
36091      });
36092      return object;
36093    }
36094
36095    /**
36096     * A specialized version of `baseMerge` for arrays and objects which performs
36097     * deep merges and tracks traversed objects enabling objects with circular
36098     * references to be merged.
36099     *
36100     * @private
36101     * @param {Object} object The destination object.
36102     * @param {Object} source The source object.
36103     * @param {string} key The key of the value to merge.
36104     * @param {Function} mergeFunc The function to merge values.
36105     * @param {Function} [customizer] The function to customize merged values.
36106     * @param {Array} [stackA=[]] Tracks traversed source objects.
36107     * @param {Array} [stackB=[]] Associates values with source counterparts.
36108     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
36109     */
36110    function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
36111      var length = stackA.length,
36112          srcValue = source[key];
36113
36114      while (length--) {
36115        if (stackA[length] == srcValue) {
36116          object[key] = stackB[length];
36117          return;
36118        }
36119      }
36120      var value = object[key],
36121          result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
36122          isCommon = result === undefined;
36123
36124      if (isCommon) {
36125        result = srcValue;
36126        if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
36127          result = isArray(value)
36128            ? value
36129            : (isArrayLike(value) ? arrayCopy(value) : []);
36130        }
36131        else if (isPlainObject(srcValue) || isArguments(srcValue)) {
36132          result = isArguments(value)
36133            ? toPlainObject(value)
36134            : (isPlainObject(value) ? value : {});
36135        }
36136        else {
36137          isCommon = false;
36138        }
36139      }
36140      // Add the source value to the stack of traversed objects and associate
36141      // it with its merged value.
36142      stackA.push(srcValue);
36143      stackB.push(result);
36144
36145      if (isCommon) {
36146        // Recursively merge objects and arrays (susceptible to call stack limits).
36147        object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
36148      } else if (result === result ? (result !== value) : (value === value)) {
36149        object[key] = result;
36150      }
36151    }
36152
36153    /**
36154     * The base implementation of `_.property` without support for deep paths.
36155     *
36156     * @private
36157     * @param {string} key The key of the property to get.
36158     * @returns {Function} Returns the new function.
36159     */
36160    function baseProperty(key) {
36161      return function(object) {
36162        return object == null ? undefined : object[key];
36163      };
36164    }
36165
36166    /**
36167     * A specialized version of `baseProperty` which supports deep paths.
36168     *
36169     * @private
36170     * @param {Array|string} path The path of the property to get.
36171     * @returns {Function} Returns the new function.
36172     */
36173    function basePropertyDeep(path) {
36174      var pathKey = (path + '');
36175      path = toPath(path);
36176      return function(object) {
36177        return baseGet(object, path, pathKey);
36178      };
36179    }
36180
36181    /**
36182     * The base implementation of `_.pullAt` without support for individual
36183     * index arguments and capturing the removed elements.
36184     *
36185     * @private
36186     * @param {Array} array The array to modify.
36187     * @param {number[]} indexes The indexes of elements to remove.
36188     * @returns {Array} Returns `array`.
36189     */
36190    function basePullAt(array, indexes) {
36191      var length = array ? indexes.length : 0;
36192      while (length--) {
36193        var index = indexes[length];
36194        if (index != previous && isIndex(index)) {
36195          var previous = index;
36196          splice.call(array, index, 1);
36197        }
36198      }
36199      return array;
36200    }
36201
36202    /**
36203     * The base implementation of `_.random` without support for argument juggling
36204     * and returning floating-point numbers.
36205     *
36206     * @private
36207     * @param {number} min The minimum possible value.
36208     * @param {number} max The maximum possible value.
36209     * @returns {number} Returns the random number.
36210     */
36211    function baseRandom(min, max) {
36212      return min + nativeFloor(nativeRandom() * (max - min + 1));
36213    }
36214
36215    /**
36216     * The base implementation of `_.reduce` and `_.reduceRight` without support
36217     * for callback shorthands and `this` binding, which iterates over `collection`
36218     * using the provided `eachFunc`.
36219     *
36220     * @private
36221     * @param {Array|Object|string} collection The collection to iterate over.
36222     * @param {Function} iteratee The function invoked per iteration.
36223     * @param {*} accumulator The initial value.
36224     * @param {boolean} initFromCollection Specify using the first or last element
36225     *  of `collection` as the initial value.
36226     * @param {Function} eachFunc The function to iterate over `collection`.
36227     * @returns {*} Returns the accumulated value.
36228     */
36229    function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
36230      eachFunc(collection, function(value, index, collection) {
36231        accumulator = initFromCollection
36232          ? (initFromCollection = false, value)
36233          : iteratee(accumulator, value, index, collection);
36234      });
36235      return accumulator;
36236    }
36237
36238    /**
36239     * The base implementation of `setData` without support for hot loop detection.
36240     *
36241     * @private
36242     * @param {Function} func The function to associate metadata with.
36243     * @param {*} data The metadata.
36244     * @returns {Function} Returns `func`.
36245     */
36246    var baseSetData = !metaMap ? identity : function(func, data) {
36247      metaMap.set(func, data);
36248      return func;
36249    };
36250
36251    /**
36252     * The base implementation of `_.slice` without an iteratee call guard.
36253     *
36254     * @private
36255     * @param {Array} array The array to slice.
36256     * @param {number} [start=0] The start position.
36257     * @param {number} [end=array.length] The end position.
36258     * @returns {Array} Returns the slice of `array`.
36259     */
36260    function baseSlice(array, start, end) {
36261      var index = -1,
36262          length = array.length;
36263
36264      start = start == null ? 0 : (+start || 0);
36265      if (start < 0) {
36266        start = -start > length ? 0 : (length + start);
36267      }
36268      end = (end === undefined || end > length) ? length : (+end || 0);
36269      if (end < 0) {
36270        end += length;
36271      }
36272      length = start > end ? 0 : ((end - start) >>> 0);
36273      start >>>= 0;
36274
36275      var result = Array(length);
36276      while (++index < length) {
36277        result[index] = array[index + start];
36278      }
36279      return result;
36280    }
36281
36282    /**
36283     * The base implementation of `_.some` without support for callback shorthands
36284     * and `this` binding.
36285     *
36286     * @private
36287     * @param {Array|Object|string} collection The collection to iterate over.
36288     * @param {Function} predicate The function invoked per iteration.
36289     * @returns {boolean} Returns `true` if any element passes the predicate check,
36290     *  else `false`.
36291     */
36292    function baseSome(collection, predicate) {
36293      var result;
36294
36295      baseEach(collection, function(value, index, collection) {
36296        result = predicate(value, index, collection);
36297        return !result;
36298      });
36299      return !!result;
36300    }
36301
36302    /**
36303     * The base implementation of `_.sortBy` which uses `comparer` to define
36304     * the sort order of `array` and replaces criteria objects with their
36305     * corresponding values.
36306     *
36307     * @private
36308     * @param {Array} array The array to sort.
36309     * @param {Function} comparer The function to define sort order.
36310     * @returns {Array} Returns `array`.
36311     */
36312    function baseSortBy(array, comparer) {
36313      var length = array.length;
36314
36315      array.sort(comparer);
36316      while (length--) {
36317        array[length] = array[length].value;
36318      }
36319      return array;
36320    }
36321
36322    /**
36323     * The base implementation of `_.sortByOrder` without param guards.
36324     *
36325     * @private
36326     * @param {Array|Object|string} collection The collection to iterate over.
36327     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
36328     * @param {boolean[]} orders The sort orders of `iteratees`.
36329     * @returns {Array} Returns the new sorted array.
36330     */
36331    function baseSortByOrder(collection, iteratees, orders) {
36332      var callback = getCallback(),
36333          index = -1;
36334
36335      iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); });
36336
36337      var result = baseMap(collection, function(value) {
36338        var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); });
36339        return { 'criteria': criteria, 'index': ++index, 'value': value };
36340      });
36341
36342      return baseSortBy(result, function(object, other) {
36343        return compareMultiple(object, other, orders);
36344      });
36345    }
36346
36347    /**
36348     * The base implementation of `_.sum` without support for callback shorthands
36349     * and `this` binding.
36350     *
36351     * @private
36352     * @param {Array|Object|string} collection The collection to iterate over.
36353     * @param {Function} iteratee The function invoked per iteration.
36354     * @returns {number} Returns the sum.
36355     */
36356    function baseSum(collection, iteratee) {
36357      var result = 0;
36358      baseEach(collection, function(value, index, collection) {
36359        result += +iteratee(value, index, collection) || 0;
36360      });
36361      return result;
36362    }
36363
36364    /**
36365     * The base implementation of `_.uniq` without support for callback shorthands
36366     * and `this` binding.
36367     *
36368     * @private
36369     * @param {Array} array The array to inspect.
36370     * @param {Function} [iteratee] The function invoked per iteration.
36371     * @returns {Array} Returns the new duplicate-value-free array.
36372     */
36373    function baseUniq(array, iteratee) {
36374      var index = -1,
36375          indexOf = getIndexOf(),
36376          length = array.length,
36377          isCommon = indexOf == baseIndexOf,
36378          isLarge = isCommon && length >= LARGE_ARRAY_SIZE,
36379          seen = isLarge ? createCache() : null,
36380          result = [];
36381
36382      if (seen) {
36383        indexOf = cacheIndexOf;
36384        isCommon = false;
36385      } else {
36386        isLarge = false;
36387        seen = iteratee ? [] : result;
36388      }
36389      outer:
36390      while (++index < length) {
36391        var value = array[index],
36392            computed = iteratee ? iteratee(value, index, array) : value;
36393
36394        if (isCommon && value === value) {
36395          var seenIndex = seen.length;
36396          while (seenIndex--) {
36397            if (seen[seenIndex] === computed) {
36398              continue outer;
36399            }
36400          }
36401          if (iteratee) {
36402            seen.push(computed);
36403          }
36404          result.push(value);
36405        }
36406        else if (indexOf(seen, computed, 0) < 0) {
36407          if (iteratee || isLarge) {
36408            seen.push(computed);
36409          }
36410          result.push(value);
36411        }
36412      }
36413      return result;
36414    }
36415
36416    /**
36417     * The base implementation of `_.values` and `_.valuesIn` which creates an
36418     * array of `object` property values corresponding to the property names
36419     * of `props`.
36420     *
36421     * @private
36422     * @param {Object} object The object to query.
36423     * @param {Array} props The property names to get values for.
36424     * @returns {Object} Returns the array of property values.
36425     */
36426    function baseValues(object, props) {
36427      var index = -1,
36428          length = props.length,
36429          result = Array(length);
36430
36431      while (++index < length) {
36432        result[index] = object[props[index]];
36433      }
36434      return result;
36435    }
36436
36437    /**
36438     * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`,
36439     * and `_.takeWhile` without support for callback shorthands and `this` binding.
36440     *
36441     * @private
36442     * @param {Array} array The array to query.
36443     * @param {Function} predicate The function invoked per iteration.
36444     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
36445     * @param {boolean} [fromRight] Specify iterating from right to left.
36446     * @returns {Array} Returns the slice of `array`.
36447     */
36448    function baseWhile(array, predicate, isDrop, fromRight) {
36449      var length = array.length,
36450          index = fromRight ? length : -1;
36451
36452      while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {}
36453      return isDrop
36454        ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
36455        : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
36456    }
36457
36458    /**
36459     * The base implementation of `wrapperValue` which returns the result of
36460     * performing a sequence of actions on the unwrapped `value`, where each
36461     * successive action is supplied the return value of the previous.
36462     *
36463     * @private
36464     * @param {*} value The unwrapped value.
36465     * @param {Array} actions Actions to peform to resolve the unwrapped value.
36466     * @returns {*} Returns the resolved value.
36467     */
36468    function baseWrapperValue(value, actions) {
36469      var result = value;
36470      if (result instanceof LazyWrapper) {
36471        result = result.value();
36472      }
36473      var index = -1,
36474          length = actions.length;
36475
36476      while (++index < length) {
36477        var action = actions[index];
36478        result = action.func.apply(action.thisArg, arrayPush([result], action.args
36478));
36479      }
36480      return result;
36481    }
36482
36483    /**
36484     * Performs a binary search of `array` to determine the index at which `value`
36485     * should be inserted into `array` in order to maintain its sort order.
36486     *
36487     * @private
36488     * @param {Array} array The sorted array to inspect.
36489     * @param {*} value The value to evaluate.
36490     * @param {boolean} [retHighest] Specify returning the highest qualified index.
36491     * @returns {number} Returns the index at which `value` should be inserted
36492     *  into `array`.
36493     */
36494    function binaryIndex(array, value, retHighest) {
36495      var low = 0,
36496          high = array ? array.length : low;
36497
36498      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
36499        while (low < high) {
36500          var mid = (low + high) >>> 1,
36501              computed = array[mid];
36502
36503          if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
36504            low = mid + 1;
36505          } else {
36506            high = mid;
36507          }
36508        }
36509        return high;
36510      }
36511      return binaryIndexBy(array, value, identity, retHighest);
36512    }
36513
36514    /**
36515     * This function is like `binaryIndex` except that it invokes `iteratee` for
36516     * `value` and each element of `array` to compute their sort ranking. The
36517     * iteratee is invoked with one argument; (value).
36518     *
36519     * @private
36520     * @param {Array} array The sorted array to inspect.
36521     * @param {*} value The value to evaluate.
36522     * @param {Function} iteratee The function invoked per iteration.
36523     * @param {boolean} [retHighest] Specify returning the highest qualified index.
36524     * @returns {number} Returns the index at which `value` should be inserted
36525     *  into `array`.
36526     */
36527    function binaryIndexBy(array, value, iteratee, retHighest) {
36528      value = iteratee(value);
36529
36530      var low = 0,
36531          high = array ? array.length : 0,
36532          valIsNaN = value !== value,
36533          valIsNull = value === null,
36534          valIsUndef = value === undefined;
36535
36536      while (low < high) {
36537        var mid = nativeFloor((low + high) / 2),
36538            computed = iteratee(array[mid]),
36539            isDef = computed !== undefined,
36540            isReflexive = computed === computed;
36541
36542        if (valIsNaN) {
36543          var setLow = isReflexive || retHighest;
36544        } else if (valIsNull) {
36545          setLow = isReflexive && isDef && (retHighest || computed != null);
36546        } else if (valIsUndef) {
36547          setLow = isReflexive && (retHighest || isDef);
36548        } else if (computed == null) {
36549          setLow = false;
36550        } else {
36551          setLow = retHighest ? (computed <= value) : (computed < value);
36552        }
36553        if (setLow) {
36554          low = mid + 1;
36555        } else {
36556          high = mid;
36557        }
36558      }
36559      return nativeMin(high, MAX_ARRAY_INDEX);
36560    }
36561
36562    /**
36563     * A specialized version of `baseCallback` which only supports `this` binding
36564     * and specifying the number of arguments to provide to `func`.
36565     *
36566     * @private
36567     * @param {Function} func The function to bind.
36568     * @param {*} thisArg The `this` binding of `func`.
36569     * @param {number} [argCount] The number of arguments to provide to `func`.
36570     * @returns {Function} Returns the callback.
36571     */
36572    function bindCallback(func, thisArg, argCount) {
36573      if (typeof func != 'function') {
36574        return identity;
36575      }
36576      if (thisArg === undefined) {
36577        return func;
36578      }
36579      switch (argCount) {
36580        case 1: return function(value) {
36581          return func.call(thisArg, value);
36582        };
36583        case 3: return function(value, index, collection) {
36584          return func.call(thisArg, value, index, collection);
36585        };
36586        case 4: return function(accumulator, value, index, collection) {
36587          return func.call(thisArg, accumulator, value, index, collection);
36588        };
36589        case 5: return function(value, other, key, object, source) {
36590          return func.call(thisArg, value, other, key, object, source);
36591        };
36592      }
36593      return function() {
36594        return func.apply(thisArg, arguments);
36595      };
36596    }
36597
36598    /**
36599     * Creates a clone of the given array buffer.
36600     *
36601     * @private
36602     * @param {ArrayBuffer} buffer The array buffer to clone.
36603     * @returns {ArrayBuffer} Returns the cloned array buffer.
36604     */
36605    function bufferClone(buffer) {
36606      var result = new ArrayBuffer(buffer.byteLength),
36607          view = new Uint8Array(result);
36608
36609      view.set(new Uint8Array(buffer));
36610      return result;
36611    }
36612
36613    /**
36614     * Creates an array that is the composition of partially applied arguments,
36615     * placeholders, and provided arguments into a single array of arguments.
36616     *
36617     * @private
36618     * @param {Array|Object} args The provided arguments.
36619     * @param {Array}
36619 partials The arguments to prepend to those provided.
36620     * @param {Array} holders The `partials` placeholder indexes.
36621     * @returns {Array} Returns the new array of composed arguments.
36622     */
36623    function composeArgs(args, partials, holders) {
36624      var holdersLength = holders.length,
36625          argsIndex = -1,
36626          argsLength = nativeMax(args.length - holdersLength, 0),
36627          leftIndex = -1,
36628          leftLength = partials.length,
36629          result = Array(leftLength + argsLength);
36630
36631      while (++leftIndex < leftLength) {
36632        result[leftIndex] = partials[leftIndex];
36633      }
36634      while (++argsIndex < holdersLength) {
36635        result[holders[argsIndex]] = args[argsIndex];
36636      }
36637      while (argsLength--) {
36638        result[leftIndex++] = args[argsIndex++];
36639      }
36640      return result;
36641    }
36642
36643    /**
36644     * This function is like `composeArgs` except that the arguments composition
36645     * is tailored for `_.partialRight`.
36646     *
36647     * @private
36648     * @param {Array|Object} args The provided arguments.
36649     * @param {Array} partials The arguments to append to those provided.
36650     * @param {Array} holders The `partials` placeholder indexes.
36651     * @returns {Array} Returns the new array of composed arguments.
36652     */
36653    function composeArgsRight(args, partials, holders) {
36654      var holdersIndex = -1,
36655          holdersLength = holders.length,
36656          argsIndex = -1,
36657          argsLength = nativeMax(args.length - holdersLength, 0),
36658          rightIndex = -1,
36659          rightLength = partials.length,
36660          result = Array(argsLength + rightLength);
36661
36662      while (++argsIndex < argsLength) {
36663        result[argsIndex] = args[argsIndex];
36664      }
36665      var offset = argsIndex;
36666      while (++rightIndex < rightLength) {
36667        result[offset + rightIndex] = partials[rightIndex];
36668      }
36669      while (++holdersIndex < holdersLength) {
36670        result[offset + holders[holdersIndex]] = args[argsIndex++];
36671      }
36672      return result;
36673    }
36674
36675    /**
36676     * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function.
36677     *
36678     * @private
36679     * @param {Function} setter The function to set keys and values of the accumulator object.
36680     * @param {Function} [initializer] The function to initialize the accumulator object.
36681     * @returns {Function} Returns the new aggregator function.
36682     */
36683    function createAggregator(setter, initializer) {
36684      return function(collection, iteratee, thisArg) {
36685        var result = initializer ? initializer() : {};
36686        iteratee = getCallback(iteratee, thisArg, 3);
36687
36688        if (isArray(collection)) {
36689          var index = -1,
36690              length = collection.length;
36691
36692          while (++index < length) {
36693            var value = collection[index];
36694            setter(result, value, iteratee(value, index, collection), collection);
36695          }
36696        } else {
36697          baseEach(collection, function(value, key, collection) {
36698            setter(result, value, iteratee(value, key, collection), collection);
36699          });
36700        }
36701        return result;
36702      };
36703    }
36704
36705    /**
36706     * Creates a `_.assign`, `_.defaults`, or `_.merge` function.
36707     *
36708     * @private
36709     * @param {Function} assigner The function to assign values.
36710     * @returns {Function} Returns the new assigner function.
36711     */
36712    function createAssigner(assigner) {
36713      return restParam(function(object, sources) {
36714        var index = -1,
36715            length = object == null ? 0 : sources.length,
36716            customizer = length > 2 ? sources[length - 2] : undefined,
36717            guard = length > 2 ? sources[2] : undefined,
36718            thisArg = length > 1 ? sources[length - 1] : undefined;
36719
36720        if (typeof customizer == 'function') {
36721          customizer = bindCallback(customizer, thisArg, 5);
36722          length -= 2;
36723        } else {
36724          customizer = typeof thisArg == 'function' ? thisArg : undefined;
36725          length -= (customizer ? 1 : 0);
36726        }
36727        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
36728          customizer = length < 3 ? undefined : customizer;
36729          length = 1;
36730        }
36731        while (++index < length) {
36732          var source = sources[index];
36733          if (source) {
36734            assigner(object, source, customizer);
36735          }
36736        }
36737        return object;
36738      });
36739    }
36740
36741    /**
36742     * Creates a `baseEach` or `baseEachRight` function.
36743     *
36744     * @private
36745     * @param {Function} eachFunc The function to iterate over a collection.
36746     * @param {boolean} [fromRight] Specify iterating from right to left.
36747     * @returns {Function} Returns the new base function.
36748     */
36749    function createBaseEach(eachFunc, fromRight) {
36750      return function(collection, iteratee) {
36751        var length = collection ? getLength(collection) : 0;
36752        if (!isLength(length)) {
36753          return eachFunc(collection, iteratee);
36754        }
36755        var index = fromRight ? length : -1,
36756            iterable = toObject(collection);
36757
36758        while ((fromRight ? index-- : ++index < length)) {
36759          if (iteratee(iterable[index], index, iterable) === false) {
36760            break;
36761          }
36762        }
36763        return collection;
36764      };
36765    }
36766
36767    /**
36768     * Creates a base function for `_.forIn` or `_.forInRight`.
36769     *
36770     * @private
36771     * @param {boolean} [fromRight] Specify iterating from right to left.
36772     * @returns {Function} Returns the new base function.
36773     */
36774    function createBaseFor(fromRight) {
36775      return function(object, iteratee, keysFunc) {
36776        var iterable = toObject(object),
36777            props = keysFunc(object),
36778            length = props.length,
36779            index = fromRight ? length : -1;
36780
36781        while ((fromRight ? index-- : ++index < length)) {
36782          var key = props[index];
36783          if (iteratee(iterable[key], key, iterable) === false) {
36784            break;
36785          }
36786        }
36787        return object;
36788      };
36789    }
36790
36791    /**
36792     * Creates a function that wraps `func` and invokes it with the `this`
36793     * binding of `thisArg`.
36794     *
36795     * @private
36796     * @param {Function} func The function to bind.
36797     * @param {*} [thisArg] The `this` binding of `func`.
36798     * @returns {Function} Returns the new bound function.
36799     */
36800    function createBindWrapper(func, thisArg) {
36801      var Ctor = createCtorWrapper(func);
36802
36803      function wrapper() {
36804        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
36805        return fn.apply(thisArg, arguments);
36806      }
36807      return wrapper;
36808    }
36809
36810    /**
36811     * Creates a `Set` cache object to optimize linear searches of large arrays.
36812     *
36813     * @private
36814     * @param {Array} [values] The values to cache.
36815     * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
36816     */
36817    function createCache(values) {
36818      return (nativeCreate && Set) ? new SetCache(values) : null;
36819    }
36820
36821    /**
36822     * Creates a function that produces compound words out of the words in a
36823     * given string.
36824     *
36825     * @private
36826     * @param {Function} callback The function to combine each word.
36827     * @returns {Function} Returns the new compounder function.
36828     */
36829    function createCompounder(callback) {
36830      return function(string) {
36831        var index = -1,
36832            array = words(deburr(string)),
36833            length = array.length,
36834            result = '';
36835
36836        while (++index < length) {
36837          result = callback(result, array[index], index);
36838        }
36839        return result;
36840      };
36841    }
36842
36843    /**
36844     * Creates a function that produces an instance of `Ctor` regardless of
36845     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
36846     *
36847     * @private
36848     * @param {Function} Ctor The constructor to wrap.
36849     * @returns {Function} Returns the new wrapped function.
36850     */
36851    function createCtorWrapper(Ctor) {
36852      return function() {
36853        // Use a `switch` statement to work with class constructors.
36854        // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
36855        // for more details.
36856        var args = arguments;
36857        switch (args.length) {
36858          case 0: return new Ctor;
36859          case 1: return new Ctor(args[0]);
36860          case 2: return new Ctor(args[0], args[1]);
36861          case 3: return new Ctor(args[0], args[1], args[2]);
36862          case 4: return new Ctor(args[0], args[1], args[2], args[3]);
36863          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
36864          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
36865          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
36866        }
36867        var thisBinding = baseCreate(Ctor.prototype),
36868            result = Ctor.apply(thisBinding, args);
36869
36870        // Mimic the constructor's `return` behavior.
36871        // See https://es5.github.io/#x13.2.2 for more details.
36872        return isObject(result) ? result : thisBinding;
36873      };
36874    }
36875
36876    /**
36877     * Creates a `_.curry` or `_.curryRight` function.
36878     *
36879     * @private
36880     * @param {boolean} flag The curry bit flag.
36881     * @returns {Function} Returns the new curry function.
36882     */
36883    function createCurry(flag) {
36884      function curryFunc(func, arity, guard) {
36885        if (guard && isIterateeCall(func, arity, guard)) {
36886          arity = undefined;
36887        }
36888        var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity);
36889        result.placeholder = curryFunc.placeholder;
36890        return result;
36891      }
36892      return curryFunc;
36893    }
36894
36895    /**
36896     * Creates a `_.defaults` or `_.defaultsDeep` function.
36897     *
36898     * @private
36899     * @param {Function} assigner The function to assign values.
36900     * @param {Function} customizer The function to customize assigned values.
36901     * @returns {Function} Returns the new defaults function.
36902     */
36903    function createDefaults(assigner, customizer) {
36904      return restParam(function(args) {
36905        var object = args[0];
36906        if (object == null) {
36907          return object;
36908        }
36909        args.push(customizer);
36910        return assigner.apply(undefined, args);
36911      });
36912    }
36913
36914    /**
36915     * Creates a `_.max` or `_.min` function.
36916     *
36917     * @private
36918     * @param {Function} comparator The function used to compare values.
36919     * @param {*} exValue The initial extremum value.
36920     * @returns {Function} Returns the new extremum function.
36921     */
36922    function createExtremum(comparator, exValue) {
36923      return function(collection, iteratee, thisArg) {
36924        if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
36925          iteratee = undefined;
36926        }
36927        iteratee = getCallback(iteratee, thisArg, 3);
36928        if (iteratee.length == 1) {
36929          collection = isArray(collection) ? collection : toIterable(collection);
36930          var result = arrayExtremum(collection, iteratee, comparator, exValue);
36931          if (!(collection.length && result === exValue)) {
36932            return result;
36933          }
36934        }
36935        return baseExtremum(collection, iteratee, comparator, exValue);
36936      };
36937    }
36938
36939    /**
36940     * Creates a `_.find` or `_.findLast` function.
36941     *
36942     * @private
36943     * @param {Function} eachFunc The function to iterate over a collection.
36944     * @param {boolean} [fromRight] Specify iterating from right to left.
36945     * @returns {Function} Returns the new find function.
36946     */
36947    function createFind(eachFunc, fromRight) {
36948      return function(collection, predicate, thisArg) {
36949        predicate = getCallback(predicate, thisArg, 3);
36950        if (isArray(collection)) {
36951          var index = baseFindIndex(collection, predicate, fromRight);
36952          return index > -1 ? collection[index] : undefined;
36953        }
36954        return baseFind(collection, predicate, eachFunc);
36955      };
36956    }
36957
36958    /**
36959     * Creates a `_.findIndex` or `_.findLastIndex` function.
36960     *
36961     * @private
36962     * @param {boolean} [fromRight] Specify iterating from right to left.
36963     * @returns {Function} Returns the new find function.
36964     */
36965    function createFindIndex(fromRight) {
36966      return function(array, predicate, thisArg) {
36967        if (!(array && array.length)) {
36968          return -1;
36969        }
36970        predicate = getCallback(predicate, thisArg, 3);
36971        return baseFindIndex(array, predicate, fromRight);
36972      };
36973    }
36974
36975    /**
36976     * Creates a `_.findKey` or `_.findLastKey` function.
36977     *
36978     * @private
36979     * @param {Function} objectFunc The function to iterate over an object.
36980     * @returns {Function} Returns the new find function.
36981     */
36982    function createFindKey(objectFunc) {
36983      return function(object, predicate, thisArg) {
36984        predicate = getCallback(predicate, thisArg, 3);
36985        return baseFind(object, predicate, objectFunc, true);
36986      };
36987    }
36988
36989    /**
36990     * Creates a `_.flow` or `_.flowRight` function.
36991     *
36992     * @private
36993     * @param {boolean} [fromRight] Specify iterating from right to left.
36994     * @returns {Function} Returns the new flow function.
36995     */
36996    function createFlow(fromRight) {
36997      return function() {
36998        var wrapper,
36999            length = arguments.length,
37000            index = fromRight ? length : -1,
37001            leftIndex = 0,
37002            funcs = Array(length);
37003
37004        while ((fromRight ? index-- : ++index < length)) {
37005          var func = funcs[leftIndex++] = arguments[index];
37006          if (typeof func != 'function') {
37007            throw new TypeError(FUNC_ERROR_TEXT);
37008          }
37009          if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') {
37010            wrapper = new LodashWrapper([], true);
37011          }
37012        }
37013        index = wrapper ? -1 : length;
37014        while (++index < length) {
37015          func = funcs[index];
37016
37017          var funcName = getFuncName(func),
37018              data = funcName == 'wrapper' ? getData(func) : undefined;
37019
37020          if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) {
37021            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
37022          } else {
37023            wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func);
37024          }
37025        }
37026        return function() {
37027          var args = arguments,
37028              value = args[0];
37029
37030          if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) {
37031            return wrapper.plant(value).value();
37032          }
37033          var index = 0,
37034              result = length ? funcs[index].apply(this, args) : value;
37035
37036          while (++index < length) {
37037            result = funcs[index].call(this, result);
37038          }
37039          return result;
37040        };
37041      };
37042    }
37043
37044    /**
37045     * Creates a function for `_.forEach` or `_.forEachRight`.
37046     *
37047     * @private
37048     * @param {Function} arrayFunc The function to iterate over an array.
37049     * @param {Function} eachFunc The function to iterate over a collection.
37050     * @returns {Function} Returns the new each function.
37051     */
37052    function createForEach(arrayFunc, eachFunc) {
37053      return function(collection, iteratee, thisArg) {
37054        return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
37055          ? arrayFunc(collection, iteratee)
37056          : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
37057      };
37058    }
37059
37060    /**
37061     * Creates a function for `_.forIn` or `_.forInRight`.
37062     *
37063     * @private
37064     * @param {Function} objectFunc The function to iterate over an object.
37065     * @returns {Function} Returns the new each function.
37066     */
37067    function createForIn(objectFunc) {
37068      return function(object, iteratee, thisArg) {
37069        if (typeof iteratee != 'function' || thisArg !== undefined) {
37070          iteratee = bindCallback(iteratee, thisArg, 3);
37071        }
37072        return objectFunc(object, iteratee, keysIn);
37073      };
37074    }
37075
37076    /**
37077     * Creates a function for `_.forOwn` or `_.forOwnRight`.
37078     *
37079     * @private
37080     * @param {Function} objectFunc The function to iterate over an object.
37081     * @returns {Function} Returns the new each function.
37082     */
37083    function createForOwn(objectFunc) {
37084      return function(object, iteratee, thisArg) {
37085        if (typeof iteratee != 'function' || thisArg !== undefined) {
37086          iteratee = bindCallback(iteratee, thisArg, 3);
37087        }
37088        return objectFunc(object, iteratee);
37089      };
37090    }
37091
37092    /**
37093     * Creates a function for `_.mapKeys` or `_.mapValues`.
37094     *
37095     * @private
37096     * @param {boolean} [isMapKeys] Specify mapping keys instead of values.
37097     * @returns {Function} Returns the new map function.
37098     */
37099    function createObjectMapper(isMapKeys) {
37100      return function(object, iteratee, thisArg) {
37101        var result = {};
37102        iteratee = getCallback(iteratee, thisArg, 3);
37103
37104        baseForOwn(object, function(value, key, object) {
37105          var mapped = iteratee(value, key, object);
37106          key = isMapKeys ? mapped : key;
37107          value = isMapKeys ? value : mapped;
37108          result[key] = value;
37109        });
37110        return result;
37111      };
37112    }
37113
37114    /**
37115     * Creates a function for `_.padLeft` or `_.padRight`.
37116     *
37117     * @private
37118     * @param {boolean} [fromRight] Specify padding from the right.
37119     * @returns {Function} Returns the new pad function.
37120     */
37121    function createPadDir(fromRight) {
37122      return function(string, length, chars) {
37123        string = baseToString(string);
37124        return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string);
37125      };
37126    }
37127
37128    /**
37129     * Creates a `_.partial` or `_.partialRight` function.
37130     *
37131     * @private
37132     * @param {boolean} flag The partial bit flag.
37133     * @returns {Function} Returns the new partial function.
37134     */
37135    function createPartial(flag) {
37136      var partialFunc = restParam(function(func, partials) {
37137        var holders = replaceHolders(partials, partialFunc.placeholder);
37138        return createWrapper(func, flag, undefined, partials, holders);
37139      });
37140      return partialFunc;
37141    }
37142
37143    /**
37144     * Creates a function for `_.reduce` or `_.reduceRight`.
37145     *
37146     * @private
37147     * @param {Function} arrayFunc The function to iterate over an array.
37148     * @param {Function} eachFunc The function to iterate over a collection.
37149     * @returns {Function} Returns the new each function.
37150     */
37151    function createReduce(arrayFunc, eachFunc) {
37152      return function(collection, iteratee, accumulator, thisArg) {
37153        var initFromArray = arguments.length < 3;
37154        return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
37155          ? arrayFunc(collection, iteratee, accumulator, initFromArray)
37156          : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
37157      };
37158    }
37159
37160    /**
37161     * Creates a function that wraps `func` and invokes it with optional `this`
37162     * binding of, partial application, and currying.
37163     *
37164     * @private
37165     * @param {Function|string} func The function or method name to reference.
37166     * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
37167     * @param {*} [thisArg] The `this` binding of `func`.
37168     * @param {Array} [partials] The arguments to prepend to those provided to the new function.
37169     * @param {Array} [holders] The `partials` placeholder indexes.
37170     * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
37171     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
37172     * @param {Array} [argPos] The argument positions of the new function.
37173     * @param {number} [ary] The arity cap of `func`.
37174     * @param {number} [arity] The arity of `func`.
37175     * @returns {Function} Returns the new wrapped function.
37176     */
37177    function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
37178      var isAry = bitmask & ARY_FLAG,
37179          isBind = bitmask & BIND_FLAG,
37180          isBindKey = bitmask & BIND_KEY_FLAG,
37181          isCurry = bitmask & CURRY_FLAG,
37182          isCurryBound = bitmask & CURRY_BOUND_FLAG,
37183          isCurryRight = bitmask & CURRY_RIGHT_FLAG,
37184          Ctor = isBindKey ? undefined : createCtorWrapper(func);
37185
37186      function wrapper() {
37187        // Avoid `arguments` object use disqualifying optimizations by
37188        // converting it to an array before providing it to other functions.
37189        var length = arguments.length,
37190            index = length,
37191            args = Array(length);
37192
37193        while (index--) {
37194          args[index] = arguments[index];
37195        }
37196        if (partials) {
37197          args = composeArgs(args, partials, holders);
37198        }
37199        if (partialsRight) {
37200          args = composeArgsRight(args, partialsRight, holdersRight);
37201        }
37202        if (isCurry || isCurryRight) {
37203          var placeholder = wrapper.placeholder,
37204              argsHolders = replaceHolders(args, placeholder);
37205
37206          length -= argsHolders.length;
37207          if (length < arity) {
37208            var newArgPos = argPos ? arrayCopy(argPos) : undefined,
37209                newArity = nativeMax(arity - length, 0),
37210                newsHolders = isCurry ? argsHolders : undefined,
37211                newHoldersRight = isCurry ? undefined : argsHolders,
37212                newPartials = isCurry ? args : undefined,
37213                newPartialsRight = isCurry ? undefined : args;
37214
37215            bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
37216            bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
37217
37218            if (!isCurryBound) {
37219              bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
37220            }
37221            var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
37222                result = createHybridWrapper.apply(undefined, newData);
37223
37224            if (isLaziable(func)) {
37225              setData(result, newData);
37226            }
37227            result.placeholder = placeholder;
37228            return result;
37229          }
37230        }
37231        var thisBinding = isBind ? thisArg : this,
37232            fn = isBindKey ? thisBinding[func] : func;
37233
37234        if (argPos) {
37235          args = reorder(args, argPos);
37236        }
37237        if (isAry && ary < args.length) {
37238          args.length = ary;
37239        }
37240        if (this && this !== root && this instanceof wrapper) {
37241          fn = Ctor || createCtorWrapper(func);
37242        }
37243        return fn.apply(thisBinding, args);
37244      }
37245      return wrapper;
37246    }
37247
37248    /**
37249     * Creates the padding required for `string` based on the given `length`.
37250     * The `chars` string is truncated if the number of characters exceeds `length`.
37251     *
37252     * @private
37253     * @param {string} string The string to create padding for.
37254     * @param {number} [length=0] The padding length.
37255     * @param {string} [chars=' '] The string used as padding.
37256     * @returns {string} Returns the pad for `string`.
37257     */
37258    function createPadding(string, length, chars) {
37259      var strLength = string.length;
37260      length = +length;
37261
37262      if (strLength >= length || !nativeIsFinite(length)) {
37263        return '';
37264      }
37265      var padLength = length - strLength;
37266      chars = chars == null ? ' ' : (chars + '');
37267      return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength);
37268    }
37269
37270    /**
37271     * Creates a function that wraps `func` and invokes it with the optional `this`
37272     * binding of `thisArg` and the `partials` prepended to those provided to
37273     * the wrapper.
37274     *
37275     * @private
37276     * @param {Function} func The function to partially apply arguments to.
37277     * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
37278     * @param {*} thisArg The `this` binding of `func`.
37279     * @param {Array} partials The arguments to prepend to those provided to the new function.
37280     * @returns {Function} Returns the new bound function.
37281     */
37282    function createPartialWrapper(func, bitmask, thisArg, partials) {
37283      var isBind = bitmask & BIND_FLAG,
37284          Ctor = createCtorWrapper(func);
37285
37286      function wrapper() {
37287        // Avoid `arguments` object use disqualifying optimizations by
37288        // converting it to an array before providing it `func`.
37289        var argsIndex = -1,
37290            argsLength = arguments.length,
37291            leftIndex = -1,
37292            leftLength = partials.length,
37293            args = Array(leftLength + argsLength);
37294
37295        while (++leftIndex < leftLength) {
37296          args[leftIndex] = partials[leftIndex];
37297        }
37298        while (argsLength--) {
37299          args[leftIndex++] = arguments[++argsIndex];
37300        }
37301        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
37302        return fn.apply(isBind ? thisArg : this, args);
37303      }
37304      return wrapper;
37305    }
37306
37307    /**
37308     * Creates a `_.ceil`, `_.floor`, or `_.round` function.
37309     *
37310     * @private
37311     * @param {string} methodName The name of the `Math` method to use when rounding.
37312     * @returns {Function} Returns the new round function.
37313     */
37314    function createRound(methodName) {
37315      var func = Math[methodName];
37316      return function(number, precision) {
37317        precision = precision === undefined ? 0 : (+precision || 0);
37318        if (precision) {
37319          precision = pow(10, precision);
37320          return func(number * precision) / precision;
37321        }
37322        return func(number);
37323      };
37324    }
37325
37326    /**
37327     * Creates a `_.sortedIndex` or `_.sortedLastIndex` function.
37328     *
37329     * @private
37330     * @param {boolean} [retHighest] Specify returning the highest qualified index.
37331     * @returns {Function} Returns the new index function.
37332     */
37333    function createSortedIndex(retHighest) {
37334      return function(array, value, iteratee, thisArg) {
37335        var callback = getCallback(iteratee);
37336        return (iteratee == null && callback === baseCallback)
37337          ? binaryIndex(array, value, retHighest)
37338          : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest);
37339      };
37340    }
37341
37342    /**
37343     * Creates a function that either curries or invokes `func` with optional
37344     * `this` binding and partially applied arguments.
37345     *
37346     * @private
37347     * @param {Function|string} func The function or method name to reference.
37348     * @param {number} bitmask The bitmask of flags.
37349     *  The bitmask may be composed of the following flags:
37350     *     1 - `_.bind`
37351     *     2 - `_.bindKey`
37352     *     4 - `_.curry` or `_.curryRight` of a bound function
37353     *     8 - `_.curry`
37354     *    16 - `_.curryRight`
37355     *    32 - `_.partial`
37356     *    64 - `_.partialRight`
37357     *   128 - `_.rearg`
37358     *   256 - `_.ary`
37359     * @param {*} [thisArg] The `this` binding of `func`.
37360     * @param {Array} [partials] The arguments to be partially applied.
37361     * @param {Array} [holders] The `partials` placeholder indexes.
37362     * @param {Array} [argPos] The argument positions of the new function.
37363     * @param {number} [ary] The arity cap of `func`.
37364     * @param {number} [arity] The arity of `func`.
37365     * @returns {Function} Returns the new wrapped function.
37366     */
37367    function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
37368      var isBindKey = bitmask & BIND_KEY_FLAG;
37369      if (!isBindKey && typeof func != 'function') {
37370        throw new TypeError(FUNC_ERROR_TEXT);
37371      }
37372      var length = partials ? partials.length : 0;
37373      if (!length) {
37374        bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
37375        partials = holders = undefined;
37376      }
37377      length -= (holders ? holders.length : 0);
37378      if (bitmask & PARTIAL_RIGHT_FLAG) {
37379        var partialsRight = partials,
37380            holdersRight = holders;
37381
37382        partials = holders = undefined;
37383      }
37384      var data = isBindKey ? undefined : getData(func),
37385          newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
37386
37387      if (data) {
37388        mergeData(newData, data);
37389        bitmask = newData[1];
37390        arity = newData[9];
37391      }
37392      newData[9] = arity == null
37393        ? (isBindKey ? 0 : func.length)
37394        : (nativeMax(arity - length, 0) || 0);
37395
37396      if (bitmask == BIND_FLAG) {
37397        var result = createBindWrapper(newData[0], newData[2]);
37398      } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
37399        result = createPartialWrapper.apply(undefined, newData);
37400      } else {
37401        result = createHybridWrapper.apply(undefined, newData);
37402      }
37403      var setter = data ? baseSetData : setData;
37404      return setter(result, newData);
37405    }
37406
37407    /**
37408     * A specialized version of `baseIsEqualDeep` for arrays with support for
37409     * partial deep comparisons.
37410     *
37411     * @private
37412     * @param {Array} array The array to compare.
37413     * @param {Array} other The other array to compare.
37414     * @param {Function} equalFunc The function to determine equivalents of values.
37415     * @param {Function} [customizer] The function to customize comparing arrays.
37416     * @param {boolean} [isLoose] Specify performing partial comparisons.
37417     * @param {Array} [stackA] Tracks traversed `value` objects.
37418     * @param {Array} [stackB] Tracks traversed `other` objects.
37419     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
37420     */
37421    function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
37422      var index = -1,
37423          arrLength = array.length,
37424          othLength = other.length;
37425
37426      if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
37427        return false;
37428      }
37429      // Ignore non-index properties.
37430      while (++index < arrLength) {
37431        var arrValue = array[index],
37432            othValue = other[index],
37433            result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
37434
37435        if (result !== undefined) {
37436          if (result) {
37437            continue;
37438          }
37439          return false;
37440        }
37441        // Recursively compare arrays (susceptible to call stack limits).
37442        if (isLoose) {
37443          if (!arraySome(other, function(othValue) {
37444                return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
37445              })) {
37446            return false;
37447          }
37448        } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
37449          return false;
37450        }
37451      }
37452      return true;
37453    }
37454
37455    /**
37456     * A specialized version of `baseIsEqualDeep` for comparing objects of
37457     * the same `toStringTag`.
37458     *
37459     * **Note:** This function only supports comparing values with tags of
37460     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
37461     *
37462     * @private
37463     * @param {Object} object The object to compare.
37464     * @param {Object} other The other object to compare.
37465     * @param {string} tag The `toStringTag` of the objects to compare.
37466     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
37467     */
37468    function equalByTag(object, other, tag) {
37469      switch (tag) {
37470        case boolTag:
37471        case dateTag:
37472          // Coerce dates and booleans to numbers, dates to milliseconds and booleans
37473          // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
37474          return +object == +other;
37475
37476        case errorTag:
37477          return object.name == other.name && object.message == other.message;
37478
37479        case numberTag:
37480          // Treat `NaN` vs. `NaN` as equal.
37481          return (object != +object)
37482            ? other != +other
37483            : object == +other;
37484
37485        case regexpTag:
37486        case stringTag:
37487          // Coerce regexes to strings and treat strings primitives and string
37488          // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
37489          return object == (other + '');
37490      }
37491      return false;
37492    }
37493
37494    /**
37495     * A specialized version of `baseIsEqualDeep` for objects with support for
37496     * partial deep comparisons.
37497     *
37498     * @private
37499     * @param {Object} object The object to compare.
37500     * @param {Object} other The other object to compare.
37501     * @param {Function} equalFunc The function to determine equivalents of values.
37502     * @param {Function} [customizer] The function to customize comparing values.
37503     * @param {boolean} [isLoose] Specify performing partial comparisons.
37504     * @param {Array} [stackA] Tracks traversed `value` objects.
37505     * @param {Array} [stackB] Tracks traversed `other` objects.
37506     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
37507     */
37508    function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
37509      var objProps = keys(object),
37510          objLength = objProps.length,
37511          othProps = keys(other),
37512          othLength = othProps.length;
37513
37514      if (objLength != othLength && !isLoose) {
37515        return false;
37516      }
37517      var index = objLength;
37518      while (index--) {
37519        var key = objProps[index];
37520        if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
37521          return false;
37522        }
37523      }
37524      var skipCtor = isLoose;
37525      while (++index < objLength) {
37526        key = objProps[index];
37527        var objValue = object[key],
37528            othValue = other[key],
37529            result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
37530
37531        // Recursively compare objects (susceptible to call stack limits).
37532        if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
37533          return false;
37534        }
37535        skipCtor || (skipCtor = key == 'constructor');
37536      }
37537      if (!skipCtor) {
37538        var objCtor = object.constructor,
37539            othCtor = other.constructor;
37540
37541        // Non `Object` object instances with different constructors are not equal.
37542        if (objCtor != othCtor &&
37543            ('constructor' in object && 'constructor' in other) &&
37544            !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
37545              typeof othCtor == 'function' && othCtor instanceof othCtor)) {
37546          return false;
37547        }
37548      }
37549      return true;
37550    }
37551
37552    /**
37553     * Gets the appropriate "callback" function. If the `_.callback` method is
37554     * customized this function returns the custom method, otherwise it returns
37555     * the `baseCallback` function. If arguments are provided the chosen function
37556     * is invoked with them and its result is returned.
37557     *
37558     * @private
37559     * @returns {Function} Returns the chosen function or its result.
37560     */
37561    function getCallback(func, thisArg, argCount) {
37562      var result = lodash.callback || callback;
37563      result = result === callback ? baseCallback : result;
37564      return argCount ? result(func, thisArg, argCount) : result;
37565    }
37566
37567    /**
37568     * Gets metadata for `func`.
37569     *
37570     * @private
37571     * @param {Function} func The function to query.
37572     * @returns {*} Returns the metadata for `func`.
37573     */
37574    var getData = !metaMap ? noop : function(func) {
37575      return metaMap.get(func);
37576    };
37577
37578    /**
37579     * Gets the name of `func`.
37580     *
37581     * @private
37582     * @param {Function} func The function to query.
37583     * @returns {string} Returns the function name.
37584     */
37585    function getFuncName(func) {
37586      var result = func.name,
37587          array = realNames[result],
37588          length = array ? array.length : 0;
37589
37590      while (length--) {
37591        var data = array[length],
37592            otherFunc = data.func;
37593        if (otherFunc == null || otherFunc == func) {
37594          return data.name;
37595        }
37596      }
37597      return result;
37598    }
37599
37600    /**
37601     * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
37602     * customized this function returns the custom method, otherwise it returns
37603     * the `baseIndexOf` function. If arguments are provided the chosen function
37604     * is invoked with them and its result is returned.
37605     *
37606     * @private
37607     * @returns {Function|number} Returns the chosen function or its result.
37608     */
37609    function getIndexOf(collection, target, fromIndex) {
37610      var result = lodash.indexOf || indexOf;
37611      result = result === indexOf ? baseIndexOf : result;
37612      return collection ? result(collection, target, fromIndex) : result;
37613    }
37614
37615    /**
37616     * Gets the "length" property value of `object`.
37617     *
37618     * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
37619     * that affects Safari on at least iOS 8.1-8.3 ARM64.
37620     *
37621     * @private
37622     * @param {Object} object The object to query.
37623     * @returns {*} Returns the "length" value.
37624     */
37625    var getLength = baseProperty('length');
37626
37627    /**
37628     * Gets the propery names, values, and compare flags of `object`.
37629     *
37630     * @private
37631     * @param {Object} object The object to query.
37632     * @returns {Array} Returns the match data of `object`.
37633     */
37634    function getMatchData(object) {
37635      var result = pairs(object),
37636          length = result.length;
37637
37638      while (length--) {
37639        result[length][2] = isStrictComparable(result[length][1]);
37640      }
37641      return result;
37642    }
37643
37644    /**
37645     * Gets the native function at `key` of `object`.
37646     *
37647     * @private
37648     * @param {Object} object The object to query.
37649     * @param {string} key The key of the method to get.
37650     * @returns {*} Returns the function if it's native, else `undefined`.
37651     */
37652    function getNative(object, key) {
37653      var value = object == null ? undefined : object[key];
37654      return isNative(value) ? value : undefined;
37655    }
37656
37657    /**
37658     * Gets the view, applying any `transforms` to the `start` and `end` positions.
37659     *
37660     * @private
37661     * @param {number} start The start of the view.
37662     * @param {number} end The end of the view.
37663     * @param {Array} transforms The transformations to apply to the view.
37664     * @returns {Object}
37664 Returns an object containing the `start` and `end`
37665     *  positions of the view.
37666     */
37667    function getView(start, end, transforms) {
37668      var index = -1,
37669          length = transforms.length;
37670
37671      while (++index < length) {
37672        var data = transforms[index],
37673            size = data.size;
37674
37675        switch (data.type) {
37676          case 'drop':      start += size; break;
37677          case 'dropRight': end -= size; break;
37678          case 'take':      end = nativeMin(end, start + size); break;
37679          case 'takeRight': start = nativeMax(start, end - size); break;
37680        }
37681      }
37682      return { 'start': start, 'end': end };
37683    }
37684
37685    /**
37686     * Initializes an array clone.
37687     *
37688     * @private
37689     * @param {Array} array The array to clone.
37690     * @returns {Array} Returns the initialized clone.
37691     */
37692    function initCloneArray(array) {
37693      var length = array.length,
37694          result = new array.constructor(length);
37695
37696      // Add array properties assigned by `RegExp#exec`.
37697      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
37698        result.index = array.index;
37699        result.input = array.input;
37700      }
37701      return result;
37702    }
37703
37704    /**
37705     * Initializes an object clone.
37706     *
37707     * @private
37708     * @param {Object} object The object to clone.
37709     * @returns {Object} Returns the initialized clone.
37710     */
37711    function initCloneObject(object) {
37712      var Ctor = object.constructor;
37713      if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
37714        Ctor = Object;
37715      }
37716      return new Ctor;
37717    }
37718
37719    /**
37720     * Initializes an object clone based on its `toStringTag`.
37721     *
37722     * **Note:** This function only supports cloning values with tags of
37723     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
37724     *
37725     * @private
37726     * @param {Object} object The object to clone.
37727     * @param {string} tag The `toStringTag` of the object to clone.
37728     * @param {boolean} [isDeep] Specify a deep clone.
37729     * @returns {Object} Returns the initialized clone.
37730     */
37731    function initCloneByTag(object, tag, isDeep) {
37732      var Ctor = object.constructor;
37733      switch (tag) {
37734        case arrayBufferTag:
37735          return bufferClone(object);
37736
37737        case boolTag:
37738        case dateTag:
37739          return new Ctor(+object);
37740
37741        case float32Tag: case float64Tag:
37742        case int8Tag: case int16Tag: case int32Tag:
37743        case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
37744          var buffer = object.buffer;
37745          return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
37746
37747        case numberTag:
37748        case stringTag:
37749          return new Ctor(object);
37750
37751        case regexpTag:
37752          var result = new Ctor(object.source, reFlags.exec(object));
37753          result.lastIndex = object.lastIndex;
37754      }
37755      return result;
37756    }
37757
37758    /**
37759     * Invokes the method at `path` on `object`.
37760     *
37761     * @private
37762     * @param {Object} object The object to query.
37763     * @param {Array|string} path The path of the method to invoke.
37764     * @param {Array} args The arguments to invoke the method with.
37765     * @returns {*} Returns the result of the invoked method.
37766     */
37767    function invokePath(object, path, args) {
37768      if (object != null && !isKey(path, object)) {
37769        path = toPath(path);
37770        object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
37771        path = last(path);
37772      }
37773      var func = object == null ? object : object[path];
37774      return func == null ? undefined : func.apply(object, args);
37775    }
37776
37777    /**
37778     * Checks if `value` is array-like.
37779     *
37780     * @private
37781     * @param {*} value The value to check.
37782     * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
37783     */
37784    function isArrayLike(value) {
37785      return value != null && isLength(getLength(value));
37786    }
37787
37788    /**
37789     * Checks if `value` is a valid array-like index.
37790     *
37791     * @private
37792     * @param {*} value The value to check.
37793     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
37794     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
37795     */
37796    function isIndex(value, length) {
37797      value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
37798      length = length == null ? MAX_SAFE_INTEGER : length;
37799      return value > -1 && value % 1 == 0 && value < length;
37800    }
37801
37802    /**
37803     * Checks if the provided arguments are from an iteratee call.
37804     *
37805     * @private
37806     * @param {*} value The potential iteratee value argument.
37807     * @param {*} index The potential iteratee index or key argument.
37808     * @param {*} object The potential iteratee object argument.
37809     * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
37810     */
37811    function isIterateeCall(value, index, object) {
37812      if (!isObject(object)) {
37813        return false;
37814      }
37815      var type = typeof index;
37816      if (type == 'number'
37817          ? (isArrayLike(object) && isIndex(index, object.length))
37818          : (type == 'string' && index in object)) {
37819        var other = object[index];
37820        return value === value ? (value === other) : (other !== other);
37821      }
37822      return false;
37823    }
37824
37825    /**
37826     * Checks if `value` is a property name and not a property path.
37827     *
37828     * @private
37829     * @param {*} value The value to check.
37830     * @param {Object} [object] The object to query keys on.
37831     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
37832     */
37833    function isKey(value, object) {
37834      var type = typeof value;
37835      if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
37836        return true;
37837      }
37838      if (isArray(value)) {
37839        return false;
37840      }
37841      var result = !reIsDeepProp.test(value);
37842      return result || (object != null && value in toObject(object));
37843    }
37844
37845    /**
37846     * Checks if `func` has a lazy counterpart.
37847     *
37848     * @private
37849     * @param {Function} func The function to check.
37850     * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
37851     */
37852    function isLaziable(func) {
37853      var funcName = getFuncName(func);
37854      if (!(funcName in LazyWrapper.prototype)) {
37855        return false;
37856      }
37857      var other = lodash[funcName];
37858      if (func === other) {
37859        return true;
37860      }
37861      var data = getData(other);
37862      return !!data && func === data[0];
37863    }
37864
37865    /**
37866     * Checks if `value` is a valid array-like length.
37867     *
37868     * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
37869     *
37870     * @private
37871     * @param {*} value The value to check.
37872     * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
37873     */
37874    function isLength(value) {
37875      return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
37876    }
37877
37878    /**
37879     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
37880     *
37881     * @private
37882     * @param {*} value The value to check.
37883     * @returns {boolean} Returns `true` if `value` if suitable for strict
37884     *  equality comparisons, else `false`.
37885     */
37886    function isStrictComparable(value) {
37887      return value === value && !isObject(value);
37888    }
37889
37890    /**
37891     * Merges the function metadata of `source` into `data`.
37892     *
37893     * Merging metadata reduces the number of wrappers required to invoke a function.
37894     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
37895     * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
37896     * augment function arguments, making the order in which they are executed important,
37897     * preventing the merging of metadata. However, we make an exception for a safe
37898     * common case where curried functions have `_.ary` and or `_.rearg` applied.
37899     *
37900     * @private
37901     * @param {Array} data The destination metadata.
37902     * @param {Array} source The source metadata.
37903     * @returns {Array} Returns `data`.
37904     */
37905    function mergeData(data, source) {
37906      var bitmask = data[1],
37907          srcBitmask = source[1],
37908          newBitmask = bitmask | srcBitmask,
37909          isCommon = newBitmask < ARY_FLAG;
37910
37911      var isCombo =
37912        (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
37913        (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
37914        (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
37915
37916      // Exit early if metadata can't be merged.
37917      if (!(isCommon || isCombo)) {
37918        return data;
37919      }
37920      // Use source `thisArg` if available.
37921      if (srcBitmask & BIND_FLAG) {
37922        data[2] = source[2];
37923        // Set when currying a bound function.
37924        newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
37925      }
37926      // Compose partial arguments.
37927      var value = source[3];
37928      if (value) {
37929        var partials = data[3];
37930        data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
37931        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
37932      }
37933      // Compose partial right arguments.
37934      value = source[5];
37935      if (value) {
37936        partials = data[5];
37937        data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
37938        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
37939      }
37940      // Use source `argPos` if available.
37941      value = source[7];
37942      if (value) {
37943        data[7] = arrayCopy(value);
37944      }
37945      // Use source `ary` if it's smaller.
37946      if (srcBitmask & ARY_FLAG) {
37947        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
37948      }
37949      // Use source `arity` if one is not provided.
37950      if (data[9] == null) {
37951        data[9] = source[9];
37952      }
37953      // Use source `func` and merge bitmasks.
37954      data[0] = source[0];
37955      data[1] = newBitmask;
37956
37957      return data;
37958    }
37959
37960    /**
37961     * Used by `_.defaultsDeep` to customize its `_.merge` use.
37962     *
37963     * @private
37964     * @param {*} objectValue The destination object property value.
37965     * @param {*} sourceValue The source object property value.
37966     * @returns {*} Returns the value to assign to the destination object.
37967     */
37968    function mergeDefaults(objectValue, sourceValue) {
37969      return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults);
37970    }
37971
37972    /**
37973     * A specialized version of `_.pick` which picks `object` properties specified
37974     * by `props`.
37975     *
37976     * @private
37977     * @param {Object} object The source object.
37978     * @param {string[]} props The property names to pick.
37979     * @returns {Object} Returns the new object.
37980     */
37981    function pickByArray(object, props) {
37982      object = toObject(object);
37983
37984      var index = -1,
37985          length = props.length,
37986          result = {};
37987
37988      while (++index < length) {
37989        var key = props[index];
37990        if (key in object) {
37991          result[key] = object[key];
37992        }
37993      }
37994      return result;
37995    }
37996
37997    /**
37998     * A specialized version of `_.pick` which picks `object` properties `predicate`
37999     * returns truthy for.
38000     *
38001     * @private
38002     * @param {Object} object The source object.
38003     * @param {Function} predicate The function invoked per iteration.
38004     * @returns {Object} Returns the new object.
38005     */
38006    function pickByCallback(object, predicate) {
38007      var result = {};
38008      baseForIn(object, function(value, key, object) {
38009        if (predicate(value, key, object)) {
38010          result[key] = value;
38011        }
38012      });
38013      return result;
38014    }
38015
38016    /**
38017     * Reorder `array` according to the specified indexes where the element at
38018     * the first index is assigned as the first element, the element at
38019     * the second index is assigned as the second element, and so on.
38020     *
38021     * @private
38022     * @param {Array} array The array to reorder.
38023     * @param {Array} indexes The arranged array indexes.
38024     * @returns {Array} Returns `array`.
38025     */
38026    function reorder(array, indexes) {
38027      var arrLength = array.length,
38028          length = nativeMin(indexes.length, arrLength),
38029          oldArray = arrayCopy(array);
38030
38031      while (length--) {
38032        var index = indexes[length];
38033        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
38034      }
38035      return array;
38036    }
38037
38038    /**
38039     * Sets metadata for `func`.
38040     *
38041     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
38042     * period of time, it will trip its breaker and transition to an identity function
38043     * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
38044     * for more details.
38045     *
38046     * @private
38047     * @param {Function} func The function to associate metadata with.
38048     * @param {*} data The metadata.
38049     * @returns {Function} Returns `func`.
38050     */
38051    var setData = (function() {
38052      var count = 0,
38053          lastCalled = 0;
38054
38055      return function(key, value) {
38056        var stamp = now(),
38057            remaining = HOT_SPAN - (stamp - lastCalled);
38058
38059        lastCalled = stamp;
38060        if (remaining > 0) {
38061          if (++count >= HOT_COUNT) {
38062            return key;
38063          }
38064        } else {
38065          count = 0;
38066        }
38067        return baseSetData(key, value);
38068      };
38069    }());
38070
38071    /**
38072     * A fallback implementation of `Object.keys` which creates an array of the
38073     * own enumerable property names of `object`.
38074     *
38075     * @private
38076     * @param {Object} object The object to query.
38077     * @returns {Array} Returns the array of property names.
38078     */
38079    function shimKeys(object) {
38080      var props = keysIn(object),
38081          propsLength = props.length,
38082          length = propsLength && object.length;
38083
38084      var allowIndexes = !!length && isLength(length) &&
38085        (isArray(object) || isArguments(object));
38086
38087      var index = -1,
38088          result = [];
38089
38090      while (++index < propsLength) {
38091        var key = props[index];
38092        if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
38093          result.push(key);
38094        }
38095      }
38096      return result;
38097    }
38098
38099    /**
38100     * Converts `value` to an array-like object if it's not one.
38101     *
38102     * @private
38103     * @param {*} value The value to process.
38104     * @returns {Array|Object} Returns the array-like object.
38105     */
38106    function toIterable(value) {
38107      if (value == null) {
38108        return [];
38109      }
38110      if (!isArrayLike(value)) {
38111        return values(value);
38112      }
38113      return isObject(value) ? value : Object(value);
38114    }
38115
38116    /**
38117     * Converts `value` to an object if it's not one.
38118     *
38119     * @private
38120     * @param {*} value The value to process.
38121     * @returns {Object} Returns the object.
38122     */
38123    function toObject(value) {
38124      return isObject(value) ? value : Object(value);
38125    }
38126
38127    /**
38128     * Converts `value` to property path array if it's not one.
38129     *
38130     * @private
38131     * @param {*} value The value to process.
38132     * @returns {Array} Returns the property path array.
38133     */
38134    function toPath(value) {
38135      if (isArray(value)) {
38136        return value;
38137      }
38138      var result = [];
38139      baseToString(value).replace(rePropName, function(match, number, quote, string) {
38140        result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
38141      });
38142      return result;
38143    }
38144
38145    /**
38146     * Creates a clone of `wrapper`.
38147     *
38148     * @private
38149     * @param {Object} wrapper The wrapper to clone.
38150     * @returns {Object} Returns the cloned wrapper.
38151     */
38152    function wrapperClone(wrapper) {
38153      return wrapper instanceof LazyWrapper
38154        ? wrapper.clone()
38155        : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
38156    }
38157
38158    /*------------------------------------------------------------------------*/
38159
38160    /**
38161     * Creates an array of elements split into groups the length of `size`.
38162     * If `collection` can't be split evenly, the final chunk will be the remaining
38163     * elements.
38164     *
38165     * @static
38166     * @memberOf _
38167     * @category Array
38168     * @param {Array} array The array to process.
38169     * @param {number} [size=1] The length of each chunk.
38170     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
38171     * @returns {Array} Returns the new array containing chunks.
38172     * @example
38173     *
38174     * _.chunk(['a', 'b', 'c', 'd'], 2);
38175     * // => [['a', 'b'], ['c', 'd']]
38176     *
38177     * _.chunk(['a', 'b', 'c', 'd'], 3);
38178     * // => [['a', 'b', 'c'], ['d']]
38179     */
38180    function chunk(array, size, guard) {
38181      if (guard ? isIterateeCall(array, size, guard) : size == null) {
38182        size = 1;
38183      } else {
38184        size = nativeMax(nativeFloor(size) || 1, 1);
38185      }
38186      var index = 0,
38187          length = array ? array.length : 0,
38188          resIndex = -1,
38189          result = Array(nativeCeil(length / size));
38190
38191      while (index < length) {
38192        result[++resIndex] = baseSlice(array, index, (index += size));
38193      }
38194      return result;
38195    }
38196
38197    /**
38198     * Creates an array with all falsey values removed. The values `false`, `null`,
38199     * `0`, `""`, `undefined`, and `NaN` are falsey.
38200     *
38201     * @static
38202     * @memberOf _
38203     * @category Array
38204     * @param {Array} array The array to compact.
38205     * @returns {Array} Returns the new array of filtered values.
38206     * @example
38207     *
38208     * _.compact([0, 1, false, 2, '', 3]);
38209     * // => [1, 2, 3]
38210     */
38211    function compact(array) {
38212      var index = -1,
38213          length = array ? array.length : 0,
38214          resIndex = -1,
38215          result = [];
38216
38217      while (++index < length) {
38218        var value = array[index];
38219        if (value) {
38220          result[++resIndex] = value;
38221        }
38222      }
38223      return result;
38224    }
38225
38226    /**
38227     * Creates an array of unique `array` values not included in the other
38228     * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
38229     * for equality comparisons.
38230     *
38231     * @static
38232     * @memberOf _
38233     * @category Array
38234     * @param {Array} array The array to inspect.
38235     * @param {...Array} [values] The arrays of values to exclude.
38236     * @returns {Array} Returns the new array of filtered values.
38237     * @example
38238     *
38239     * _.difference([1, 2, 3], [4, 2]);
38240     * // => [1, 3]
38241     */
38242    var difference = restParam(function(array, values) {
38243      return (isObjectLike(array) && isArrayLike(array))
38244        ? baseDifference(array, baseFlatten(values, false, true))
38245        : [];
38246    });
38247
38248    /**
38249     * Creates a slice of `array` with `n` elements dropped from the beginning.
38250     *
38251     * @static
38252     * @memberOf _
38253     * @category Array
38254     * @param {Array} array The array to query.
38255     * @param {number} [n=1] The number of elements to drop.
38256     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
38257     * @returns {Array} Returns the slice of `array`.
38258     * @example
38259     *
38260     * _.drop([1, 2, 3]);
38261     * // => [2, 3]
38262     *
38263     * _.drop([1, 2, 3], 2);
38264     * // => [3]
38265     *
38266     * _.drop([1, 2, 3], 5);
38267     * // => []
38268     *
38269     * _.drop([1, 2, 3], 0);
38270     * // => [1, 2, 3]
38271     */
38272    function drop(array, n, guard) {
38273      var length = array ? array.length : 0;
38274      if (!length) {
38275        return [];
38276      }
38277      if (guard ? isIterateeCall(array, n, guard) : n == null) {
38278        n = 1;
38279      }
38280      return baseSlice(array, n < 0 ? 0 : n);
38281    }
38282
38283    /**
38284     * Creates a slice of `array` with `n` elements dropped from the end.
38285     *
38286     * @static
38287     * @memberOf _
38288     * @category Array
38289     * @param {Array} array The array to query.
38290     * @param {number} [n=1] The number of elements to drop.
38291     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
38292     * @returns {Array} Returns the slice of `array`.
38293     * @example
38294     *
38295     * _.dropRight([1, 2, 3]);
38296     * // => [1, 2]
38297     *
38298     * _.dropRight([1, 2, 3], 2);
38299     * // => [1]
38300     *
38301     * _.dropRight([1, 2, 3], 5);
38302     * // => []
38303     *
38304     * _.dropRight([1, 2, 3], 0);
38305     * // => [1, 2, 3]
38306     */
38307    function dropRight(array, n, guard) {
38308      var length = array ? array.length : 0;
38309      if (!length) {
38310        return [];
38311      }
38312      if (guard ? isIterateeCall(array, n, guard) : n == null) {
38313        n = 1;
38314      }
38315      n = length - (+n || 0);
38316      return baseSlice(array, 0, n < 0 ? 0 : n);
38317    }
38318
38319    /**
38320     * Creates a slice of `array` excluding elements dropped from the end.
38321     * Elements are dropped until `predicate` returns falsey. The predicate is
38322     * bound to `thisArg` and invoked with three arguments: (value, index, array).
38323     *
38324     * If a property name is provided for `predicate` the created `_.property`
38325     * style callback returns the property value of the given element.
38326     *
38327     * If a value is also provided for `thisArg` the created `_.matchesProperty`
38328     * style callback returns `true` for elements that have a matching property
38329     * value, else `false`.
38330     *
38331     * If an object is provided for `predicate` the created `_.matches` style
38332     * callback returns `true` for elements that match the properties of the given
38333     * object, else `false`.
38334     *
38335     * @static
38336     * @memberOf _
38337     * @category Array
38338     * @param {Array} array The array to query.
38339     * @param {Function|Object|string} [predicate=_.identity] The function invoked
38340     *  per iteration.
38341     * @param {*} [thisArg] The `this` binding of `predicate`.
38342     * @returns {Array} Returns the slice of `array`.
38343     * @example
38344     *
38345     * _.dropRightWhile([1, 2, 3], function(n) {
38346     *   return n > 1;
38347     * });
38348     * // => [1]
38349     *
38350     * var users = [
38351     *   { 'user': 'barney',  'active': true },
38352     *   { 'user': 'fred',    'active': false },
38353     *   { 'user': 'pebbles', 'active': false }
38354     * ];
38355     *
38356     * // using the `_.matches` callback shorthand
38357     * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user');
38358     * // => ['barney', 'fred']
38359     *
38360     * // using the `_.matchesProperty` callback shorthand
38361     * _.pluck(_.dropRightWhile(users, 'active', false), 'user');
38362     * // => ['barney']
38363     *
38364     * // using the `_.property` callback shorthand
38365     * _.pluck(_.dropRightWhile(users, 'active'), 'user');
38366     * // => ['barney', 'fred', 'pebbles']
38367     */
38368    function dropRightWhile(array, predicate, thisArg) {
38369      return (array && array.length)
38370        ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true)
38371        : [];
38372    }
38373
38374    /**
38375     * Creates a slice of `array` excluding elements dropped from the beginning.
38376     * Elements are dropped until `predicate` returns falsey. The predicate is
38377     * bound to `thisArg` and invoked with three arguments: (value, index, array).
38378     *
38379     * If a property name is provided for `predicate` the created `_.property`
38380     * style callback returns the property value of the given element.
38381     *
38382     * If a value is also provided for `thisArg` the created `_.matchesProperty`
38383     * style callback returns `true` for elements that have a matching property
38384     * value, else `false`.
38385     *
38386     * If an object is provided for `predicate` the created `_.matches` style
38387     * callback returns `true` for elements that have the properties of the given
38388     * object, else `false`.
38389     *
38390     * @static
38391     * @memberOf _
38392     * @category Array
38393     * @param {Array} array The array to query.
38394     * @param {Function|Object|string} [predicate=_.identity] The function invoked
38395     *  per iteration.
38396     * @param {*} [thisArg] The `this` binding of `predicate`.
38397     * @returns {Array} Returns the slice of `array`.
38398     * @example
38399     *
38400     * _.dropWhile([1, 2, 3], function(n) {
38401     *   return n < 3;
38402     * });
38403     * // => [3]
38404     *
38405     * var users = [
38406     *   { 'user': 'barney',  'active': false },
38407     *   { 'user': 'fred',    'active': false },
38408     *   { 'user': 'pebbles', 'active': true }
38409     * ];
38410     *
38411     * // using the `_.matches` callback shorthand
38412     * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user');
38413     * // => ['fred', 'pebbles']
38414     *
38415     * // using the `_.matchesProperty` callback shorthand
38416     * _.pluck(_.dropWhile(users, 'active', false), 'user');
38417     * // => ['pebbles']
38418     *
38419     * // using the `_.property` callback shorthand
38420     * _.pluck(_.dropWhile(users, 'active'), 'user');
38421     * // => ['barney', 'fred', 'pebbles']
38422     */
38423    function dropWhile(array, predicate, thisArg) {
38424      return (array && array.length)
38425        ? baseWhile(array, getCallback(predicate, thisArg, 3), true)
38426        : [];
38427    }
38428
38429    /**
38430     * Fills elements of `array` with `value` from `start` up to, but not
38431     * including, `end`.
38432     *
38433     * **Note:** This method mutates `array`.
38434     *
38435     * @static
38436     * @memberOf _
38437     * @category Array
38438     * @param {Array} array The array to fill.
38439     * @param {*} value The value to fill `array` with.
38440     * @param {number} [start=0] The start position.
38441     * @param {number} [end=array.length] The end position.
38442     * @returns {Array} Returns `array`.
38443     * @example
38444     *
38445     * var array = [1, 2, 3];
38446     *
38447     * _.fill(array, 'a');
38448     * console.log(array);
38449     * // => ['a', 'a', 'a']
38450     *
38451     * _.fill(Array(3), 2);
38452     * // => [2, 2, 2]
38453     *
38454     * _.fill([4, 6, 8], '*', 1, 2);
38455     * // => [4, '*', 8]
38456     */
38457    function fill(array, value, start, end) {
38458      var length = array ? array.length : 0;
38459      if (!length) {
38460        return [];
38461      }
38462      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
38463        start = 0;
38464        end = length;
38465      }
38466      return baseFill(array, value, start, end);
38467    }
38468
38469    /**
38470     * This method is like `_.find` except that it returns the index of the first
38471     * element `predicate` returns truthy for instead of the element itself.
38472     *
38473     * If a property name is provided for `predicate` the created `_.property`
38474     * style callback returns the property value of the given element.
38475     *
38476     * If a value is also provided for `thisArg` the created `_.matchesProperty`
38477     * style callback returns `true` for elements that have a matching property
38478     * value, else `false`.
38479     *
38480     * If an object is provided for `predicate` the created `_.matches` style
38481     * callback returns `true` for elements that have the properties of the given
38482     * object, else `false`.
38483     *
38484     * @static
38485     * @memberOf _
38486     * @category Array
38487     * @param {Array} array The array to search.
38488     * @param {Function|Object|string} [predicate=_.identity] The function invoked
38489     *  per iteration.
38490     * @param {*} [thisArg] The `this` binding of `predicate`.
38491     * @returns {number} Returns the index of the found element, else `-1`.
38492     * @example
38493     *
38494     * var users = [
38495     *   { 'user': 'barney',  'active': false },
38496     *   { 'user': 'fred',    'active': false },
38497     *   { 'user': 'pebbles', 'active': true }
38498     * ];
38499     *
38500     * _.findIndex(users, function(chr) {
38501     *   return chr.user == 'barney';
38502     * });
38503     * // => 0
38504     *
38505     * // using the `_.matches` callback shorthand
38506     * _.findIndex(users, { 'user': 'fred', 'active': false });
38507     * // => 1
38508     *
38509     * // using the `_.matchesProperty` callback shorthand
38510     * _.findIndex(users, 'active', false);
38511     * // => 0
38512     *
38513     * // using the `_.property` callback shorthand
38514     * _.findIndex(users, 'active');
38515     * // => 2
38516     */
38517    var findIndex = createFindIndex();
38518
38519    /**
38520     * This method is like `_.findIndex` except that it iterates over elements
38521     * of `collection` from right to left.
38522     *
38523     * If a property name is provided for `predicate` the created `_.property`
38524     * style callback returns the property value of the given element.
38525     *
38526     * If a value is also provided for `thisArg` the created `_.matchesProperty`
38527     * style callback returns `true` for elements that have a matching property
38528     * value, else `false`.
38529     *
38530     * If an object is provided for `predicate` the created `_.matches` style
38531     * callback returns `true` for elements that have the properties of the given
38532     * object, else `false`.
38533     *
38534     * @static
38535     * @memberOf _
38536     * @category Array
38537     * @param {Array} array The array to search.
38538     * @param {Function|Object|string} [predicate=_.identity] The function invoked
38539     *  per iteration.
38540     * @param {*} [thisArg] The `this` binding of `predicate`.
38541     * @returns {number} Returns the index of the found element, else `-1`.
38542     * @example
38543     *
38544     * var users = [
38545     *   { 'user': 'barney',  'active': true },
38546     *   { 'user': 'fred',    'active': false },
38547     *   { 'user': 'pebbles', 'active': false }
38548     * ];
38549     *
38550     * _.findLastIndex(users, function(chr) {
38551     *   return chr.user == 'pebbles';
38552     * });
38553     * // => 2
38554     *
38555     * // using the `_.matches` callback shorthand
38556     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
38557     * // => 0
38558     *
38559     * // using the `_.matchesProperty` callback shorthand
38560     * _.findLastIndex(users, 'active', false);
38561     * // => 2
38562     *
38563     * // using the `_.property` callback shorthand
38564     * _.findLastIndex(users, 'active');
38565     * // => 0
38566     */
38567    var findLastIndex = createFindIndex(true);
38568
38569    /**
38570     * Gets the first element of `array`.
38571     *
38572     * @static
38573     * @memberOf _
38574     * @alias head
38575     * @category Array
38576     * @param {Array} array The array to query.
38577     * @returns {*} Returns the first element of `array`.
38578     * @example
38579     *
38580     * _.first([1, 2, 3]);
38581     * // => 1
38582     *
38583     * _.first([]);
38584     * // => undefined
38585     */
38586    function first(array) {
38587      return array ? array[0] : undefined;
38588    }
38589
38590    /**
38591     * Flattens a nested array. If `isDeep` is `true` the array is recursively
38592     * flattened, otherwise it is only flattened a single level.
38593     *
38594     * @static
38595     * @memberOf _
38596     * @category Array
38597     * @param {Array} array The array to flatten.
38598     * @param {boolean} [isDeep] Specify a deep flatten.
38599     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
38600     * @returns {Array} Returns the new flattened array.
38601     * @example
38602     *
38603     * _.flatten([1, [2, 3, [4]]]);
38604     * // => [1, 2, 3, [4]]
38605     *
38606     * // using `isDeep`
38607     * _.flatten([1, [2, 3, [4]]], true);
38608     * // => [1, 2, 3, 4]
38609     */
38610    function flatten(array, isDeep, guard) {
38611      var length = array ? array.length : 0;
38612      if (guard && isIterateeCall(array, isDeep, guard)) {
38613        isDeep = false;
38614      }
38615      return length ? baseFlatten(array, isDeep) : [];
38616    }
38617
38618    /**
38619     * Recursively flattens a nested array.
38620     *
38621     * @static
38622     * @memberOf _
38623     * @category Array
38624     * @param {Array} array The array to recursively flatten.
38625     * @returns {Array} Returns the new flattened array.
38626     * @example
38627     *
38628     * _.flattenDeep([1, [2, 3, [4]]]);
38629     * // => [1, 2, 3, 4]
38630     */
38631    function flattenDeep(array) {
38632      var length = array ? array.length : 0;
38633      return length ? baseFlatten(array, true) : [];
38634    }
38635
38636    /**
38637     * Gets the index at which the first occurrence of `value` is found in `array`
38638     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
38639     * for equality comparisons. If `fromIndex` is negative, it is used as the offset
38640     * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
38641     * performs a faster binary search.
38642     *
38643     * @static
38644     * @memberOf _
38645     * @category Array
38646     * @param {Array} array The array to search.
38647     * @param {*} value The value to search for.
38648     * @param {boolean|number} [fromIndex=0] The index to search from or `true`
38649     *  to perform a binary search on a sorted array.
38650     * @returns {number} Returns the index of the matched value, else `-1`.
38651     * @example
38652     *
38653     * _.indexOf([1, 2, 1, 2], 2);
38654     * // => 1
38655     *
38656     * // using `fromIndex`
38657     * _.indexOf([1, 2, 1, 2], 2, 2);
38658     * // => 3
38659     *
38660     * // performing a binary search
38661     * _.indexOf([1, 1, 2, 2], 2, true);
38662     * // => 2
38663     */
38664    function indexOf(array, value, fromIndex) {
38665      var length = array ? array.length : 0;
38666      if (!length) {
38667        return -1;
38668      }
38669      if (typeof fromIndex == 'number') {
38670        fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
38671      } else if (fromIndex) {
38672        var index = binaryIndex(array, value);
38673        if (index < length &&
38674            (value === value ? (value === array[index]) : (array[index] !== array[index]))) {
38675          return index;
38676        }
38677        return -1;
38678      }
38679      return baseIndexOf(array, value, fromIndex || 0);
38680    }
38681
38682    /**
38683     * Gets all but the last element of `array`.
38684     *
38685     * @static
38686     * @memberOf _
38687     * @category Array
38688     * @param {Array} array The array to query.
38689     * @returns {Array} Returns the slice of `array`.
38690     * @example
38691     *
38692     * _.initial([1, 2, 3]);
38693     * // => [1, 2]
38694     */
38695    function initial(array) {
38696      return dropRight(array, 1);
38697    }
38698
38699    /**
38700     * Creates an array of unique values that are included in all of the provided
38701     * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
38702     * for equality comparisons.
38703     *
38704     * @static
38705     * @memberOf _
38706     * @category Array
38707     * @param {...Array} [arrays] The arrays to inspect.
38708     * @returns {Array} Returns the new array of shared values.
38709     * @example
38710     * _.intersection([1, 2], [4, 2], [2, 1]);
38711     * // => [2]
38712     */
38713    var intersection = restParam(function(arrays) {
38714      var othLength = arrays.length,
38715          othIndex = othLength,
38716          caches = Array(length),
38717          indexOf = getIndexOf(),
38718          isCommon = indexOf == baseIndexOf,
38719          result = [];
38720
38721      while (othIndex--) {
38722        var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
38723        caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
38724      }
38725      var array = arrays[0],
38726          index = -1,
38727          length = array ? array.length : 0,
38728          seen = caches[0];
38729
38730      outer:
38731      while (++index < length) {
38732        value = array[index];
38733        if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
38734          var othIndex = othLength;
38735          while (--othIndex) {
38736            var cache = caches[othIndex];
38737            if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
38738              continue outer;
38739            }
38740          }
38741          if (seen) {
38742            seen.push(value);
38743          }
38744          result.push(value);
38745        }
38746      }
38747      return result;
38748    });
38749
38750    /**
38751     * Gets the last element of `array`.
38752     *
38753     * @static
38754     * @memberOf _
38755     * @category Array
38756     * @param {Array} array The array to query.
38757     * @returns {*} Returns the last element of `array`.
38758     * @example
38759     *
38760     * _.last([1, 2, 3]);
38761     * // => 3
38762     */
38763    function last(array) {
38764      var length = array ? array.length : 0;
38765      return length ? array[length - 1] : undefined;
38766    }
38767
38768    /**
38769     * This method is like `_.indexOf` except that it iterates over elements of
38770     * `array` from right to left.
38771     *
38772     * @static
38773     * @memberOf _
38774     * @category Array
38775     * @param {Array} array The array to search.
38776     * @param {*} value The value to search for.
38777     * @param {boolean|number} [fromIndex=array.length-1] The index to search from
38778     *  or `true` to perform a binary search on a sorted array.
38779     * @returns {number} Returns the index of the matched value, else `-1`.
38780     * @example
38781     *
38782     * _.lastIndexOf([1, 2, 1, 2], 2);
38783     * // => 3
38784     *
38785     * // using `fromIndex`
38786     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
38787     * // => 1
38788     *
38789     * // performing a binary search
38790     * _.lastIndexOf([1, 1, 2, 2], 2, true);
38791     * // => 3
38792     */
38793    function lastIndexOf(array, value, fromIndex) {
38794      var length = array ? array.length : 0;
38795      if (!length) {
38796        return -1;
38797      }
38798      var index = length;
38799      if (typeof fromIndex == 'number') {
38800        index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1;
38801      } else if (fromIndex) {
38802        index = binaryIndex(array, value, true) - 1;
38803        var other = array[index];
38804        if (value === value ? (value === other) : (other !== other)) {
38805          return index;
38806        }
38807        return -1;
38808      }
38809      if (value !== value) {
38810        return indexOfNaN(array, index, true);
38811      }
38812      while (index--) {
38813        if (array[index] === value) {
38814          return index;
38815        }
38816      }
38817      return -1;
38818    }
38819
38820    /**
38821     * Removes all provided values from `array` using
38822     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
38823     * for equality comparisons.
38824     *
38825     * **Note:** Unlike `_.without`, this method mutates `array`.
38826     *
38827     * @static
38828     * @memberOf _
38829     * @category Array
38830     * @param {Array} array The array to modify.
38831     * @param {...*} [values] The values to remove.
38832     * @returns {Array} Returns `array`.
38833     * @example
38834     *
38835     * var array = [1, 2, 3, 1, 2, 3];
38836     *
38837     * _.pull(array, 2, 3);
38838     * console.log(array);
38839     * // => [1, 1]
38840     */
38841    function pull() {
38842      var args = arguments,
38843          array = args[0];
38844
38845      if (!(array && array.length)) {
38846        return array;
38847      }
38848      var index = 0,
38849          indexOf = getIndexOf(),
38850          length = args.length;
38851
38852      while (++index < length) {
38853        var fromIndex = 0,
38854            value = args[index];
38855
38856        while ((fromIndex = indexOf(array, value, fromIndex)) > -1) {
38857          splice.call(array, fromIndex, 1);
38858        }
38859      }
38860      return array;
38861    }
38862
38863    /**
38864     * Removes elements from `array` corresponding to the given indexes and returns
38865     * an array of the removed elements. Indexes may be specified as an array of
38866     * indexes or as individual arguments.
38867     *
38868     * **Note:** Unlike `_.at`, this method mutates `array`.
38869     *
38870     * @static
38871     * @memberOf _
38872     * @category Array
38873     * @param {Array} array The array to modify.
38874     * @param {...(number|number[])} [indexes] The indexes of elements to remove,
38875     *  specified as individual indexes or arrays of indexes.
38876     * @returns {Array} Returns the new array of removed elements.
38877     * @example
38878     *
38879     * var array = [5, 10, 15, 20];
38880     * var evens = _.pullAt(array, 1, 3);
38881     *
38882     * console.log(array);
38883     * // => [5, 15]
38884     *
38885     * console.log(evens);
38886     * // => [10, 20]
38887     */
38888    var pullAt = restParam(function(array, indexes) {
38889      indexes = baseFlatten(indexes);
38890
38891      var result = baseAt(array, indexes);
38892      basePullAt(array, indexes.sort(baseCompareAscending));
38893      return result;
38894    });
38895
38896    /**
38897     * Removes all elements from `array` that `predicate` returns truthy for
38898     * and returns an array of the removed elements. The predicate is bound to
38899     * `thisArg` and invoked with three arguments: (value, index, array).
38900     *
38901     * If a property name is provided for `predicate` the created `_.property`
38902     * style callback returns the property value of the given element.
38903     *
38904     * If a value is also provided for `thisArg` the created `_.matchesProperty`
38905     * style callback returns `true` for elements that have a matching property
38906     * value, else `false`.
38907     *
38908     * If an object is provided for `predicate` the created `_.matches` style
38909     * callback returns `true` for elements that have the properties of the given
38910     * object, else `false`.
38911     *
38912     * **Note:** Unlike `_.filter`, this method mutates `array`.
38913     *
38914     * @static
38915     * @memberOf _
38916     * @category Array
38917     * @param {Array} array The array to modify.
38918     * @param {Function|Object|string} [predicate=_.identity] The function invoked
38919     *  per iteration.
38920     * @param {*} [thisArg] The `this` binding of `predicate`.
38921     * @returns {Array} Returns the new array of removed elements.
38922     * @example
38923     *
38924     * var array = [1, 2, 3, 4];
38925     * var evens = _.remove(array, function(n) {
38926     *   return n % 2 == 0;
38927     * });
38928     *
38929     * console.log(array);
38930     * // => [1, 3]
38931     *
38932     * console.log(evens);
38933     * // => [2, 4]
38934     */
38935    function remove(array, predicate, thisArg) {
38936      var result = [];
38937      if (!(array && array.length)) {
38938        return result;
38939      }
38940      var index = -1,
38941          indexes = [],
38942          length = array.length;
38943
38944      predicate = getCallback(predicate, thisArg, 3);
38945      while (++index < length) {
38946        var value = array[index];
38947        if (predicate(value, index, array)) {
38948          result.push(value);
38949          indexes.push(index);
38950        }
38951      }
38952      basePullAt(array, indexes);
38953      return result;
38954    }
38955
38956    /**
38957     * Gets all but the first element of `array`.
38958     *
38959     * @static
38960     * @memberOf _
38961     * @alias tail
38962     * @category Array
38963     * @param {Array} array The array to query.
38964     * @returns {Array} Returns the slice of `array`.
38965     * @example
38966     *
38967     * _.rest([1, 2, 3]);
38968     * // => [2, 3]
38969     */
38970    function rest(array) {
38971      return drop(array, 1);
38972    }
38973
38974    /**
38975     * Creates a slice of `array` from `start` up to, but not including, `end`.
38976     *
38977     * **Note:** This method is used instead of `Array#slice` to supp
38977ort node
38978     * lists in IE < 9 and to ensure dense arrays are returned.
38979     *
38980     * @static
38981     * @memberOf _
38982     * @category Array
38983     * @param {Array} array The array to slice.
38984     * @param {number} [start=0] The start position.
38985     * @param {number} [end=array.length] The end position.
38986     * @returns {Array} Returns the slice of `array`.
38987     */
38988    function slice(array, start, end) {
38989      var length = array ? array.length : 0;
38990      if (!length) {
38991        return [];
38992      }
38993      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
38994        start = 0;
38995        end = length;
38996      }
38997      return baseSlice(array, start, end);
38998    }
38999
39000    /**
39001     * Uses a binary search to determine the lowest index at which `value` should
39002     * be inserted into `array` in order to maintain its sort order. If an iteratee
39003     * function is provided it is invoked for `value` and each element of `array`
39004     * to compute their sort ranking. The iteratee is bound to `thisArg` and
39005     * invoked with one argument; (value).
39006     *
39007     * If a property name is provided for `iteratee` the created `_.property`
39008     * style callback returns the property value of the given element.
39009     *
39010     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39011     * style callback returns `true` for elements that have a matching property
39012     * value, else `false`.
39013     *
39014     * If an object is provided for `iteratee` the created `_.matches` style
39015     * callback returns `true` for elements that have the properties of the given
39016     * object, else `false`.
39017     *
39018     * @static
39019     * @memberOf _
39020     * @category Array
39021     * @param {Array} array The sorted array to inspect.
39022     * @param {*} value The value to evaluate.
39023     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
39024     *  per iteration.
39025     * @param {*} [thisArg] The `this` binding of `iteratee`.
39026     * @returns {number} Returns the index at which `value` should be inserted
39027     *  into `array`.
39028     * @example
39029     *
39030     * _.sortedIndex([30, 50], 40);
39031     * // => 1
39032     *
39033     * _.sortedIndex([4, 4, 5, 5], 5);
39034     * // => 2
39035     *
39036     * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } };
39037     *
39038     * // using an iteratee function
39039     * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) {
39040     *   return this.data[word];
39041     * }, dict);
39042     * // => 1
39043     *
39044     * // using the `_.property` callback shorthand
39045     * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
39046     * // => 1
39047     */
39048    var sortedIndex = createSortedIndex();
39049
39050    /**
39051     * This method is like `_.sortedIndex` except that it returns the highest
39052     * index at which `value` should be inserted into `array` in order to
39053     * maintain its sort order.
39054     *
39055     * @static
39056     * @memberOf _
39057     * @category Array
39058     * @param {Array} array The sorted array to inspect.
39059     * @param {*} value The value to evaluate.
39060     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
39061     *  per iteration.
39062     * @param {*} [thisArg] The `this` binding of `iteratee`.
39063     * @returns {number} Returns the index at which `value` should be inserted
39064     *  into `array`.
39065     * @example
39066     *
39067     * _.sortedLastIndex([4, 4, 5, 5], 5);
39068     * // => 4
39069     */
39070    var sortedLastIndex = createSortedIndex(true);
39071
39072    /**
39073     * Creates a slice of `array` with `n` elements taken from the beginning.
39074     *
39075     * @static
39076     * @memberOf _
39077     * @category Array
39078     * @param {Array} array The array to query.
39079     * @param {number} [n=1] The number of elements to take.
39080     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
39081     * @returns {Array} Returns the slice of `array`.
39082     * @example
39083     *
39084     * _.take([1, 2, 3]);
39085     * // => [1]
39086     *
39087     * _.take([1, 2, 3], 2);
39088     * // => [1, 2]
39089     *
39090     * _.take([1, 2, 3], 5);
39091     * // => [1, 2, 3]
39092     *
39093     * _.take([1, 2, 3], 0);
39094     * // => []
39095     */
39096    function take(array, n, guard) {
39097      var length = array ? array.length : 0;
39098      if (!length) {
39099        return [];
39100      }
39101      if (guard ? isIterateeCall(array, n, guard) : n == null) {
39102        n = 1;
39103      }
39104      return baseSlice(array, 0, n < 0 ? 0 : n);
39105    }
39106
39107    /**
39108     * Creates a slice of `array` with `n` elements taken from the end.
39109     *
39110     * @static
39111     * @memberOf _
39112     * @category Array
39113     * @param {Array} array The array to query.
39114     * @param {number} [n=1] The number of elements to take.
39115     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
39116     * @returns {Array} Returns the slice of `array`.
39117     * @example
39118     *
39119     * _.takeRight([1, 2, 3]);
39120     * // => [3]
39121     *
39122     * _.takeRight([1, 2, 3], 2);
39123     * // => [2, 3]
39124     *
39125     * _.takeRight([1, 2, 3], 5);
39126     * // => [1, 2, 3]
39127     *
39128     * _.takeRight([1, 2, 3], 0);
39129     * // => []
39130     */
39131    function takeRight(array, n, guard) {
39132      var length = array ? array.length : 0;
39133      if (!length) {
39134        return [];
39135      }
39136      if (guard ? isIterateeCall(array, n, guard) : n == null) {
39137        n = 1;
39138      }
39139      n = length - (+n || 0);
39140      return baseSlice(array, n < 0 ? 0 : n);
39141    }
39142
39143    /**
39144     * Creates a slice of `array` with elements taken from the end. Elements are
39145     * taken until `predicate` returns falsey. The predicate is bound to `thisArg`
39146     * and invoked with three arguments: (value, index, array).
39147     *
39148     * If a property name is provided for `predicate` the created `_.property`
39149     * style callback returns the property value of the given element.
39150     *
39151     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39152     * style callback returns `true` for elements that have a matching property
39153     * value, else `false`.
39154     *
39155     * If an object is provided for `predicate` the created `_.matches` style
39156     * callback returns `true` for elements that have the properties of the given
39157     * object, else `false`.
39158     *
39159     * @static
39160     * @memberOf _
39161     * @category Array
39162     * @param {Array} array The array to query.
39163     * @param {Function|Object|string} [predicate=_.identity] The function invoked
39164     *  per iteration.
39165     * @param {*} [thisArg] The `this` binding of `predicate`.
39166     * @returns {Array} Returns the slice of `array`.
39167     * @example
39168     *
39169     * _.takeRightWhile([1, 2, 3], function(n) {
39170     *   return n > 1;
39171     * });
39172     * // => [2, 3]
39173     *
39174     * var users = [
39175     *   { 'user': 'barney',  'active': true },
39176     *   { 'user': 'fred',    'active': false },
39177     *   { 'user': 'pebbles', 'active': false }
39178     * ];
39179     *
39180     * // using the `_.matches` callback shorthand
39181     * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user');
39182     * // => ['pebbles']
39183     *
39184     * // using the `_.matchesProperty` callback shorthand
39185     * _.pluck(_.takeRightWhile(users, 'active', false), 'user');
39186     * // => ['fred', 'pebbles']
39187     *
39188     * // using the `_.property` callback shorthand
39189     * _.pluck(_.takeRightWhile(users, 'active'), 'user');
39190     * // => []
39191     */
39192    function takeRightWhile(array, predicate, thisArg) {
39193      return (array && array.length)
39194        ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true)
39195        : [];
39196    }
39197
39198    /**
39199     * Creates a slice of `array` with elements taken from the beginning. Elements
39200     * are taken until `predicate` returns falsey. The predicate is bound to
39201     * `thisArg` and invoked with three arguments: (value, index, array).
39202     *
39203     * If a property name is provided for `predicate` the created `_.property`
39204     * style callback returns the property value of the given element.
39205     *
39206     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39207     * style callback returns `true` for elements that have a matching property
39208     * value, else `false`.
39209     *
39210     * If an object is provided for `predicate` the created `_.matches` style
39211     * callback returns `true` for elements that have the properties of the given
39212     * object, else `false`.
39213     *
39214     * @static
39215     * @memberOf _
39216     * @category Array
39217     * @param {Array} array The array to query.
39218     * @param {Function|Object|string} [predicate=_.identity] The function invoked
39219     *  per iteration.
39220     * @param {*} [thisArg] The `this` binding of `predicate`.
39221     * @returns {Array} Returns the slice of `array`.
39222     * @example
39223     *
39224     * _.takeWhile([1, 2, 3], function(n) {
39225     *   return n < 3;
39226     * });
39227     * // => [1, 2]
39228     *
39229     * var users = [
39230     *   { 'user': 'barney',  'active': false },
39231     *   { 'user': 'fred',    'active': false},
39232     *   { 'user': 'pebbles', 'active': true }
39233     * ];
39234     *
39235     * // using the `_.matches` callback shorthand
39236     * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user');
39237     * // => ['barney']
39238     *
39239     * // using the `_.matchesProperty` callback shorthand
39240     * _.pluck(_.takeWhile(users, 'active', false), 'user');
39241     * // => ['barney', 'fred']
39242     *
39243     * // using the `_.property` callback shorthand
39244     * _.pluck(_.takeWhile(users, 'active'), 'user');
39245     * // => []
39246     */
39247    function takeWhile(array, predicate, thisArg) {
39248      return (array && array.length)
39249        ? baseWhile(array, getCallback(predicate, thisArg, 3))
39250        : [];
39251    }
39252
39253    /**
39254     * Creates an array of unique values, in order, from all of the provided arrays
39255     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
39256     * for equality comparisons.
39257     *
39258     * @static
39259     * @memberOf _
39260     * @category Array
39261     * @param {...Array} [arrays] The arrays to inspect.
39262     * @returns {Array} Returns the new array of combined values.
39263     * @example
39264     *
39265     * _.union([1, 2], [4, 2], [2, 1]);
39266     * // => [1, 2, 4]
39267     */
39268    var union = restParam(function(arrays) {
39269      return baseUniq(baseFlatten(arrays, false, true));
39270    });
39271
39272    /**
39273     * Creates a duplicate-free version of an array, using
39274     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
39275     * for equality comparisons, in which only the first occurence of each element
39276     * is kept. Providing `true` for `isSorted` performs a faster search algorithm
39277     * for sorted arrays. If an iteratee function is provided it is invoked for
39278     * each element in the array to generate the criterion by which uniqueness
39279     * is computed. The `iteratee` is bound to `thisArg` and invoked with three
39280     * arguments: (value, index, array).
39281     *
39282     * If a property name is provided for `iteratee` the created `_.property`
39283     * style callback returns the property value of the given element.
39284     *
39285     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39286     * style callback returns `true` for elements that have a matching property
39287     * value, else `false`.
39288     *
39289     * If an object is provided for `iteratee` the created `_.matches` style
39290     * callback returns `true` for elements that have the properties of the given
39291     * object, else `false`.
39292     *
39293     * @static
39294     * @memberOf _
39295     * @alias unique
39296     * @category Array
39297     * @param {Array} array The array to inspect.
39298     * @param {boolean} [isSorted] Specify the array is sorted.
39299     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
39300     * @param {*} [thisArg] The `this` binding of `iteratee`.
39301     * @returns {Array} Returns the new duplicate-value-free array.
39302     * @example
39303     *
39304     * _.uniq([2, 1, 2]);
39305     * // => [2, 1]
39306     *
39307     * // using `isSorted`
39308     * _.uniq([1, 1, 2], true);
39309     * // => [1, 2]
39310     *
39311     * // using an iteratee function
39312     * _.uniq([1, 2.5, 1.5, 2], function(n) {
39313     *   return this.floor(n);
39314     * }, Math);
39315     * // => [1, 2.5]
39316     *
39317     * // using the `_.property` callback shorthand
39318     * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
39319     * // => [{ 'x': 1 }, { 'x': 2 }]
39320     */
39321    function uniq(array, isSorted, iteratee, thisArg) {
39322      var length = array ? array.length : 0;
39323      if (!length) {
39324        return [];
39325      }
39326      if (isSorted != null && typeof isSorted != 'boolean') {
39327        thisArg = iteratee;
39328        iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted;
39329        isSorted = false;
39330      }
39331      var callback = getCallback();
39332      if (!(iteratee == null && callback === baseCallback)) {
39333        iteratee = callback(iteratee, thisArg, 3);
39334      }
39335      return (isSorted && getIndexOf() == baseIndexOf)
39336        ? sortedUniq(array, iteratee)
39337        : baseUniq(array, iteratee);
39338    }
39339
39340    /**
39341     * This method is like `_.zip` except that it accepts an array of grouped
39342     * elements and creates an array regrouping the elements to their pre-zip
39343     * configuration.
39344     *
39345     * @static
39346     * @memberOf _
39347     * @category Array
39348     * @param {Array} array The array of grouped elements to process.
39349     * @returns {Array} Returns the new array of regrouped elements.
39350     * @example
39351     *
39352     * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
39353     * // => [['fred', 30, true], ['barney', 40, false]]
39354     *
39355     * _.unzip(zipped);
39356     * // => [['fred', 'barney'], [30, 40], [true, false]]
39357     */
39358    function unzip(array) {
39359      if (!(array && array.length)) {
39360        return [];
39361      }
39362      var index = -1,
39363          length = 0;
39364
39365      array = arrayFilter(array, function(group) {
39366        if (isArrayLike(group)) {
39367          length = nativeMax(group.length, length);
39368          return true;
39369        }
39370      });
39371      var result = Array(length);
39372      while (++index < length) {
39373        result[index] = arrayMap(array, baseProperty(index));
39374      }
39375      return result;
39376    }
39377
39378    /**
39379     * This method is like `_.unzip` except that it accepts an iteratee to specify
39380     * how regrouped values should be combined. The `iteratee` is bound to `thisArg`
39381     * and invoked with four arguments: (accumulator, value, index, group).
39382     *
39383     * @static
39384     * @memberOf _
39385     * @category Array
39386     * @param {Array} array The array of grouped elements to process.
39387     * @param {Function} [iteratee] The function to combine regrouped values.
39388     * @param {*} [thisArg] The `this` binding of `iteratee`.
39389     * @returns {Array} Returns the new array of regrouped elements.
39390     * @example
39391     *
39392     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
39393     * // => [[1, 10, 100], [2, 20, 200]]
39394     *
39395     * _.unzipWith(zipped, _.add);
39396     * // => [3, 30, 300]
39397     */
39398    function unzipWith(array, iteratee, thisArg) {
39399      var length = array ? array.length : 0;
39400      if (!length) {
39401        return [];
39402      }
39403      var result = unzip(array);
39404      if (iteratee == null) {
39405        return result;
39406      }
39407      iteratee = bindCallback(iteratee, thisArg, 4);
39408      return arrayMap(result, function(group) {
39409        return arrayReduce(group, iteratee, undefined, true);
39410      });
39411    }
39412
39413    /**
39414     * Creates an array excluding all provided values using
39415     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
39416     * for equality comparisons.
39417     *
39418     * @static
39419     * @memberOf _
39420     * @category Array
39421     * @param {Array} array The array to filter.
39422     * @param {...*} [values] The values to exclude.
39423     * @returns {Array} Returns the new array of filtered values.
39424     * @example
39425     *
39426     * _.without([1, 2, 1, 3], 1, 2);
39427     * // => [3]
39428     */
39429    var without = restParam(function(array, values) {
39430      return isArrayLike(array)
39431        ? baseDifference(array, values)
39432        : [];
39433    });
39434
39435    /**
39436     * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
39437     * of the provided arrays.
39438     *
39439     * @static
39440     * @memberOf _
39441     * @category Array
39442     * @param {...Array} [arrays] The arrays to inspect.
39443     * @returns {Array} Returns the new array of values.
39444     * @example
39445     *
39446     * _.xor([1, 2], [4, 2]);
39447     * // => [1, 4]
39448     */
39449    function xor() {
39450      var index = -1,
39451          length = arguments.length;
39452
39453      while (++index < length) {
39454        var array = arguments[index];
39455        if (isArrayLike(array)) {
39456          var result = result
39457            ? arrayPush(baseDifference(result, array), baseDifference(array, result))
39458            : array;
39459        }
39460      }
39461      return result ? baseUniq(result) : [];
39462    }
39463
39464    /**
39465     * Creates an array of grouped elements, the first of which contains the first
39466     * elements of the given arrays, the second of which contains the second elements
39467     * of the given arrays, and so on.
39468     *
39469     * @static
39470     * @memberOf _
39471     * @category Array
39472     * @param {...Array} [arrays] The arrays to process.
39473     * @returns {Array} Returns the new array of grouped elements.
39474     * @example
39475     *
39476     * _.zip(['fred', 'barney'], [30, 40], [true, false]);
39477     * // => [['fred', 30, true], ['barney', 40, false]]
39478     */
39479    var zip = restParam(unzip);
39480
39481    /**
39482     * The inverse of `_.pairs`;
39482 this method returns an object composed from arrays
39483     * of property names and values. Provide either a single two dimensional array,
39484     * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names
39485     * and one of corresponding values.
39486     *
39487     * @static
39488     * @memberOf _
39489     * @alias object
39490     * @category Array
39491     * @param {Array} props The property names.
39492     * @param {Array} [values=[]] The property values.
39493     * @returns {Object} Returns the new object.
39494     * @example
39495     *
39496     * _.zipObject([['fred', 30], ['barney', 40]]);
39497     * // => { 'fred': 30, 'barney': 40 }
39498     *
39499     * _.zipObject(['fred', 'barney'], [30, 40]);
39500     * // => { 'fred': 30, 'barney': 40 }
39501     */
39502    function zipObject(props, values) {
39503      var index = -1,
39504          length = props ? props.length : 0,
39505          result = {};
39506
39507      if (length && !values && !isArray(props[0])) {
39508        values = [];
39509      }
39510      while (++index < length) {
39511        var key = props[index];
39512        if (values) {
39513          result[key] = values[index];
39514        } else if (key) {
39515          result[key[0]] = key[1];
39516        }
39517      }
39518      return result;
39519    }
39520
39521    /**
39522     * This method is like `_.zip` except that it accepts an iteratee to specify
39523     * how grouped values should be combined. The `iteratee` is bound to `thisArg`
39524     * and invoked with four arguments: (accumulator, value, index, group).
39525     *
39526     * @static
39527     * @memberOf _
39528     * @category Array
39529     * @param {...Array} [arrays] The arrays to process.
39530     * @param {Function} [iteratee] The function to combine grouped values.
39531     * @param {*} [thisArg] The `this` binding of `iteratee`.
39532     * @returns {Array} Returns the new array of grouped elements.
39533     * @example
39534     *
39535     * _.zipWith([1, 2], [10, 20], [100, 200], _.add);
39536     * // => [111, 222]
39537     */
39538    var zipWith = restParam(function(arrays) {
39539      var length = arrays.length,
39540          iteratee = length > 2 ? arrays[length - 2] : undefined,
39541          thisArg = length > 1 ? arrays[length - 1] : undefined;
39542
39543      if (length > 2 && typeof iteratee == 'function') {
39544        length -= 2;
39545      } else {
39546        iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined;
39547        thisArg = undefined;
39548      }
39549      arrays.length = length;
39550      return unzipWith(arrays, iteratee, thisArg);
39551    });
39552
39553    /*------------------------------------------------------------------------*/
39554
39555    /**
39556     * Creates a `lodash` object that wraps `value` with explicit method
39557     * chaining enabled.
39558     *
39559     * @static
39560     * @memberOf _
39561     * @category Chain
39562     * @param {*} value The value to wrap.
39563     * @returns {Object} Returns the new `lodash` wrapper instance.
39564     * @example
39565     *
39566     * var users = [
39567     *   { 'user': 'barney',  'age': 36 },
39568     *   { 'user': 'fred',    'age': 40 },
39569     *   { 'user': 'pebbles', 'age': 1 }
39570     * ];
39571     *
39572     * var youngest = _.chain(users)
39573     *   .sortBy('age')
39574     *   .map(function(chr) {
39575     *     return chr.user + ' is ' + chr.age;
39576     *   })
39577     *   .first()
39578     *   .value();
39579     * // => 'pebbles is 1'
39580     */
39581    function chain(value) {
39582      var result = lodash(value);
39583      result.__chain__ = true;
39584      return result;
39585    }
39586
39587    /**
39588     * This method invokes `interceptor` and returns `value`. The interceptor is
39589     * bound to `thisArg` and invoked with one argument; (value). The purpose of
39590     * this method is to "tap into" a method chain in order to perform operations
39591     * on intermediate results within the chain.
39592     *
39593     * @static
39594     * @memberOf _
39595     * @category Chain
39596     * @param {*} value The value to provide to `interceptor`.
39597     * @param {Function} interceptor The function to invoke.
39598     * @param {*} [thisArg] The `this` binding of `interceptor`.
39599     * @returns {*} Returns `value`.
39600     * @example
39601     *
39602     * _([1, 2, 3])
39603     *  .tap(function(array) {
39604     *    array.pop();
39605     *  })
39606     *  .reverse()
39607     *  .value();
39608     * // => [2, 1]
39609     */
39610    function tap(value, interceptor, thisArg) {
39611      interceptor.call(thisArg, value);
39612      return value;
39613    }
39614
39615    /**
39616     * This method is like `_.tap` except that it returns the result of `interceptor`.
39617     *
39618     * @static
39619     * @memberOf _
39620     * @category Chain
39621     * @param {*} value The value to provide to `interceptor`.
39622     * @param {Function} interceptor The function to invoke.
39623     * @param {*} [thisArg] The `this` binding of `interceptor`.
39624     * @returns {*} Returns the result of `interceptor`.
39625     * @example
39626     *
39627     * _('  abc  ')
39628     *  .chain()
39629     *  .trim()
39630     *  .thru(function(value) {
39631     *    return [value];
39632     *  })
39633     *  .value();
39634     * // => ['abc']
39635     */
39636    function thru(value, interceptor, thisArg) {
39637      return interceptor.call(thisArg, value);
39638    }
39639
39640    /**
39641     * Enables explicit method chaining on the wrapper object.
39642     *
39643     * @name chain
39644     * @memberOf _
39645     * @category Chain
39646     * @returns {Object} Returns the new `lodash` wrapper instance.
39647     * @example
39648     *
39649     * var users = [
39650     *   { 'user': 'barney', 'age': 36 },
39651     *   { 'user': 'fred',   'age': 40 }
39652     * ];
39653     *
39654     * // without explicit chaining
39655     * _(users).first();
39656     * // => { 'user': 'barney', 'age': 36 }
39657     *
39658     * // with explicit chaining
39659     * _(users).chain()
39660     *   .first()
39661     *   .pick('user')
39662     *   .value();
39663     * // => { 'user': 'barney' }
39664     */
39665    function wrapperChain() {
39666      return chain(this);
39667    }
39668
39669    /**
39670     * Executes the chained sequence and returns the wrapped result.
39671     *
39672     * @name commit
39673     * @memberOf _
39674     * @category Chain
39675     * @returns {Object} Returns the new `lodash` wrapper instance.
39676     * @example
39677     *
39678     * var array = [1, 2];
39679     * var wrapped = _(array).push(3);
39680     *
39681     * console.log(array);
39682     * // => [1, 2]
39683     *
39684     * wrapped = wrapped.commit();
39685     * console.log(array);
39686     * // => [1, 2, 3]
39687     *
39688     * wrapped.last();
39689     * // => 3
39690     *
39691     * console.log(array);
39692     * // => [1, 2, 3]
39693     */
39694    function wrapperCommit() {
39695      return new LodashWrapper(this.value(), this.__chain__);
39696    }
39697
39698    /**
39699     * Creates a new array joining a wrapped array with any additional arrays
39700     * and/or values.
39701     *
39702     * @name concat
39703     * @memberOf _
39704     * @category Chain
39705     * @param {...*} [values] The values to concatenate.
39706     * @returns {Array} Returns the new concatenated array.
39707     * @example
39708     *
39709     * var array = [1];
39710     * var wrapped = _(array).concat(2, [3], [[4]]);
39711     *
39712     * console.log(wrapped.value());
39713     * // => [1, 2, 3, [4]]
39714     *
39715     * console.log(array);
39716     * // => [1]
39717     */
39718    var wrapperConcat = restParam(function(values) {
39719      values = baseFlatten(values);
39720      return this.thru(function(array) {
39721        return arrayConcat(isArray(array) ? array : [toObject(array)], values);
39722      });
39723    });
39724
39725    /**
39726     * Creates a clone of the chained sequence planting `value` as the wrapped value.
39727     *
39728     * @name plant
39729     * @memberOf _
39730     * @category Chain
39731     * @returns {Object} Returns the new `lodash` wrapper instance.
39732     * @example
39733     *
39734     * var array = [1, 2];
39735     * var wrapped = _(array).map(function(value) {
39736     *   return Math.pow(value, 2);
39737     * });
39738     *
39739     * var other = [3, 4];
39740     * var otherWrapped = wrapped.plant(other);
39741     *
39742     * otherWrapped.value();
39743     * // => [9, 16]
39744     *
39745     * wrapped.value();
39746     * // => [1, 4]
39747     */
39748    function wrapperPlant(value) {
39749      var result,
39750          parent = this;
39751
39752      while (parent instanceof baseLodash) {
39753        var clone = wrapperClone(parent);
39754        if (result) {
39755          previous.__wrapped__ = clone;
39756        } else {
39757          result = clone;
39758        }
39759        var previous = clone;
39760        parent = parent.__wrapped__;
39761      }
39762      previous.__wrapped__ = value;
39763      return result;
39764    }
39765
39766    /**
39767     * Reverses the wrapped array so the first element becomes the last, the
39768     * second element becomes the second to last, and so on.
39769     *
39770     * **Note:** This method mutates the wrapped array.
39771     *
39772     * @name reverse
39773     * @memberOf _
39774     * @category Chain
39775     * @returns {Object} Returns the new reversed `lodash` wrapper instance.
39776     * @example
39777     *
39778     * var array = [1, 2, 3];
39779     *
39780     * _(array).reverse().value()
39781     * // => [3, 2, 1]
39782     *
39783     * console.log(array);
39784     * // => [3, 2, 1]
39785     */
39786    function wrapperReverse() {
39787      var value = this.__wrapped__;
39788
39789      var interceptor = function(value) {
39790        return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse();
39791      };
39792      if (value instanceof LazyWrapper) {
39793        var wrapped = value;
39794        if (this.__actions__.length) {
39795          wrapped = new LazyWrapper(this);
39796        }
39797        wrapped = wrapped.reverse();
39798        wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
39799        return new LodashWrapper(wrapped, this.__chain__);
39800      }
39801      return this.thru(interceptor);
39802    }
39803
39804    /**
39805     * Produces the result of coercing the unwrapped value to a string.
39806     *
39807     * @name toString
39808     * @memberOf _
39809     * @category Chain
39810     * @returns {string} Returns the coerced string value.
39811     * @example
39812     *
39813     * _([1, 2, 3]).toString();
39814     * // => '1,2,3'
39815     */
39816    function wrapperToString() {
39817      return (this.value() + '');
39818    }
39819
39820    /**
39821     * Executes the chained sequence to extract the unwrapped value.
39822     *
39823     * @name value
39824     * @memberOf _
39825     * @alias run, toJSON, valueOf
39826     * @category Chain
39827     * @returns {*} Returns the resolved unwrapped value.
39828     * @example
39829     *
39830     * _([1, 2, 3]).value();
39831     * // => [1, 2, 3]
39832     */
39833    function wrapperValue() {
39834      return baseWrapperValue(this.__wrapped__, this.__actions__);
39835    }
39836
39837    /*------------------------------------------------------------------------*/
39838
39839    /**
39840     * Creates an array of elements corresponding to the given keys, or indexes,
39841     * of `collection`. Keys may be specified as individual arguments or as arrays
39842     * of keys.
39843     *
39844     * @static
39845     * @memberOf _
39846     * @category Collection
39847     * @param {Array|Object|string} collection The collection to iterate over.
39848     * @param {...(number|number[]|string|string[])} [props] The property names
39849     *  or indexes of elements to pick, specified individually or in arrays.
39850     * @returns {Array} Returns the new array of picked elements.
39851     * @example
39852     *
39853     * _.at(['a', 'b', 'c'], [0, 2]);
39854     * // => ['a', 'c']
39855     *
39856     * _.at(['barney', 'fred', 'pebbles'], 0, 2);
39857     * // => ['barney', 'pebbles']
39858     */
39859    var at = restParam(function(collection, props) {
39860      return baseAt(collection, baseFlatten(props));
39861    });
39862
39863    /**
39864     * Creates an object composed of keys generated from the results of running
39865     * each element of `collection` through `iteratee`. The corresponding value
39866     * of each key is the number of times the key was returned by `iteratee`.
39867     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
39868     * (value, index|key, collection).
39869     *
39870     * If a property name is provided for `iteratee` the created `_.property`
39871     * style callback returns the property value of the given element.
39872     *
39873     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39874     * style callback returns `true` for elements that have a matching property
39875     * value, else `false`.
39876     *
39877     * If an object is provided for `iteratee` the created `_.matches` style
39878     * callback returns `true` for elements that have the properties of the given
39879     * object, else `false`.
39880     *
39881     * @static
39882     * @memberOf _
39883     * @category Collection
39884     * @param {Array|Object|string} collection The collection to iterate over.
39885     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
39886     *  per iteration.
39887     * @param {*} [thisArg] The `this` binding of `iteratee`.
39888     * @returns {Object} Returns the composed aggregate object.
39889     * @example
39890     *
39891     * _.countBy([4.3, 6.1, 6.4], function(n) {
39892     *   return Math.floor(n);
39893     * });
39894     * // => { '4': 1, '6': 2 }
39895     *
39896     * _.countBy([4.3, 6.1, 6.4], function(n) {
39897     *   return this.floor(n);
39898     * }, Math);
39899     * // => { '4': 1, '6': 2 }
39900     *
39901     * _.countBy(['one', 'two', 'three'], 'length');
39902     * // => { '3': 2, '5': 1 }
39903     */
39904    var countBy = createAggregator(function(result, value, key) {
39905      hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1);
39906    });
39907
39908    /**
39909     * Checks if `predicate` returns truthy for **all** elements of `collection`.
39910     * The predicate is bound to `thisArg` and invoked with three arguments:
39911     * (value, index|key, collection).
39912     *
39913     * If a property name is provided for `predicate` the created `_.property`
39914     * style callback returns the property value of the given element.
39915     *
39916     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39917     * style callback returns `true` for elements that have a matching property
39918     * value, else `false`.
39919     *
39920     * If an object is provided for `predicate` the created `_.matches` style
39921     * callback returns `true` for elements that have the properties of the given
39922     * object, else `false`.
39923     *
39924     * @static
39925     * @memberOf _
39926     * @alias all
39927     * @category Collection
39928     * @param {Array|Object|string} collection The collection to iterate over.
39929     * @param {Function|Object|string} [predicate=_.identity] The function invoked
39930     *  per iteration.
39931     * @param {*} [thisArg] The `this` binding of `predicate`.
39932     * @returns {boolean} Returns `true` if all elements pass the predicate check,
39933     *  else `false`.
39934     * @example
39935     *
39936     * _.every([true, 1, null, 'yes'], Boolean);
39937     * // => false
39938     *
39939     * var users = [
39940     *   { 'user': 'barney', 'active': false },
39941     *   { 'user': 'fred',   'active': false }
39942     * ];
39943     *
39944     * // using the `_.matches` callback shorthand
39945     * _.every(users, { 'user': 'barney', 'active': false });
39946     * // => false
39947     *
39948     * // using the `_.matchesProperty` callback shorthand
39949     * _.every(users, 'active', false);
39950     * // => true
39951     *
39952     * // using the `_.property` callback shorthand
39953     * _.every(users, 'active');
39954     * // => false
39955     */
39956    function every(collection, predicate, thisArg) {
39957      var func = isArray(collection) ? arrayEvery : baseEvery;
39958      if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
39959        predicate = undefined;
39960      }
39961      if (typeof predicate != 'function' || thisArg !== undefined) {
39962        predicate = getCallback(predicate, thisArg, 3);
39963      }
39964      return func(collection, predicate);
39965    }
39966
39967    /**
39968     * Iterates over elements of `collection`, returning an array of all elements
39969     * `predicate` returns truthy for. The predicate is bound to `thisArg` and
39970     * invoked with three arguments: (value, index|key, collection).
39971     *
39972     * If a property name is provided for `predicate` the created `_.property`
39973     * style callback returns the property value of the given element.
39974     *
39975     * If a value is also provided for `thisArg` the created `_.matchesProperty`
39976     * style callback returns `true` for elements that have a matching property
39977     * value, else `false`.
39978     *
39979     * If an object is provided for `predicate` the created `_.matches` style
39980     * callback returns `true` for elements that have the properties of the given
39981     * object, else `false`.
39982     *
39983     * @static
39984     * @memberOf _
39985     * @alias select
39986     * @category Collection
39987     * @param {Array|Object|string} collection The collection to iterate over.
39988     * @param {Function|Object|string} [predicate=_.identity] The function invoked
39989     *  per iteration.
39990     * @param {*} [thisArg] The `this` binding of `predicate`.
39991     * @returns {Array} Returns the new filtered array.
39992     * @example
39993     *
39994     * _.filter([4, 5, 6], function(n) {
39995     *   return n % 2 == 0;
39996     * });
39997     * // => [4, 6]
39998     *
39999     * var users = [
40000     *   { 'user': 'barney', 'age': 36, 'active': true },
40001     *   { 'user': 'fred',   'age': 40, 'active': false }
40002     * ];
40003     *
40004     * // using the `_.matches` callback shorthand
40005     * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
40006     * // => ['barney']
40007     *
40008     * // using the `_.matchesProperty` callback shorthand
40009     * _.pluck(_.filter(users, 'active', false), 'user');
40010     * // => ['fred']
40011     *
40012     * // using the `_.property` callback shorthand
40013     * _.pluck(_.filter(users, 'active'), 'user');
40014     * // => ['barney']
40015     */
40016    function filter(collection, predicate, thisArg) {
40017      var func = isArray(collection) ? arrayFilter : baseFilter;
40018      predicate = getCallback(predicate, thisArg, 3);
40019      return func(collection, predicate);
40020    }
40021
40022    /**
40023     * Iterates over elements of `collection`, returning the first element
40024     * `predicate` returns truthy for. The predicate is bound to `thisArg` and
40025     * invoked with three arguments: (value, index|key, collection).
40026     *
40027     * If a property name is provided for `predicate` the created `_.property`
40028     * style callback returns the property value of the given element.
40029     *
40030     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40031     * style callback returns `true` for elements that have a matching property
40032     * value, else `false`.
40033     *
40034     * If an object is provided for `predicate` the created `_.matches` style
40035     * callback returns `true` for elements that have the properties of the given
40036     * object, else `false`.
40037     *
40038     * @static
40039     * @memberOf _
40040     * @alias detect
40041     * @category Collection
40042     * @param {Array|Object|string} collection The collection to search.
40043     * @param {Function|Object|string} [predicate=_.identity] The function invoked
40044     *  per iteration.
40045     * @param {*} [thisArg] The `this` binding of `predicate`.
40046     * @returns {*} Returns the matched element, else `undefined`.
40047     * @example
40048     *
40049     * var users = [
40050     *   { 'user': 'barney',  'age': 36, 'active': true },
40051     *   { 'user': 'fred',    'age': 40, 'active': false },
40052     *   { 'user': 'pebbles', 'age': 1,  'active': true }
40053     * ];
40054     *
40055     * _.result(_.find(users, function(chr) {
40056     *   return chr.age < 40;
40057     * }), 'user');
40058     * // => 'barney'
40059     *
40060     * // using the `_.matches` callback shorthand
40061     * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
40062     * // => 'pebbles'
40063     *
40064     * // using the `_.matchesProperty` callback shorthand
40065     * _.result(_.find(users, 'active', false), 'user');
40066     * // => 'fred'
40067     *
40068     * // using the `_.property` callback shorthand
40069     * _.result(_.find(users, 'active'), 'user');
40070     * // => 'barney'
40071     */
40072    var find = createFind(baseEach);
40073
40074    /**
40075     * This method is like `_.find` except that it iterates over elements of
40076     * `collection` from right to left.
40077     *
40078     * @static
40079     * @memberOf _
40080     * @category Collection
40081     * @param {Array|Object|string} collection The collection to search.
40082     * @param {Function|Object|string} [predicate=_.identity] The function invoked
40083     *  per iteration.
40084     * @param {*} [thisArg] The `this` binding of `predicate`.
40085     * @returns {*} Returns the matched element, else `undefined`.
40086     * @example
40087     *
40088     * _.findLast([1, 2, 3, 4], function(n) {
40089     *   return n % 2 == 1;
40090     * });
40091     * // => 3
40092     */
40093    var findLast = createFind(baseEachRight, true);
40094
40095    /**
40096     * Performs a deep comparison between each element in `collection` and the
40097     * source object, returning the first element that has equivalent property
40098     * values.
40099     *
40100     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
40101     * numbers, `Object` objects, regexes, and strings. Objects are compared by
40102     * their own, not inherited, enumerable properties. For comparing a single
40103     * own or inherited property value see `_.matchesProperty`.
40104     *
40105     * @static
40106     * @memberOf _
40107     * @category Collection
40108     * @param {Array|Object|string} collection The collection to search.
40109     * @param {Object} source The object of property values to match.
40110     * @returns {*} Returns the matched element, else `undefined`.
40111     * @example
40112     *
40113     * var users = [
40114     *   { 'user': 'barney', 'age': 36, 'active': true },
40115     *   { 'user': 'fred',   'age': 40, 'active': false }
40116     * ];
40117     *
40118     * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user');
40119     * // => 'barney'
40120     *
40121     * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user');
40122     * // => 'fred'
40123     */
40124    function findWhere(collection, source) {
40125      return find(collection, baseMatches(source));
40126    }
40127
40128    /**
40129     * Iterates over elements of `collection` invoking `iteratee` for each element.
40130     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
40131     * (value, index|key, collection). Iteratee functions may exit iteration early
40132     * by explicitly returning `false`.
40133     *
40134     * **Note:** As with other "Collections" methods, objects with a "length" property
40135     * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
40136     * may be used for object iteration.
40137     *
40138     * @static
40139     * @memberOf _
40140     * @alias each
40141     * @category Collection
40142     * @param {Array|Object|string} collection The collection to iterate over.
40143     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
40144     * @param {*} [thisArg] The `this` binding of `iteratee`.
40145     * @returns {Array|Object|string} Returns `collection`.
40146     * @example
40147     *
40148     * _([1, 2]).forEach(function(n) {
40149     *   console.log(n);
40150     * }).value();
40151     * // => logs each value from left to right and returns the array
40152     *
40153     * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
40154     *   console.log(n, key);
40155     * });
40156     * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
40157     */
40158    var forEach = createForEach(arrayEach, baseEach);
40159
40160    /**
40161     * This method is like `_.forEach` except that it iterates over elements of
40162     * `collection` from right to left.
40163     *
40164     * @static
40165     * @memberOf _
40166     * @alias eachRight
40167     * @category Collection
40168     * @param {Array|Object|string} collection The collection to iterate over.
40169     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
40170     * @param {*} [thisArg] The `this` binding of `iteratee`.
40171     * @returns {Array|Object|string} Returns `collection`.
40172     * @example
40173     *
40174     * _([1, 2]).forEachRight(function(n) {
40175     *   console.log(n);
40176     * }).value();
40177     * // => logs each value from right to left and returns the array
40178     */
40179    var forEachRight = createForEach(arrayEachRight, baseEachRight);
40180
40181    /**
40182     * Creates an object composed of keys generated from the results of running
40183     * each element of `collection` through `iteratee`. The corresponding value
40184     * of each key is an array of the elements responsible for generating the key.
40185     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
40186     * (value, index|key, collection).
40187     *
40188     * If a property name is provided for `iteratee` the created `_.property`
40189     * style callback returns the property value of the given element.
40190     *
40191     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40192     * style callback returns `true` for elements that have a matching property
40193     * value, else `false`.
40194     *
40195     * If an object is provided for `iteratee` the created `_.matches` style
40196     * callback returns `true` for elements that have the properties of the given
40197     * object, else `false`.
40198     *
40199     * @static
40200     * @memberOf _
40201     * @category Collection
40202     * @param {Array|Object|string} collection The collection to iterate over.
40203     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
40204     *  per iteration.
40205     * @param {*} [thisArg] The `this` binding of `iteratee`.
40206     * @returns {Object} Returns the composed aggregate object.
40207     * @example
40208     *
40209     * _.groupBy([4.2, 6.1, 6.4], function(n) {
40210     *   return Math.floor(n);
40211     * });
40212     * // => { '4': [4.2], '6': [6.1, 6.4] }
40213     *
40214     * _.groupBy([4.2, 6.1, 6.4], function(n) {
40215     *   return this.floor(n);
40216     * }, Math);
40217     * // => { '4': [4.2], '6': [6.1, 6.4] }
40218     *
40219     * // using the `_.property` callback shorthand
40220     * _.groupBy(['one', 'two', 'three'], 'length');
40221     * // => { '3': ['one', 'two'], '5': ['three'] }
40222     */
40223    var groupBy = createAggregator(function(result, value, key) {
40224      if (hasOwnProperty.call(result, key)) {
40225        result[key].push(value);
40226      } else {
40227        result[key] = [value];
40228      }
40229    });
40230
40231    /**
40232     * Checks if `value` is in `collection` using
40233     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
40234     * for equality comparisons. If `fromIndex` is negative, it is used as the offset
40235     * from the end of `collection`.
40236     *
40237     * @static
40238     * @memberOf _
40239     * @alias contains, include
40240     * @category Collection
40241     * @param {Array|Object|string} collection The collection to search.
40242     * @param {*} target The value to search for.
40243     * @param {number} [fromIndex=0] The index to search from.
40244     * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
40245     * @returns {boolean} Returns `true` if a matching element is found, else `false`.
40246     * @example
40247     *
40248     * _.includes([1, 2, 3], 1);
40249     * // => true
40250     *
40251     * _.includes([1, 2, 3], 1, 2);
40252     * // => false
40253     *
40254     * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
40255     * // => true
40256     *
40257     * _.includes('pebbles', 'eb');
40258     * // => true
40259     */
40260    function includes(collection, target, fromIndex, guard) {
40261      var length = collection ? getLength(collection) : 0;
40262      if (!isLength(length)) {
40263        collection = values(collection);
40264        length = collection.length;
40265      }
40266      if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
40267        fromIndex = 0;
40268      } else {
40269        fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
40270      }
40271      return (typeof collection == 'string' || !isArray(collection) && isString(collection))
40272        ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1)
40273        : (!!length && getIndexOf(collection, target, fromIndex) > -1);
40274    }
40275
40276    /**
40277     * Creates an object composed of keys generated from the results of running
40278     * each element of `collection` through `iteratee`. The corresponding value
40279     * of each key is the last element responsible for generating the key. The
40280     * iteratee function is bound to `thisArg` and invoked with three arguments:
40281     * (value, index|key, collection).
40282     *
40283     * If a property name is provided for `iteratee` the created `_.property`
40284     * style callback returns the property value of the given element.
40285     *
40286     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40287     * style callback returns `true` for elements that have a matching property
40288     * value, else `false`.
40289     *
40290     * If an object is provided for `iteratee` the created `_.matches` style
40291     * callback returns `true` for elements that have the properties of the given
40292     * object, else `false`.
40293     *
40294     * @static
40295     * @memberOf _
40296     * @category Collection
40297     * @param {Array|Object|string} collection The collection to iterate over.
40298     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
40299     *  per iteration.
40300     * @param {*} [thisArg] The `this` binding of `iteratee`.
40301     * @returns {Object} Returns the composed aggregate object.
40302     * @example
40303     *
40304     * var keyData = [
40305     *   { 'dir': 'left', 'code': 97 },
40306     *   { 'dir': 'right', 'code': 100 }
40307     * ];
40308     *
40309     * _.indexBy(keyData, 'dir');
40310     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
40311     *
40312     * _.indexBy(keyData, function(object) {
40313     *   return String.fromCharCode(object.code);
40314     * });
40315     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
40316     *
40317     * _.indexBy(keyData, function(object) {
40318     *   return this.fromCharCode(object.code);
40319     * }, String);
40320     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
40321     */
40322    var indexBy = createAggregator(function(result, value, key) {
40323      result[key] = value;
40324    });
40325
40326    /**
40327     * Invokes the method at `path` of each element in `collection`, returning
40328     * an array of the results of each invoked method. Any additional arguments
40329     * are provided to each invoked method. If `methodName` is a function it is
40330     * invoked for, and `this` bound to, each element in `collection`.
40331     *
40332     * @static
40333     * @memberOf _
40334     * @category Collection
40335     * @param {Array|Object|string} collection The collection to iterate over.
40336     * @param {Array|Function|string} path The path of the method to invoke or
40337     *  the function invoked per iteration.
40338     * @param {...*} [args] The arguments to invoke the method with.
40339     * @returns {Array} Returns the array of results.
40340     * @example
40341     *
40342     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
40343     * // => [[1, 5, 7], [1, 2, 3]]
40344     *
40345     * _.invoke([123, 456], String.prototype.split, '');
40346     * // => [['1', '2', '3'], ['4', '5', '6']]
40347     */
40348    var invoke = restParam(function(collection, path, args) {
40349      var index = -1,
40350          isFunc = typeof path == 'function',
40351          isProp = isKey(path),
40352          result = isArrayLike(collection) ? Array(collection.length) : [];
40353
40354      baseEach(collection, function(value) {
40355        var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined);
40356        result[++index] = func ? func.apply(value, args) : invokePath(value, path, args);
40357      });
40358      return result;
40359    });
40360
40361    /**
40362     * Creates an array of values by running each element in `collection` through
40363     * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
40364     * arguments: (value, index|key, collection).
40365     *
40366     * If a property name is provided for `iteratee` the created `_.property`
40367     * style callback returns the property value of the given element.
40368     *
40369     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40370     * style callback returns `true` for elements that have a matching property
40371     * value, else `false`.
40372     *
40373     * If an object is provided for `iteratee` the created `_.matches` style
40374     * callback returns `true` for elements that have the properties of the given
40375     * object, else `false`.
40376     *
40377     * Many lodash methods are guarded to work as iteratees for methods like
40378     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
40379     *
40380     * The guarded methods are:
40381     * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
40382     * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
40383     * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
40384     * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
40385     * `sum`, `uniq`, and `words`
40386     *
40387     * @static
40388     * @memberOf _
40389     * @alias collect
40390     * @category Collection
40391     * @param {Array|Object|string} collection The collection to iterate over.
40392     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
40393     *  per iteration.
40394     * @param {*} [thisArg] The `this` binding of `iteratee`.
40395     * @returns {Array} Returns the new mapped array.
40396     * @example
40397     *
40398     * function timesThree(n) {
40399     *   return n * 3;
40400     * }
40401     *
40402     * _.map([1, 2], timesThree);
40403     * // => [3, 6]
40404     *
40405     * _.map({ 'a': 1, 'b': 2 }, timesThree);
40406     * // => [3, 6] (iteration order is not guaranteed)
40407     *
40408     * var users = [
40409     *   { 'user': 'barney' },
40410     *   { 'user': 'fred' }
40411     * ];
40412     *
40413     * // using the `_.property` callback shorthand
40414     * _.map(users, 'user');
40415     * // => ['barney', 'fred']
40416     */
40417    function map(collection, iteratee, thisArg) {
40418      var func = isArray(collection) ? arrayMap : baseMap;
40419      iteratee = getCallback(iteratee, thisArg, 3);
40420      return func(collection, iteratee);
40421    }
40422
40423    /**
40424     * Creates an array of elements split into two groups, the first of which
40425     * contains elements `predicate` returns truthy for, while the second of which
40426     * contains elements `predicate` returns falsey for. The predicate is bound
40427     * to `thisArg` and invoked with three arguments: (value, index|key, collection).
40428     *
40429     * If a property name is provided for `predicate` the created `_.property`
40430     * style callback returns the property value of the given element.
40431     *
40432     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40433     * style callback returns `true` for elements that have a matching property
40434     * value, else `false`.
40435     *
40436     * If an object is provided for `predicate` the created `_.matches` style
40437     * callback returns `true` for elements that have the properties of the given
40438     * object, else `false`.
40439     *
40440     * @static
40441     * @memberOf _
40442     * @category Collection
40443     * @param {Array|Object|string} collection The collection to iterate over.
40444     * @param {Function|Object|string} [predicate=_.identity] The function invoked
40445     *  per iteration.
40446     * @param {*} [thisArg] The `this` binding of `predicate`.
40447     * @returns {Array} Returns the array of grouped elements.
40448     * @example
40449     *
40450     * _.partition([1, 2, 3], function(n) {
40451     *   return n % 2;
40452     * });
40453     * // => [[1, 3], [2]]
40454     *
40455     * _.partition([1.2, 2.3, 3.4], function(n) {
40456     *   return this.floor(n) % 2;
40457     * }, Math);
40458     * // => [[1.2, 3.4], [2.3]]
40459     *
40460     * var users = [
40461     *   { 'user': 'barney',  'age': 36, 'active': false },
40462     *   { 'user': 'fred',    'age': 40, 'active': true },
40463     *   { 'user': 'pebbles', 'age': 1,  'active': false }
40464     * ];
40465     *
40466     * var mapper = function(array) {
40467     *   return _.pluck(array, 'user');
40468     * };
40469     *
40470     * // using the `_.matches` callback shorthand
40471     * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper);
40472     * // => [['pebbles'], ['barney', 'fred']]
40473     *
40474     * // using the `_.matchesProperty` callback shorthand
40475     * _.map(_.partition(users, 'active', false), mapper);
40476     * // => [['barney', 'pebbles'], ['fred']]
40477     *
40478     * // using the `_.property` callback shorthand
40479     * _.map(_.partition(users, 'active'), mapper);
40480     * // => [['fred'], ['barney', 'pebbles']]
40481     */
40482    var partition = createAggregator(function(result, value, key) {
40483      result[key ? 0 : 1].push(value);
40484    }, function() { return [[], []]; });
40485
40486    /**
40487     * Gets the property value of `path` from all elements in `collection`.
40488     *
40489     * @static
40490     * @memberOf _
40491     * @category Collection
40492     * @param {Array|Object|string} collection The collection to iterate over.
40493     * @param {Array|string} path The path of the property to pluck.
40494     * @returns {Array} Returns the property values.
40495     * @example
40496     *
40497     * var users = [
40498     *   { 'user': 'barney', 'age': 36 },
40499     *   { 'user': 'fred',   'age': 40 }
40500     * ];
40501     *
40502     * _.pluck(users, 'user');
40503     * // => ['barney', 'fred']
40504     *
40505     * var userIndex = _.indexBy(users, 'user');
40506     * _.pluck(userIndex, 'age');
40507     * // => [36, 40] (iteration order is not guaranteed)
40508     */
40509    function pluck(collection, path) {
40510      return map(collection, property(path));
40511    }
40512
40513    /**
40514     * Reduces `collection` to a value which is the accumulated result of running
40515     * each element in `collection` through `iteratee`, where each successive
40516     * invocation is supplied the return value of the previous. If `accumulator`
40517     * is not provided the first element of `collection` is used as the initial
40518     * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
40519     * (accumulator, value, index|key, collection).
40520     *
40521     * Many lodash methods are guarded to work as iteratees for methods like
40522     * `_.reduce`, `_.reduceRight`, and `_.transform`.
40523     *
40524     * The guarded methods are:
40525     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`,
40526     * and `sortByOrder`
40527     *
40528     * @static
40529     * @memberOf _
40530     * @alias foldl, inject
40531     * @category Collection
40532     * @param {Array|Object|string} collection The collection to iterate over.
40533     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
40534     * @param {*} [accumulator] The initial value.
40535     * @param {*} [thisArg] The `this` binding of `iteratee`.
40536     * @returns {*} Returns the accumulated value.
40537     * @example
40538     *
40539     * _.reduce([1, 2], function(total, n) {
40540     *   return total + n;
40541     * });
40542     * // => 3
40543     *
40544     * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
40545     *   result[key] = n * 3;
40546     *   return result;
40547     * }, {});
40548     * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
40549     */
40550    var reduce = createReduce(arrayReduce, baseEach);
40551
40552    /**
40553     * This method is like `_.reduce` except that it iterates over elements of
40554     * `collection` from right to left.
40555     *
40556     * @static
40557     * @memberOf _
40558     * @alias foldr
40559     * @category Collection
40560     * @param {Array|Object|string} collection The collection to iterate over.
40561     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
40562     * @param {*} [accumulator] The initial value.
40563     * @param {*} [thisArg] The `this` binding of `iteratee`.
40564     * @returns {*} Returns the accumulated value.
40565     * @example
40566     *
40567     * var array = [[0, 1], [2, 3], [4, 5]];
40568     *
40569     * _.reduceRight(array, function(flattened, other) {
40570     *   return flattened.concat(other);
40571     * }, []);
40572     * // => [4, 5, 2, 3, 0, 1]
40573     */
40574    var reduceRight = createReduce(arrayReduceRight, baseEachRight);
40575
40576    /**
40577     * The opposite of `_.filter`; this method returns the elements of `collection`
40578     * that `predicate` does **not** return truthy for.
40579     *
40580     * @static
40581     * @memberOf _
40582     * @category Collection
40583     * @param {Array|Object|string} collection The collection to iterate over.
40584     * @param {Function|Object|string} [predicate=_.identity] The function invoked
40585     *  per iteration.
40586     * @param {*} [thisArg] The `this` binding of `predicate`.
40587     * @returns {Array} Returns the new filtered array.
40588     * @example
40589     *
40590     * _.reject([1, 2, 3, 4], function(n) {
40591     *   return n % 2 == 0;
40592     * });
40593     * // => [1, 3]
40594     *
40595     * var users = [
40596     *   { 'user': 'barney', 'age': 36, 'active': false },
40597     *   { 'user': 'fred',   'age': 40, 'active': true }
40598     * ];
40599     *
40600     * // using the `_.matches` callback shorthand
40601     * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
40602     * // => ['barney']
40603     *
40604     * // using the `_.matchesProperty` callback shorthand
40605     * _.pluck(_.reject(users, 'active', false), 'user');
40606     * // => ['fred']
40607     *
40608     * // using the `_.property` callback shorthand
40609     * _.pluck(_.reject(users, 'active'), 'user');
40610     * // => ['barney']
40611     */
40612    function reject(collection, predicate, thisArg) {
40613      var func = isArray(collection) ? arrayFilter : baseFilter;
40614      predicate = getCallback(predicate, thisArg, 3);
40615      return func(collection, function(value, index, collection) {
40616        return !predicate(value, index, collection);
40617      });
40618    }
40619
40620    /**
40621     * Gets a random element or `n` random elements from a collection.
40622     *
40623     * @static
40624     * @memberOf _
40625     * @category Collection
40626     * @param {Array|Object|string} collection The collection to sample.
40627     * @param {number} [n] The number of elements to sample.
40628     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
40629     * @returns {*} Returns the random sample(s).
40630     * @example
40631     *
40632     * _.sample([1, 2, 3, 4]);
40633     * // => 2
40634     *
40635     * _.sample([1, 2, 3, 4], 2);
40636     * // => [3, 1]
40637     */
40638    function sample(collection, n, guard) {
40639      if (guard ? isIterateeCall(collection, n, guard) : n == null) {
40640        collection = toIterable(collection);
40641        var length = collection.length;
40642        return length > 0 ? collection[baseRandom(0, length - 1)] : undefined;
40643      }
40644      var index = -1,
40645          result = toArray(collection),
40646          length = result.length,
40647          lastIndex = length - 1;
40648
40649      n = nativeMin(n < 0 ? 0 : (+n || 0), length);
40650      while (++index < n) {
40651        var rand = baseRandom(index, lastIndex),
40652            value = result[rand];
40653
40654        result[rand] = result[index];
40655        result[index] = value;
40656      }
40657      result.length = n;
40658      return result;
40659    }
40660
40661    /**
40662     * Creates an array of shuffled values, using a version of the
40663     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
40664     *
40665     * @static
40666     * @memberOf _
40667     * @category Collection
40668     * @param {Array|Object|string} collection The collection to shuffle.
40669     * @returns {Array} Returns the new shuffled array.
40670     * @example
40671     *
40672     * _.shuffle([1, 2, 3, 4]);
40673     * // => [4, 1, 3, 2]
40674     */
40675    function shuffle(collection) {
40676      return sample(collection, POSITIVE_INFINITY);
40677    }
40678
40679    /**
40680     * Gets the size of `collection` by returning its length for array-like
40681     * values or the number of own enumerable properties for objects.
40682     *
40683     * @static
40684     * @memberOf _
40685     * @category Collection
40686     * @param {Array|Object|string} collection The collection to inspect.
40687     * @returns {number} Returns the size of `collection`.
40688     * @example
40689     *
40690     * _.size([1, 2, 3]);
40691     * // => 3
40692     *
40693     * _.size({ 'a': 1, 'b': 2 });
40694     * // => 2
40695     *
40696     * _.size('pebbles');
40697     * // => 7
40698     */
40699    function size(collection) {
40700      var length = collection ? getLength(collection) : 0;
40701      return isLength(length) ? length : keys(collection).length;
40702    }
40703
40704    /**
40705     * Checks if `predicate` returns truthy for **any** element of `collection`.
40706     * The function returns as soon as it finds a passing value and does not iterate
40707     * over the entire collection. The predicate is bound to `thisArg` and invoked
40708     * with three arguments: (value, index|key, collection).
40709     *
40710     * If a property name is provided for `predicate` the created `_.property`
40711     * style callback returns the property value of the given element.
40712     *
40713     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40714     * style callback returns `true` for elements that have a matching property
40715     * value, else `false`.
40716     *
40717     * If an object is provided for `predicate` the created `_.matches` style
40718     * callback returns `true` for elements that have the properties of the given
40719     * object, else `false`.
40720     *
40721     * @static
40722     * @memberOf _
40723     * @alias any
40724     * @category Collection
40725     * @param {Array|Object|string} collection The collection to iterate over.
40726     * @param {Function|Object|string} [predicate=_.identity] The function invoked
40727     *  per iteration.
40728     * @param {*} [thisArg] The `this` binding of `predicate`.
40729     * @returns {boolean} Returns `true` if any element passes the predicate check,
40730     *  else `false`.
40731     * @example
40732     *
40733     * _.some([null, 0, 'yes', false], Boolean);
40734     * // => true
40735     *
40736     * var users = [
40737     *   { 'user': 'barney', 'active': true },
40738     *   { 'user': 'fred',   'active': false }
40739     * ];
40740     *
40741     * // using the `_.matches` callback shorthand
40742     * _.some(users, { 'user': 'barney', 'active': false });
40743     * // => false
40744     *
40745     * // using the `_.matchesProperty` callback shorthand
40746     * _.some(users, 'active', false);
40747     * // => true
40748     *
40749     * // using the `_.property` callback shorthand
40750     * _.some(users, 'active');
40751     * // => true
40752     */
40753    function some(collection, predicate, thisArg) {
40754      var func = isArray(collection) ? arraySome : baseSome;
40755      if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
40756        predicate = undefined;
40757      }
40758      if (typeof predicate != 'function' || thisArg !== undefined) {
40759        predicate = getCallback(predicate, thisArg, 3);
40760      }
40761      return func(collection, predicate);
40762    }
40763
40764    /**
40765     * Creates an array of elements, sorted in ascending order by the results of
40766     * running each element in a collection through `iteratee`. This method performs
40767     * a stable sort, that is, it preserves the original sort order of equal elements.
40768     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
40769     * (value, index|key, collection).
40770     *
40771     * If a property name is provided for `iteratee` the created `_.property`
40772     * style callback returns the property value of the given element.
40773     *
40774     * If a value is also provided for `thisArg` the created `_.matchesProperty`
40775     * style callback returns `true` for elements that have a matching property
40776     * value, else `false`.
40777     *
40778     * If an object is provided for `iteratee` the created `_.matches` style
40779     * callback returns `true` for elements that have the properties of the given
40780     * object, else `false`.
40781     *
40782     * @static
40783     * @memberOf _
40784     * @category Collection
40785     * @param {Array|Object|string} collection The collection to iterate over.
40786     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
40787     *  per iteration.
40788     * @param {*} [thisArg] The `this` binding of `iteratee`.
40789     * @returns {Array} Returns the new sorted array.
40790     * @example
40791     *
40792     * _.sortBy([1, 2, 3], function(n) {
40793     *   return Math.sin(n);
40794     * });
40795     * // => [3, 1, 2]
40796     *
40797     * _.sortBy([1, 2, 3], function(n) {
40798     *   return this.sin(n);
40799     * }, Math);
40800     * // => [3, 1, 2]
40801     *
40802     * var users = [
40803     *   { 'user': 'fred' },
40804     *   { 'user': 'pebbles' },
40805     *   { 'user': 'barney' }
40806     * ];
40807     *
40808     * // using the `_.property` callback shorthand
40809     * _.pluck(_.sortBy(users, 'user'), 'user');
40810     * // => ['barney', 'fred', 'pebbles']
40811     */
40812    function sortBy(collection, iteratee, thisArg) {
40813      if (collection == null) {
40814        return [];
40815      }
40816      if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
40817        iteratee = undefined;
40818      }
40819      var index = -1;
40820      iteratee = getCallback(iteratee, thisArg, 3);
40821
40822      var result = baseMap(collection, function(value, key, collection) {
40823        return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value };
40824      });
40825      return baseSortBy(result, compareAscending);
40826    }
40827
40828    /**
40829     * This method is like `_.sortBy` except that it can sort by multiple iteratees
40830     * or property names.
40831     *
40832     * If a property name is provided for an iteratee the created `_.property`
40833     * style callback returns the property value of the given element.
40834     *
40835     * If an object is provided for an iteratee the created `_.matches` style
40836     * callback returns `true` for elements that have the properties of the given
40837     * object, else `false`.
40838     *
40839     * @static
40840     * @memberOf _
40841     * @category Collection
40842     * @param {Array|Object|string} collection The collection to iterate over.
40843     * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees
40844     *  The iteratees to sort by, specified as individual values or arrays of values.
40845     * @returns {Array} Returns the new sorted array.
40846     * @example
40847     *
40848     * var users = [
40849     *   { 'user': 'fred',   'age': 48 },
40850     *   { 'user': 'barney', 'age': 36 },
40851     *   { 'user': 'fred',   'age': 42 },
40852     *   { 'user': 'barney', 'age': 34 }
40853     * ];
40854     *
40855     * _.map(_.sortByAll(users, ['user', 'age']), _.values);
40856     * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]
40857     *
40858     * _.map(_.sortByAll(users, 'user', function(chr) {
40859     *   return Math.floor(chr.age / 10);
40860     * }), _.values);
40861     * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
40862     */
40863    var sortByAll = restParam(function(collection, iteratees) {
40864      if (collection == null) {
40865        return [];
40866      }
40867      var guard = iteratees[2];
40868      if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) {
40869        iteratees.length = 1;
40870      }
40871      return baseSortByOrder(collection, baseFlatten(iteratees), []);
40872    });
40873
40874    /**
40875     * This method is like `_.sortByAll` except that it allows specifying the
40876     * sort orders of the iteratees to sort by. If `orders` is unspecified, all
40877     * values are sorted in ascending order. Otherwise, a value is sorted in
40878     * ascending order if its corresponding order is "asc", and desce
40878nding if "desc".
40879     *
40880     * If a property name is provided for an iteratee the created `_.property`
40881     * style callback returns the property value of the given element.
40882     *
40883     * If an object is provided for an iteratee the created `_.matches` style
40884     * callback returns `true` for elements that have the properties of the given
40885     * object, else `false`.
40886     *
40887     * @static
40888     * @memberOf _
40889     * @category Collection
40890     * @param {Array|Object|string} collection The collection to iterate over.
40891     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
40892     * @param {boolean[]} [orders] The sort orders of `iteratees`.
40893     * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
40894     * @returns {Array} Returns the new sorted array.
40895     * @example
40896     *
40897     * var users = [
40898     *   { 'user': 'fred',   'age': 48 },
40899     *   { 'user': 'barney', 'age': 34 },
40900     *   { 'user': 'fred',   'age': 42 },
40901     *   { 'user': 'barney', 'age': 36 }
40902     * ];
40903     *
40904     * // sort by `user` in ascending order and by `age` in descending order
40905     * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values);
40906     * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
40907     */
40908    function sortByOrder(collection, iteratees, orders, guard) {
40909      if (collection == null) {
40910        return [];
40911      }
40912      if (guard && isIterateeCall(iteratees, orders, guard)) {
40913        orders = undefined;
40914      }
40915      if (!isArray(iteratees)) {
40916        iteratees = iteratees == null ? [] : [iteratees];
40917      }
40918      if (!isArray(orders)) {
40919        orders = orders == null ? [] : [orders];
40920      }
40921      return baseSortByOrder(collection, iteratees, orders);
40922    }
40923
40924    /**
40925     * Performs a deep comparison between each element in `collection` and the
40926     * source object, returning an array of all elements that have equivalent
40927     * property values.
40928     *
40929     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
40930     * numbers, `Object` objects, regexes, and strings. Objects are compared by
40931     * their own, not inherited, enumerable properties. For comparing a single
40932     * own or inherited property value see `_.matchesProperty`.
40933     *
40934     * @static
40935     * @memberOf _
40936     * @category Collection
40937     * @param {Array|Object|string} collection The collection to search.
40938     * @param {Object} source The object of property values to match.
40939     * @returns {Array} Returns the new filtered array.
40940     * @example
40941     *
40942     * var users = [
40943     *   { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] },
40944     *   { 'user': 'fred',   'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] }
40945     * ];
40946     *
40947     * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user');
40948     * // => ['barney']
40949     *
40950     * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user');
40951     * // => ['fred']
40952     */
40953    function where(collection, source) {
40954      return filter(collection, baseMatches(source));
40955    }
40956
40957    /*------------------------------------------------------------------------*/
40958
40959    /**
40960     * Gets the number of milliseconds that have elapsed since the Unix epoch
40961     * (1 January 1970 00:00:00 UTC).
40962     *
40963     * @static
40964     * @memberOf _
40965     * @category Date
40966     * @example
40967     *
40968     * _.defer(function(stamp) {
40969     *   console.log(_.now() - stamp);
40970     * }, _.now());
40971     * // => logs the number of milliseconds it took for the deferred function to be invoked
40972     */
40973    var now = nativeNow || function() {
40974      return new Date().getTime();
40975    };
40976
40977    /*------------------------------------------------------------------------*/
40978
40979    /**
40980     * The opposite of `_.before`; this method creates a function that invokes
40981     * `func` once it is called `n` or more times.
40982     *
40983     * @static
40984     * @memberOf _
40985     * @category Function
40986     * @param {number} n The number of calls before `func` is invoked.
40987     * @param {Function} func The function to restrict.
40988     * @returns {Function} Returns the new restricted function.
40989     * @example
40990     *
40991     * var saves = ['profile', 'settings'];
40992     *
40993     * var done = _.after(saves.length, function() {
40994     *   console.log('done saving!');
40995     * });
40996     *
40997     * _.forEach(saves, function(type) {
40998     *   asyncSave({ 'type': type, 'complete': done });
40999     * });
41000     * // => logs 'done saving!' after the two async saves have completed
41001     */
41002    function after(n, func) {
41003      if (typeof func != 'function') {
41004        if (typeof n == 'function') {
41005          var temp = n;
41006          n = func;
41007          func = temp;
41008        } else {
41009          throw new TypeError(FUNC_ERROR_TEXT);
41010        }
41011      }
41012      n = nativeIsFinite(n = +n) ? n : 0;
41013      return function() {
41014        if (--n < 1) {
41015          return func.apply(this, arguments);
41016        }
41017      };
41018    }
41019
41020    /**
41021     * Creates a function that accepts up to `n` arguments ignoring any
41022     * additional arguments.
41023     *
41024     * @static
41025     * @memberOf _
41026     * @category Function
41027     * @param {Function} func The function to cap arguments for.
41028     * @param {number} [n=func.length] The arity cap.
41029     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
41030     * @returns {Function} Returns the new function.
41031     * @example
41032     *
41033     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
41034     * // => [6, 8, 10]
41035     */
41036    function ary(func, n, guard) {
41037      if (guard && isIterateeCall(func, n, guard)) {
41038        n = undefined;
41039      }
41040      n = (func && n == null) ? func.length : nativeMax(+n || 0, 0);
41041      return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n);
41042    }
41043
41044    /**
41045     * Creates a function that invokes `func`, with the `this` binding and arguments
41046     * of the created function, while it is called less than `n` times. Subsequent
41047     * calls to the created function return the result of the last `func` invocation.
41048     *
41049     * @static
41050     * @memberOf _
41051     * @category Function
41052     * @param {number} n The number of calls at which `func` is no longer invoked.
41053     * @param {Function} func The function to restrict.
41054     * @returns {Function} Returns the new restricted function.
41055     * @example
41056     *
41057     * jQuery('#add').on('click', _.before(5, addContactToList));
41058     * // => allows adding up to 4 contacts to the list
41059     */
41060    function before(n, func) {
41061      var result;
41062      if (typeof func != 'function') {
41063        if (typeof n == 'function') {
41064          var temp = n;
41065          n = func;
41066          func = temp;
41067        } else {
41068          throw new TypeError(FUNC_ERROR_TEXT);
41069        }
41070      }
41071      return function() {
41072        if (--n > 0) {
41073          result = func.apply(this, arguments);
41074        }
41075        if (n <= 1) {
41076          func = undefined;
41077        }
41078        return result;
41079      };
41080    }
41081
41082    /**
41083     * Creates a function that invokes `func` with the `this` binding of `thisArg`
41084     * and prepends any additional `_.bind` arguments to those provided to the
41085     * bound function.
41086     *
41087     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
41088     * may be used as a placeholder for partially applied arguments.
41089     *
41090     * **Note:** Unlike native `Function#bind` this method does not set the "length"
41091     * property of bound functions.
41092     *
41093     * @static
41094     * @memberOf _
41095     * @category Function
41096     * @param {Function} func The function to bind.
41097     * @param {*} thisArg The `this` binding of `func`.
41098     * @param {...*} [partials] The arguments to be partially applied.
41099     * @returns {Function} Returns the new bound function.
41100     * @example
41101     *
41102     * var greet = function(greeting, punctuation) {
41103     *   return greeting + ' ' + this.user + punctuation;
41104     * };
41105     *
41106     * var object = { 'user': 'fred' };
41107     *
41108     * var bound = _.bind(greet, object, 'hi');
41109     * bound('!');
41110     * // => 'hi fred!'
41111     *
41112     * // using placeholders
41113     * var bound = _.bind(greet, object, _, '!');
41114     * bound('hi');
41115     * // => 'hi fred!'
41116     */
41117    var bind = restParam(function(func, thisArg, partials) {
41118      var bitmask = BIND_FLAG;
41119      if (partials.length) {
41120        var holders = replaceHolders(partials, bind.placeholder);
41121        bitmask |= PARTIAL_FLAG;
41122      }
41123      return createWrapper(func, bitmask, thisArg, partials, holders);
41124    });
41125
41126    /**
41127     * Binds methods of an object to the object itself, overwriting the existing
41128     * method. Method names may be specified as individual arguments or as arrays
41129     * of method names. If no method names are provided all enumerable function
41130     * properties, own and inherited, of `object` are bound.
41131     *
41132     * **Note:** This method does not set the "length" property of bound functions.
41133     *
41134     * @static
41135     * @memberOf _
41136     * @category Function
41137     * @param {Object} object The object to bind and assign the bound methods to.
41138     * @param {...(string|string[])} [methodNames] The object method names to bind,
41139     *  specified as individual method names or arrays of method names.
41140     * @returns {Object} Returns `object`.
41141     * @example
41142     *
41143     * var view = {
41144     *   'label': 'docs',
41145     *   'onClick': function() {
41146     *     console.log('clicked ' + this.label);
41147     *   }
41148     * };
41149     *
41150     * _.bindAll(view);
41151     * jQuery('#docs').on('click', view.onClick);
41152     * // => logs 'clicked docs' when the element is clicked
41153     */
41154    var bindAll = restParam(function(object, methodNames) {
41155      methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object);
41156
41157      var index = -1,
41158          length = methodNames.length;
41159
41160      while (++index < length) {
41161        var key = methodNames[index];
41162        object[key] = createWrapper(object[key], BIND_FLAG, object);
41163      }
41164      return object;
41165    });
41166
41167    /**
41168     * Creates a function that invokes the method at `object[key]` and prepends
41169     * any additional `_.bindKey` arguments to those provided to the bound function.
41170     *
41171     * This method differs from `_.bind` by allowing bound functions to reference
41172     * methods that may be redefined or don't yet exist.
41173     * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
41174     * for more details.
41175     *
41176     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
41177     * builds, may be used as a placeholder for partially applied arguments.
41178     *
41179     * @static
41180     * @memberOf _
41181     * @category Function
41182     * @param {Object} object The object the method belongs to.
41183     * @param {string} key The key of the method.
41184     * @param {...*} [partials] The arguments to be partially applied.
41185     * @returns {Function} Returns the new bound function.
41186     * @example
41187     *
41188     * var object = {
41189     *   'user': 'fred',
41190     *   'greet': function(greeting, punctuation) {
41191     *     return greeting + ' ' + this.user + punctuation;
41192     *   }
41193     * };
41194     *
41195     * var bound = _.bindKey(object, 'greet', 'hi');
41196     * bound('!');
41197     * // => 'hi fred!'
41198     *
41199     * object.greet = function(greeting, punctuation) {
41200     *   return greeting + 'ya ' + this.user + punctuation;
41201     * };
41202     *
41203     * bound('!');
41204     * // => 'hiya fred!'
41205     *
41206     * // using placeholders
41207     * var bound = _.bindKey(object, 'greet', _, '!');
41208     * bound('hi');
41209     * // => 'hiya fred!'
41210     */
41211    var bindKey = restParam(function(object, key, partials) {
41212      var bitmask = BIND_FLAG | BIND_KEY_FLAG;
41213      if (partials.length) {
41214        var holders = replaceHolders(partials, bindKey.placeholder);
41215        bitmask |= PARTIAL_FLAG;
41216      }
41217      return createWrapper(key, bitmask, object, partials, holders);
41218    });
41219
41220    /**
41221     * Creates a function that accepts one or more arguments of `func` that when
41222     * called either invokes `func` returning its result, if all `func` arguments
41223     * have been provided, or returns a function that accepts one or more of the
41224     * remaining `func` arguments, and so on. The arity of `func` may be specified
41225     * if `func.length` is not sufficient.
41226     *
41227     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
41228     * may be used as a placeholder for provided arguments.
41229     *
41230     * **Note:** This method does not set the "length" property of curried functions.
41231     *
41232     * @static
41233     * @memberOf _
41234     * @category Function
41235     * @param {Function} func The function to curry.
41236     * @param {number} [arity=func.length] The arity of `func`.
41237     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
41238     * @returns {Function} Returns the new curried function.
41239     * @example
41240     *
41241     * var abc = function(a, b, c) {
41242     *   return [a, b, c];
41243     * };
41244     *
41245     * var curried = _.curry(abc);
41246     *
41247     * curried(1)(2)(3);
41248     * // => [1, 2, 3]
41249     *
41250     * curried(1, 2)(3);
41251     * // => [1, 2, 3]
41252     *
41253     * curried(1, 2, 3);
41254     * // => [1, 2, 3]
41255     *
41256     * // using placeholders
41257     * curried(1)(_, 3)(2);
41258     * // => [1, 2, 3]
41259     */
41260    var curry = createCurry(CURRY_FLAG);
41261
41262    /**
41263     * This method is like `_.curry` except that arguments are applied to `func`
41264     * in the manner of `_.partialRight` instead of `_.partial`.
41265     *
41266     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
41267     * builds, may be used as a placeholder for provided arguments.
41268     *
41269     * **Note:** This method does not set the "length" property of curried functions.
41270     *
41271     * @static
41272     * @memberOf _
41273     * @category Function
41274     * @param {Function} func The function to curry.
41275     * @param {number} [arity=func.length] The arity of `func`.
41276     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
41277     * @returns {Function} Returns the new curried function.
41278     * @example
41279     *
41280     * var abc = function(a, b, c) {
41281     *   return [a, b, c];
41282     * };
41283     *
41284     * var curried = _.curryRight(abc);
41285     *
41286     * curried(3)(2)(1);
41287     * // => [1, 2, 3]
41288     *
41289     * curried(2, 3)(1);
41290     * // => [1, 2, 3]
41291     *
41292     * curried(1, 2, 3);
41293     * // => [1, 2, 3]
41294     *
41295     * // using placeholders
41296     * curried(3)(1, _)(2);
41297     * // => [1, 2, 3]
41298     */
41299    var curryRight = createCurry(CURRY_RIGHT_FLAG);
41300
41301    /**
41302     * Creates a debounced function that delays invoking `func` until after `wait`
41303     * milliseconds have elapsed since the last time the debounced function was
41304     * invoked. The debounced function comes with a `cancel` method to cancel
41305     * delayed invocations. Provide an options object to indicate that `func`
41306     * should be invoked on the leading and/or trailing edge of the `wait` timeout.
41307     * Subsequent calls to the debounced function return the result of the last
41308     * `func` invocation.
41309     *
41310     * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
41311     * on the trailing edge of the timeout only if the the debounced function is
41312     * invoked more than once during the `wait` timeout.
41313     *
41314     * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
41315     * for details over the differences between `_.debounce` and `_.throttle`.
41316     *
41317     * @static
41318     * @memberOf _
41319     * @category Function
41320     * @param {Function} func The function to debounce.
41321     * @param {number} [wait=0] The number of milliseconds to delay.
41322     * @param {Object} [options] The options object.
41323     * @param {boolean} [options.leading=false] Specify invoking on the leading
41324     *  edge of the timeout.
41325     * @param {number} [options.maxWait] The maximum time `func` is allowed to be
41326     *  delayed before it is invoked.
41327     * @param {boolean} [options.trailing=true] Specify invoking on the trailing
41328     *  edge of the timeout.
41329     * @returns {Function} Returns the new debounced function.
41330     * @example
41331     *
41332     * // avoid costly calculations while the window size is in flux
41333     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
41334     *
41335     * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
41336     * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
41337     *   'leading': true,
41338     *   'trailing': false
41339     * }));
41340     *
41341     * // ensure `batchLog` is invoked once after 1 second of debounced calls
41342     * var source = new EventSource('/stream');
41343     * jQuery(source).on('message', _.debounce(batchLog, 250, {
41344     *   'maxWait': 1000
41345     * }));
41346     *
41347     * // cancel a debounced call
41348     * var todoChanges = _.debounce(batchLog, 1000);
41349     * Object.observe(models.todo, todoChanges);
41350     *
41351     * Object.observe(models, function(changes) {
41352     *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
41353     *     todoChanges.cancel();
41354     *   }
41355     * }, ['delete']);
41356     *
41357     * // ...at some point `models.todo` is changed
41358     * models.todo.completed = true;
41359     *
41360     * // ...before 1 second has passed `models.todo` is deleted
41361     * // which cancels the debounced `todoChanges` call
41362     * delete models.todo;
41363     */
41364    function debounce(func, wait, options) {
41365      var args,
41366          maxTimeoutId,
41367          result,
41368          stamp,
41369          thisArg,
41370          timeoutId,
41371          trailingCall,
41372          lastCalled = 0,
41373          maxWait = false,
41374          trailing = true;
41375
41376      if (typeof func != 'function') {
41377        throw new TypeError(FUNC_ERROR_TEXT);
41378      }
41379      wait = wait < 0 ? 0 : (+wait || 0);
41380      if (options === true) {
41381        var leading = true;
41382        trailing = false;
41383      } else if (isObject(options)) {
41384        leading = !!options.leading;
41385        maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
41386        trailing = 'trailing' in options ? !!options.trailing : trailing;
41387      }
41388
41389      function cancel() {
41390        if (timeoutId) {
41391          clearTimeout(timeoutId);
41392        }
41393        if (maxTimeoutId) {
41394          clearTimeout(maxTimeoutId);
41395        }
41396        lastCalled = 0;
41397        maxTimeoutId = timeoutId = trailingCall = undefined;
41398      }
41399
41400      function complete(isCalled, id) {
41401        if (id) {
41402          clearTimeout(id);
41403        }
41404        maxTimeoutId = timeoutId = trailingCall = undefined;
41405        if (isCalled) {
41406          lastCalled = now();
41407          result = func.apply(thisArg, args);
41408          if (!timeoutId && !maxTimeoutId) {
41409            args = thisArg = undefined;
41410          }
41411        }
41412      }
41413
41414      function delayed() {
41415        var remaining = wait - (now() - stamp);
41416        if (remaining <= 0 || remaining > wait) {
41417          complete(trailingCall, maxTimeoutId);
41418        } else {
41419          timeoutId = setTimeout(delayed, remaining);
41420        }
41421      }
41422
41423      function maxDelayed() {
41424        complete(trailing, timeoutId);
41425      }
41426
41427      function debounced() {
41428        args = arguments;
41429        stamp = now();
41430        thisArg = this;
41431        trailingCall = trailing && (timeoutId || !leading);
41432
41433        if (maxWait === false) {
41434          var leadingCall = leading && !timeoutId;
41435        } else {
41436          if (!maxTimeoutId && !leading) {
41437            lastCalled = stamp;
41438          }
41439          var remaining = maxWait - (stamp - lastCalled),
41440              isCalled = remaining <= 0 || remaining > maxWait;
41441
41442          if (isCalled) {
41443            if (maxTimeoutId) {
41444              maxTimeoutId = clearTimeout(maxTimeoutId);
41445            }
41446            lastCalled = stamp;
41447            result = func.apply(thisArg, args);
41448          }
41449          else if (!maxTimeoutId) {
41450            maxTimeoutId = setTimeout(maxDelayed, remaining);
41451          }
41452        }
41453        if (isCalled && timeoutId) {
41454          timeoutId = clearTimeout(timeoutId);
41455        }
41456        else if (!timeoutId && wait !== maxWait) {
41457          timeoutId = setTimeout(delayed, wait);
41458        }
41459        if (leadingCall) {
41460          isCalled = true;
41461          result = func.apply(thisArg, args);
41462        }
41463        if (isCalled && !timeoutId && !maxTimeoutId) {
41464          args = thisArg = undefined;
41465        }
41466        return result;
41467      }
41468      debounced.cancel = cancel;
41469      return debounced;
41470    }
41471
41472    /**
41473     * Defers invoking the `func` until the current call stack has cleared. Any
41474     * additional arguments are provided to `func` when it is invoked.
41475     *
41476     * @static
41477     * @memberOf _
41478     * @category Function
41479     * @param {Function} func The function to defer.
41480     * @param {...*} [args] The arguments to invoke the function with.
41481     * @returns {number} Returns the timer id.
41482     * @example
41483     *
41484     * _.defer(function(text) {
41485     *   console.log(text);
41486     * }, 'deferred');
41487     * // logs 'deferred' after one or more milliseconds
41488     */
41489    var defer = restParam(function(func, args) {
41490      return baseDelay(func, 1, args);
41491    });
41492
41493    /**
41494     * Invokes `func` after `wait` milliseconds. Any additional arguments are
41495     * provided to `func` when it is invoked.
41496     *
41497     * @static
41498     * @memberOf _
41499     * @category Function
41500     * @param {Function} func The function to delay.
41501     * @param {number} wait The number of milliseconds to delay invocation.
41502     * @param {...*} [args] The arguments to invoke the function with.
41503     * @returns {number} Returns the timer id.
41504     * @example
41505     *
41506     * _.delay(function(text) {
41507     *   console.log(text);
41508     * }, 1000, 'later');
41509     * // => logs 'later' after one second
41510     */
41511    var delay = restParam(function(func, wait, args) {
41512      return baseDelay(func, wait, args);
41513    });
41514
41515    /**
41516     * Creates a function that returns the result of invoking the provided
41517     * functions with the `this` binding of the created function, where each
41518     * successive invocation is supplied the return value of the previous.
41519     *
41520     * @static
41521     * @memberOf _
41522     * @category Function
41523     * @param {...Function} [funcs] Functions to invoke.
41524     * @returns {Function} Returns the new function.
41525     * @example
41526     *
41527     * function square(n) {
41528     *   return n * n;
41529     * }
41530     *
41531     * var addSquare = _.flow(_.add, square);
41532     * addSquare(1, 2);
41533     * // => 9
41534     */
41535    var flow = createFlow();
41536
41537    /**
41538     * This method is like `_.flow` except that it creates a function that
41539     * invokes the provided functions from right to left.
41540     *
41541     * @static
41542     * @memberOf _
41543     * @alias backflow, compose
41544     * @category Function
41545     * @param {...Function} [funcs] Functions to invoke.
41546     * @returns {Function} Returns the new function.
41547     * @example
41548     *
41549     * function square(n) {
41550     *   return n * n;
41551     * }
41552     *
41553     * var addSquare = _.flowRight(square, _.add);
41554     * addSquare(1, 2);
41555     * // => 9
41556     */
41557    var flowRight = createFlow(true);
41558
41559    /**
41560     * Creates a function that memoizes the result of `func`. If `resolver` is
41561     * provided it determines the cache key for storing the result based on the
41562     * arguments provided to the memoized function. By default, the first argument
41563     * provided to the memoized function is coerced to a string and used as the
41564     * cache key. The `func` is invoked with the `this` binding of the memoized
41565     * function.
41566     *
41567     * **Note:** The cache is exposed as the `cache` property on the memoized
41568     * function. Its creation may be customized by replacing the `_.memoize.Cache`
41569     * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
41570     * method interface of `get`, `has`, and `set`.
41571     *
41572     * @static
41573     * @memberOf _
41574     * @category Function
41575     * @param {Function} func The function to have its output memoized.
41576     * @param {Function} [resolver] The function to resolve the cache key.
41577     * @returns {Function} Returns the new memoizing function.
41578     * @example
41579     *
41580     * var upperCase = _.memoize(function(string) {
41581     *   return string.toUpperCase();
41582     * });
41583     *
41584     * upperCase('fred');
41585     * // => 'FRED'
41586     *
41587     * // modifying the result cache
41588     * upperCase.cache.set('fred', 'BARNEY');
41589     * upperCase('fred');
41590     * // => 'BARNEY'
41591     *
41592     * // replacing `_.memoize.Cache`
41593     * var object = { 'user': 'fred' };
41594     * var other = { 'user': 'barney' };
41595     * var identity = _.memoize(_.identity);
41596     *
41597     * identity(object);
41598     * // => { 'user': 'fred' }
41599     * identity(other);
41600     * // => { 'user': 'fred' }
41601     *
41602     * _.memoize.Cache = WeakMap;
41603     * var identity = _.memoize(_.identity);
41604     *
41605     * identity(object);
41606     * // => { 'user': 'fred' }
41607     * identity(other);
41608     * // => { 'user': 'barney' }
41609     */
41610    function memoize(func, resolver) {
41611      if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
41612        throw new TypeError(FUNC_ERROR_TEXT);
41613      }
41614      var memoized = function() {
41615        var args = arguments,
41616            key = resolver ? resolver.apply(this, args) : args[0],
41617            cache = memoized.cache;
41618
41619        if (cache.has(key)) {
41620          return cache.get(key);
41621        }
41622        var result = func.apply(this, args);
41623        memoized.cache = cache.set(key, result);
41624        return result;
41625      };
41626      memoized.cache = new memoize.Cache;
41627      return memoized;
41628    }
41629
41630    /**
41631     * Creates a function that runs each argument through a corresponding
41632     * transform function.
41633     *
41634     * @static
41635     * @memberOf _
41636     * @category Function
41637     * @param {Function} func The function to wrap.
41638     * @param {...(Function|Function[])} [transforms] The functions to transform
41639     * arguments, specified as individual functions or arrays of functions.
41640     * @returns {Function} Returns the new function.
41641     * @example
41642     *
41643     * function doubled(n) {
41644     *   return n * 2;
41645     * }
41646     *
41647     * function square(n) {
41648     *   return n * n;
41649     * }
41650     *
41651     * var modded = _.modArgs(function(x, y) {
41652     *   return [x, y];
41653     * }, square, doubled);
41654     *
41655     * modded(1, 2);
41656     * // => [1, 4]
41657     *
41658     * modded(5, 10);
41659     * // => [25, 20]
41660     */
41661    var modArgs = restParam(function(func, transforms) {
41662      transforms = baseFlatten(transforms);
41663      if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) {
41664        throw new TypeError(FUNC_ERROR_TEXT);
41665      }
41666      var length = transforms.length;
41667      return restParam(function(args) {
41668        var index = nativeMin(args.length, length);
41669        while (index--) {
41670          args[index] = transforms[index](args[index]);
41671        }
41672        return func.apply(this, args);
41673      });
41674    });
41675
41676    /**
41677     * Creates a function that negates the result of the predicate `func`. The
41678     * `func` predicate is invoked with the `this` binding and arguments of the
41679     * created function.
41680     *
41681     * @static
41682     * @memberOf _
41683     * @category Function
41684     * @param {Function} predicate The predicate to negate.
41685     * @returns {Function} Returns the new function.
41686     * @example
41687     *
41688     * function isEven(n) {
41689     *   return n % 2 == 0;
41690     * }
41691     *
41692     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
41693     * // => [1, 3, 5]
41694     */
41695    function negate(predicate) {
41696      if (typeof predicate != 'function') {
41697        throw new TypeError(FUNC_ERROR_TEXT);
41698      }
41699      return function() {
41700        return !predicate.apply(this, arguments);
41701      };
41702    }
41703
41704    /**
41705     * Creates a function that is restricted to invoking `func` once. Repeat calls
41706     * to the function return the value of the first call. The `func` is invoked
41707     * with the `this` binding and arguments of the created function.
41708     *
41709     * @static
41710     * @memberOf _
41711     * @category Function
41712     * @param {Function} func The function to restrict.
41713     * @returns {Function} Returns the new restricted function.
41714     * @example
41715     *
41716     * var initialize = _.once(createApplication);
41717     * initialize();
41718     * initialize();
41719     * // `initialize` invokes `createApplication` once
41720     */
41721    function once(func) {
41722      return before(2, func);
41723    }
41724
41725    /**
41726     * Creates a function that invokes `func` with `partial` arguments prepended
41727     * to those provided to the new function. This method is like `_.bind` except
41728     * it does **not** alter the `this` binding.
41729     *
41730     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
41731     * builds, may be used as a placeholder for partially applied arguments.
41732     *
41733     * **Note:** This method does not set the "length" property of partially
41734     * applied functions.
41735     *
41736     * @static
41737     * @memberOf _
41738     * @category Function
41739     * @param {Function} func The function to partially apply arguments to.
41740     * @param {...*} [partials] The arguments to be partially applied.
41741     * @returns {Function} Returns the new partially applied function.
41742     * @example
41743     *
41744     * var greet = function(greeting, name) {
41745     *   return greeting + ' ' + name;
41746     * };
41747     *
41748     * var sayHelloTo = _.partial(greet, 'hello');
41749     * sayHelloTo('fred');
41750     * // => 'hello fred'
41751     *
41752     * // using placeholders
41753     * var greetFred = _.partial(greet, _, 'fred');
41754     * greetFred('hi');
41755     * // => 'hi fred'
41756     */
41757    var partial = createPartial(PARTIAL_FLAG);
41758
41759    /**
41760     * This method is like `_.partial` except that partially applied arguments
41761     * are appended to those provided to the new function.
41762     *
41763     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
41764     * builds, may be used as a placeholder for partially applied arguments.
41765     *
41766     * **Note:** This method does not set the "length" property of partially
41767     * applied functions.
41768     *
41769     * @static
41770     * @memberOf _
41771     * @category Function
41772     * @param {Function} func The function to partially apply arguments to.
41773     * @param {...*} [partials] The arguments to be partially applied.
41774     * @returns {Function} Returns the new partially applied function.
41775     * @example
41776     *
41777     * var greet = function(greeting, name) {
41778     *   return greeting + ' ' + name;
41779     * };
41780     *
41781     * var greetFred = _.partialRight(greet, 'fred');
41782     * greetFred('hi');
41783     * // => 'hi fred'
41784     *
41785     * // using placeholders
41786     * var sayHelloTo = _.partialRight(greet, 'hello', _);
41787     * sayHelloTo('fred');
41788     * // => 'hello fred'
41789     */
41790    var partialRight = createPartial(PARTIAL_RIGHT_FLAG);
41791
41792    /**
41793     * Creates a function that invokes `func` with arguments arranged according
41794     * to the specified indexes where the argument value at the first index is
41795     * provided as the first argument, the argument value at the second index is
41796     * provided as the second argument, and so on.
41797     *
41798     * @static
41799     * @memberOf _
41800     * @category Function
41801     * @param {Function} func The function to rearrange arguments for.
41802     * @param {...(number|number[])} indexes The arranged argument indexes,
41803     *  specified as individual indexes or arrays of indexes.
41804     * @returns {Function} Returns the new function.
41805     * @example
41806     *
41807     * var rearged = _.rearg(function(a, b, c) {
41808     *   return [a, b, c];
41809     * }, 2, 0, 1);
41810     *
41811     * rearged('b', 'c', 'a')
41812     * // => ['a', 'b', 'c']
41813     *
41814     * var map = _.rearg(_.map, [1, 0]);
41815     * map(function(n) {
41816     *   return n * 3;
41817     * }, [1, 2, 3]);
41818     * // => [3, 6, 9]
41819     */
41820    var rearg = restParam(function(func, indexes) {
41821      return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes));
41822    });
41823
41824    /**
41825     * Creates a function that invokes `func` with the `this` binding of the
41826     * created function and arguments from `start` and beyond provided as an array.
41827     *
41828     * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
41829     *
41830     * @static
41831     * @memberOf _
41832     * @category Function
41833     * @param {Function} func The function to apply a rest parameter to.
41834     * @param {number} [start=func.length-1] The start position of the rest parameter.
41835     * @returns {Function} Returns the new function.
41836     * @example
41837     *
41838     * var say = _.restParam(function(what, names) {
41839     *   return what + ' ' + _.initial(names).join(', ') +
41840     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
41841     * });
41842     *
41843     * say('hello', 'fred', 'barney', 'pebbles');
41844     * // => 'hello fred, barney, & pebbles'
41845     */
41846    function restParam(func, start) {
41847      if (typeof func != 'function') {
41848        throw new TypeError(FUNC_ERROR_TEXT);
41849      }
41850      start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
41851      return function() {
41852        var args = arguments,
41853            index = -1,
41854            length = nativeMax(args.length - start, 0),
41855            rest = Array(length);
41856
41857        while (++index < length) {
41858          rest[index] = args[start + index];
41859        }
41860        switch (start) {
41861          case 0: return func.call(this, rest);
41862          case 1: return func.call(this, args[0], rest);
41863          case 2: return func.call(this, args[0], args[1], rest);
41864        }
41865        var otherArgs = Array(start + 1);
41866        index = -1;
41867        while (++index < start) {
41868          otherArgs[index] = args[index];
41869        }
41870        otherArgs[start] = rest;
41871        return func.apply(this, otherArgs);
41872      };
41873    }
41874
41875    /**
41876     * Creates a function that invokes `func` with the `this` binding of the created
41877     * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3).
41878     *
41879     * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator).
41880     *
41881     * @static
41882     * @memberOf _
41883     * @category Function
41884     * @param {Function} func The function to spread arguments over.
41885     * @returns {Function} Returns the new function.
41886     * @example
41887     *
41888     * var say = _.spread(function(who, what) {
41889     *   return who + ' says ' + what;
41890     * });
41891     *
41892     * say(['fred', 'hello']);
41893     * // => 'fred says hello'
41894     *
41895     * // with a Promise
41896     * var numbers = Promise.all([
41897     *   Promise.resolve(40),
41898     *   Promise.resolve(36)
41899     * ]);
41900     *
41901     * numbers.then(_.spread(function(x, y) {
41902     *   return x + y;
41903     * }));
41904     * // => a Promise of 76
41905     */
41906    function spread(func) {
41907      if (typeof func != 'function') {
41908        throw new TypeError(FUNC_ERROR_TEXT);
41909      }
41910      return function(array) {
41911        return func.apply(this, array);
41912      };
41913    }
41914
41915    /**
41916     * Creates a throttled function that only invokes `func` at most once per
41917     * every `wait` milliseconds. The throttled function comes with a `cancel`
41918     * method to cancel delayed invocations. Provide an options object to indicate
41919     * that `func` should be invoked on the leading and/or trailing edge of the
41920     * `wait` timeout. Subsequent calls to the throttled function return the
41921     * result of the last `func` call.
41922     *
41923     * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
41924     * on the trailing edge of the timeout only if the the throttled function is
41925     * invoked more than once during the `wait` timeout.
41926     *
41927     * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
41928     * for details over the differences between `_.throttle` and `_.debounce`.
41929     *
41930     * @static
41931     * @memberOf _
41932     * @category Function
41933     * @param {Function} func The function to throttle.
41934     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
41935     * @param {Object} [options] The options object.
41936     * @param {boolean} [options.leading=true] Specify invoking on the leading
41937     *  edge of the timeout.
41938     * @param {boolean} [options.trailing=true] Specify invoking on the trailing
41939     *  edge of the timeout.
41940     * @returns {Function} Returns the new throttled function.
41941     * @example
41942     *
41943     * // avoid excessively updating the position while scrolling
41944     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
41945     *
41946     * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
41947     * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
41948     *   'trailing': false
41949     * }));
41950     *
41951     * // cancel a trailing throttled call
41952     * jQuery(window).on('popstate', throttled.cancel);
41953     */
41954    function throttle(func, wait, options) {
41955      var leading = true,
41956          trailing = true;
41957
41958      if (typeof func != 'function') {
41959        throw new TypeError(FUNC_ERROR_TEXT);
41960      }
41961      if (options === false) {
41962        leading = false;
41963      } else if (isObject(options)) {
41964        leading = 'leading' in options ? !!options.leading : leading;
41965        trailing = 'trailing' in options ? !!options.trailing : trailing;
41966      }
41967      return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing });
41968    }
41969
41970    /**
41971     * Creates a function that provides `value` to the wrapper function as its
41972     * first argument. Any additional arguments provided to the function are
41973     * appended to those provided to the wrapper function. The wrapper is invoked
41974     * with the `this` binding of the created function.
41975     *
41976     * @static
41977     * @memberOf _
41978     * @category Function
41979     * @param {*} value The value to wrap.
41980     * @param {Function} wrapper The wrapper function.
41981     * @returns {Function} Returns the new function.
41982     * @example
41983     *
41984     * var p = _.wrap(_.escape, function(func, text) {
41985     *   return '<p>' + func(text) + '</p>';
41986     * });
41987     *
41988     * p('fred, barney, & pebbles');
41989     * // => '<p>fred, barney, &amp; pebbles</p>'
41990     */
41991    function wrap(value, wrapper) {
41992      wrapper = wrapper == null ? identity : wrapper;
41993      return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []);
41994    }
41995
41996    /*------------------------------------------------------------------------*/
41997
41998    /**
41999     * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
42000     * otherwise they are assigned by reference. If `customizer` is provided it is
42001     * invoked to produce the cloned values. If `customizer` returns `undefined`
42002     * cloning is handled by the method instead. The `customizer` is bound to
42003     * `thisArg` and invoked with two argument; (value [, index|key, object]).
42004     *
42005     * **Note:** This method is loosely based on the
42006     * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
42007     * The enumerable properties of `arguments` objects and objects created by
42008     * constructors other than `Object` are cloned to plain `Object` objects. An
42009     * empty object is returned for uncloneable values such as functions, DOM nodes,
42010     * Maps, Sets, and WeakMaps.
42011     *
42012     * @static
42013     * @memberOf _
42014     * @category Lang
42015     * @param {*} value The value to clone.
42016     * @param {boolean} [isDeep] Specify a deep clone.
42017     * @param {Function} [customizer] The function to customize cloning values.
42018     * @param {*} [thisArg] The `this` binding of `customizer`.
42019     * @returns {*} Returns the cloned value.
42020     * @example
42021     *
42022     * var users = [
42023     *   { 'user': 'barney' },
42024     *   { 'user': 'fred' }
42025     * ];
42026     *
42027     * var shallow = _.clone(users);
42028     * shallow[0] === users[0];
42029     * // => true
42030     *
42031     * var deep = _.clone(users, true);
42032     * deep[0] === users[0];
42033     * // => false
42034     *
42035     * // using a customizer callback
42036     * var el = _.clone(document.body, function(value) {
42037     *   if (_.isElement(value)) {
42038     *     return value.cloneNode(false);
42039     *   }
42040     * });
42041     *
42042     * el === document.body
42043     * // => false
42044     * el.nodeName
42045     * // => BODY
42046     * el.childNodes.length;
42047     * // => 0
42048     */
42049    function clone(value, isDeep, customizer, thisArg) {
42050      if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
42051        isDeep = false;
42052      }
42053      else if (typeof isDeep == 'function') {
42054        thisArg = customizer;
42055        customizer = isDeep;
42056        isDeep = false;
42057      }
42058      return typeof customizer == 'function'
42059        ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
42060        : baseClone(value, isDeep);
42061    }
42062
42063    /**
42064     * Creates a deep clone of `value`. If `customizer` is provided it is invoked
42065     * to produce the cloned values. If `customizer` returns `undefined` cloning
42066     * is handled by the method instead. The `customizer` is bound to `thisArg`
42067     * and invoked with two argument; (value [, index|key, object]).
42068     *
42069     * **Note:** This method is loosely based on the
42070     * [structured clone algorithm](http://www.w3.org/TR/html5/infrastru
42070cture.html#internal-structured-cloning-algorithm).
42071     * The enumerable properties of `arguments` objects and objects created by
42072     * constructors other than `Object` are cloned to plain `Object` objects. An
42073     * empty object is returned for uncloneable values such as functions, DOM nodes,
42074     * Maps, Sets, and WeakMaps.
42075     *
42076     * @static
42077     * @memberOf _
42078     * @category Lang
42079     * @param {*} value The value to deep clone.
42080     * @param {Function} [customizer] The function to customize cloning values.
42081     * @param {*} [thisArg] The `this` binding of `customizer`.
42082     * @returns {*} Returns the deep cloned value.
42083     * @example
42084     *
42085     * var users = [
42086     *   { 'user': 'barney' },
42087     *   { 'user': 'fred' }
42088     * ];
42089     *
42090     * var deep = _.cloneDeep(users);
42091     * deep[0] === users[0];
42092     * // => false
42093     *
42094     * // using a customizer callback
42095     * var el = _.cloneDeep(document.body, function(value) {
42096     *   if (_.isElement(value)) {
42097     *     return value.cloneNode(true);
42098     *   }
42099     * });
42100     *
42101     * el === document.body
42102     * // => false
42103     * el.nodeName
42104     * // => BODY
42105     * el.childNodes.length;
42106     * // => 20
42107     */
42108    function cloneDeep(value, customizer, thisArg) {
42109      return typeof customizer == 'function'
42110        ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
42111        : baseClone(value, true);
42112    }
42113
42114    /**
42115     * Checks if `value` is greater than `other`.
42116     *
42117     * @static
42118     * @memberOf _
42119     * @category Lang
42120     * @param {*} value The value to compare.
42121     * @param {*} other The other value to compare.
42122     * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`.
42123     * @example
42124     *
42125     * _.gt(3, 1);
42126     * // => true
42127     *
42128     * _.gt(3, 3);
42129     * // => false
42130     *
42131     * _.gt(1, 3);
42132     * // => false
42133     */
42134    function gt(value, other) {
42135      return value > other;
42136    }
42137
42138    /**
42139     * Checks if `value` is greater than or equal to `other`.
42140     *
42141     * @static
42142     * @memberOf _
42143     * @category Lang
42144     * @param {*} value The value to compare.
42145     * @param {*} other The other value to compare.
42146     * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`.
42147     * @example
42148     *
42149     * _.gte(3, 1);
42150     * // => true
42151     *
42152     * _.gte(3, 3);
42153     * // => true
42154     *
42155     * _.gte(1, 3);
42156     * // => false
42157     */
42158    function gte(value, other) {
42159      return value >= other;
42160    }
42161
42162    /**
42163     * Checks if `value` is classified as an `arguments` object.
42164     *
42165     * @static
42166     * @memberOf _
42167     * @category Lang
42168     * @param {*} value The value to check.
42169     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42170     * @example
42171     *
42172     * _.isArguments(function() { return arguments; }());
42173     * // => true
42174     *
42175     * _.isArguments([1, 2, 3]);
42176     * // => false
42177     */
42178    function isArguments(value) {
42179      return isObjectLike(value) && isArrayLike(value) &&
42180        hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
42181    }
42182
42183    /**
42184     * Checks if `value` is classified as an `Array` object.
42185     *
42186     * @static
42187     * @memberOf _
42188     * @category Lang
42189     * @param {*} value The value to check.
42190     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42191     * @example
42192     *
42193     * _.isArray([1, 2, 3]);
42194     * // => true
42195     *
42196     * _.isArray(function() { return arguments; }());
42197     * // => false
42198     */
42199    var isArray = nativeIsArray || function(value) {
42200      return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
42201    };
42202
42203    /**
42204     * Checks if `value` is classified as a boolean primitive or object.
42205     *
42206     * @static
42207     * @memberOf _
42208     * @category Lang
42209     * @param {*} value The value to check.
42210     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42211     * @example
42212     *
42213     * _.isBoolean(false);
42214     * // => true
42215     *
42216     * _.isBoolean(null);
42217     * // => false
42218     */
42219    function isBoolean(value) {
42220      return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag);
42221    }
42222
42223    /**
42224     * Checks if `value` is classified as a `Date` object.
42225     *
42226     * @static
42227     * @memberOf _
42228     * @category Lang
42229     * @param {*} value The value to check.
42230     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42231     * @example
42232     *
42233     * _.isDate(new Date);
42234     * // => true
42235     *
42236     * _.isDate('Mon April 23 2012');
42237     * // => false
42238     */
42239    function isDate(value) {
42240      return isObjectLike(value) && objToString.call(value) == dateTag;
42241    }
42242
42243    /**
42244     * Checks if `value` is a DOM element.
42245     *
42246     * @static
42247     * @memberOf _
42248     * @category Lang
42249     * @param {*} value The value to check.
42250     * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
42251     * @example
42252     *
42253     * _.isElement(document.body);
42254     * // => true
42255     *
42256     * _.isElement('<body>');
42257     * // => false
42258     */
42259    function isElement(value) {
42260      return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value);
42261    }
42262
42263    /**
42264     * Checks if `value` is empty. A value is considered empty unless it is an
42265     * `arguments` object, array, string, or jQuery-like collection with a length
42266     * greater than `0` or an object with own enumerable properties.
42267     *
42268     * @static
42269     * @memberOf _
42270     * @category Lang
42271     * @param {Array|Object|string} value The value to inspect.
42272     * @returns {boolean} Returns `true` if `value` is empty, else `false`.
42273     * @example
42274     *
42275     * _.isEmpty(null);
42276     * // => true
42277     *
42278     * _.isEmpty(true);
42279     * // => true
42280     *
42281     * _.isEmpty(1);
42282     * // => true
42283     *
42284     * _.isEmpty([1, 2, 3]);
42285     * // => false
42286     *
42287     * _.isEmpty({ 'a': 1 });
42288     * // => false
42289     */
42290    function isEmpty(value) {
42291      if (value == null) {
42292        return true;
42293      }
42294      if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
42295          (isObjectLike(value) && isFunction(value.splice)))) {
42296        return !value.length;
42297      }
42298      return !keys(value).length;
42299    }
42300
42301    /**
42302     * Performs a deep comparison between two values to determine if they are
42303     * equivalent. If `customizer` is provided it is invoked to compare values.
42304     * If `customizer` returns `undefined` comparisons are handled by the method
42305     * instead. The `customizer` is bound to `thisArg` and invoked with three
42306     * arguments: (value, other [, index|key]).
42307     *
42308     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
42309     * numbers, `Object` objects, regexes, and strings. Objects are compared by
42310     * their own, not inherited, enumerable properties. Functions and DOM nodes
42311     * are **not** supported. Provide a customizer function to extend support
42312     * for comparing other values.
42313     *
42314     * @static
42315     * @memberOf _
42316     * @alias eq
42317     * @category Lang
42318     * @param {*} value The value to compare.
42319     * @param {*} other The other value to compare.
42320     * @param {Function} [customizer] The function to customize value comparisons.
42321     * @param {*} [thisArg] The `this` binding of `customizer`.
42322     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
42323     * @example
42324     *
42325     * var object = { 'user': 'fred' };
42326     * var other = { 'user': 'fred' };
42327     *
42328     * object == other;
42329     * // => false
42330     *
42331     * _.isEqual(object, other);
42332     * // => true
42333     *
42334     * // using a customizer callback
42335     * var array = ['hello', 'goodbye'];
42336     * var other = ['hi', 'goodbye'];
42337     *
42338     * _.isEqual(array, other, function(value, other) {
42339     *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
42340     *     return true;
42341     *   }
42342     * });
42343     * // => true
42344     */
42345    function isEqual(value, other, customizer, thisArg) {
42346      customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
42347      var result = customizer ? customizer(value, other) : undefined;
42348      return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
42349    }
42350
42351    /**
42352     * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
42353     * `SyntaxError`, `TypeError`, or `URIError` object.
42354     *
42355     * @static
42356     * @memberOf _
42357     * @category Lang
42358     * @param {*} value The value to check.
42359     * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
42360     * @example
42361     *
42362     * _.isError(new Error);
42363     * // => true
42364     *
42365     * _.isError(Error);
42366     * // => false
42367     */
42368    function isError(value) {
42369      return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag;
42370    }
42371
42372    /**
42373     * Checks if `value` is a finite primitive number.
42374     *
42375     * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite).
42376     *
42377     * @static
42378     * @memberOf _
42379     * @category Lang
42380     * @param {*} value The value to check.
42381     * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
42382     * @example
42383     *
42384     * _.isFinite(10);
42385     * // => true
42386     *
42387     * _.isFinite('10');
42388     * // => false
42389     *
42390     * _.isFinite(true);
42391     * // => false
42392     *
42393     * _.isFinite(Object(10));
42394     * // => false
42395     *
42396     * _.isFinite(Infinity);
42397     * // => false
42398     */
42399    function isFinite(value) {
42400      return typeof value == 'number' && nativeIsFinite(value);
42401    }
42402
42403    /**
42404     * Checks if `value` is classified as a `Function` object.
42405     *
42406     * @static
42407     * @memberOf _
42408     * @category Lang
42409     * @param {*} value The value to check.
42410     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42411     * @example
42412     *
42413     * _.isFunction(_);
42414     * // => true
42415     *
42416     * _.isFunction(/abc/);
42417     * // => false
42418     */
42419    function isFunction(value) {
42420      // The use of `Object#toString` avoids issues with the `typeof` operator
42421      // in older versions of Chrome and Safari which return 'function' for regexes
42422      // and Safari 8 equivalents which return 'object' for typed array constructors.
42423      return isObject(value) && objToString.call(value) == funcTag;
42424    }
42425
42426    /**
42427     * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
42428     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
42429     *
42430     * @static
42431     * @memberOf _
42432     * @category Lang
42433     * @param {*} value The value to check.
42434     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
42435     * @example
42436     *
42437     * _.isObject({});
42438     * // => true
42439     *
42440     * _.isObject([1, 2, 3]);
42441     * // => true
42442     *
42443     * _.isObject(1);
42444     * // => false
42445     */
42446    function isObject(value) {
42447      // Avoid a V8 JIT bug in Chrome 19-20.
42448      // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
42449      var type = typeof value;
42450      return !!value && (type == 'object' || type == 'function');
42451    }
42452
42453    /**
42454     * Performs a deep comparison between `object` and `source` to determine if
42455     * `object` contains equivalent property values. If `customizer` is provided
42456     * it is invoked to compare values. If `customizer` returns `undefined`
42457     * comparisons are handled by the method instead. The `customizer` is bound
42458     * to `thisArg` and invoked with three arguments: (value, other, index|key).
42459     *
42460     * **Note:** This method supports comparing properties of arrays, booleans,
42461     * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions
42462     * and DOM nodes are **not** supported. Provide a customizer function to extend
42463     * support for comparing other values.
42464     *
42465     * @static
42466     * @memberOf _
42467     * @category Lang
42468     * @param {Object} object The object to inspect.
42469     * @param {Object} source The object of property values to match.
42470     * @param {Function} [customizer] The function to customize value comparisons.
42471     * @param {*} [thisArg] The `this` binding of `customizer`.
42472     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
42473     * @example
42474     *
42475     * var object = { 'user': 'fred', 'age': 40 };
42476     *
42477     * _.isMatch(object, { 'age': 40 });
42478     * // => true
42479     *
42480     * _.isMatch(object, { 'age': 36 });
42481     * // => false
42482     *
42483     * // using a customizer callback
42484     * var object = { 'greeting': 'hello' };
42485     * var source = { 'greeting': 'hi' };
42486     *
42487     * _.isMatch(object, source, function(value, other) {
42488     *   return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined;
42489     * });
42490     * // => true
42491     */
42492    function isMatch(object, source, customizer, thisArg) {
42493      customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
42494      return baseIsMatch(object, getMatchData(source), customizer);
42495    }
42496
42497    /**
42498     * Checks if `value` is `NaN`.
42499     *
42500     * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4)
42501     * which returns `true` for `undefined` and other non-numeric values.
42502     *
42503     * @static
42504     * @memberOf _
42505     * @category Lang
42506     * @param {*} value The value to check.
42507     * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
42508     * @example
42509     *
42510     * _.isNaN(NaN);
42511     * // => true
42512     *
42513     * _.isNaN(new Number(NaN));
42514     * // => true
42515     *
42516     * isNaN(undefined);
42517     * // => true
42518     *
42519     * _.isNaN(undefined);
42520     * // => false
42521     */
42522    function isNaN(value) {
42523      // An `NaN` primitive is the only value that is not equal to itself.
42524      // Perform the `toStringTag` check first to avoid errors with some host objects in IE.
42525      return isNumber(value) && value != +value;
42526    }
42527
42528    /**
42529     * Checks if `value` is a native function.
42530     *
42531     * @static
42532     * @memberOf _
42533     * @category Lang
42534     * @param {*} value The value to check.
42535     * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
42536     * @example
42537     *
42538     * _.isNative(Array.prototype.push);
42539     * // => true
42540     *
42541     * _.isNative(_);
42542     * // => false
42543     */
42544    function isNative(value) {
42545      if (value == null) {
42546        return false;
vendor: 4,241 bytes, lines 42547-42692
42547      }
42548      if (isFunction(value)) {
42549        return reIsNative.test(fnToString.call(value));
42550      }
42551      return isObjectLike(value) && reIsHostCtor.test(value);
42552    }
42553
42554    /**
42555     * Checks if `value` is `null`.
42556     *
42557     * @static
42558     * @memberOf _
42559     * @category Lang
42560     * @param {*} value The value to check.
42561     * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
42562     * @example
42563     *
42564     * _.isNull(null);
42565     * // => true
42566     *
42567     * _.isNull(void 0);
42568     * // => false
42569     */
42570    function isNull(value) {
42571      return value === null;
42572    }
42573
42574    /**
42575     * Checks if `value` is classified as a `Number` primitive or object.
42576     *
42577     * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified
42578     * as numbers, use the `_.isFinite` method.
42579     *
42580     * @static
42581     * @memberOf _
42582     * @category Lang
42583     * @param {*} value The value to check.
42584     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42585     * @example
42586     *
42587     * _.isNumber(8.4);
42588     * // => true
42589     *
42590     * _.isNumber(NaN);
42591     * // => true
42592     *
42593     * _.isNumber('8.4');
42594     * // => false
42595     */
42596    function isNumber(value) {
42597      return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag);
42598    }
42599
42600    /**
42601     * Checks if `value` is a plain object, that is, an object created by the
42602     * `Object` constructor or one with a `[[Prototype]]` of `null`.
42603     *
42604     * **Note:** This method assumes objects created by the `Object` constructor
42605     * have no inherited enumerable properties.
42606     *
42607     * @static
42608     * @memberOf _
42609     * @category Lang
42610     * @param {*} value The value to check.
42611     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
42612     * @example
42613     *
42614     * function Foo() {
42615     *   this.a = 1;
42616     * }
42617     *
42618     * _.isPlainObject(new Foo);
42619     * // => false
42620     *
42621     * _.isPlainObject([1, 2, 3]);
42622     * // => false
42623     *
42624     * _.isPlainObject({ 'x': 0, 'y': 0 });
42625     * // => true
42626     *
42627     * _.isPlainObject(Object.create(null));
42628     * // => true
42629     */
42630    function isPlainObject(value) {
42631      var Ctor;
42632
42633      // Exit early for non `Object` objects.
42634      if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) ||
42635          (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
42636        return false;
42637      }
42638      // IE < 9 iterates inherited properties before own properties. If the first
42639      // iterated property is an object's own property then there are no inherited
42640      // enumerable properties.
42641      var result;
42642      // In most environments an object's own properties are iterated before
42643      // its inherited properties. If the last iterated property is an object's
42644      // own property then there are no inherited enumerable properties.
42645      baseForIn(value, function(subValue, key) {
42646        result = key;
42647      });
42648      return result === undefined || hasOwnProperty.call(value, result);
42649    }
42650
42651    /**
42652     * Checks if `value` is classified as a `RegExp` object.
42653     *
42654     * @static
42655     * @memberOf _
42656     * @category Lang
42657     * @param {*} value The value to check.
42658     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42659     * @example
42660     *
42661     * _.isRegExp(/abc/);
42662     * // => true
42663     *
42664     * _.isRegExp('/abc/');
42665     * // => false
42666     */
42667    function isRegExp(value) {
42668      return isObject(value) && objToString.call(value) == regexpTag;
42669    }
42670
42671    /**
42672     * Checks if `value` is classified as a `String` primitive or object.
42673     *
42674     * @static
42675     * @memberOf _
42676     * @category Lang
42677     * @param {*} value The value to check.
42678     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42679     * @example
42680     *
42681     * _.isString('abc');
42682     * // => true
42683     *
42684     * _.isString(1);
42685     * // => false
42686     */
42687    function isString(value) {
42688      return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
42689    }
42690
42691    /**
42692     * Checks if `value` is classified as a typed array.
42693     *
42694     * @static
42695     * @memberOf _
42696     * @category Lang
42697     * @param {*} value The value to check.
42698     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
42699     * @example
42700     *
42701     * _.isTypedArray(new Uint8Array);
42702     * // => true
42703     *
42704     * _.isTypedArray([]);
42705     * // => false
42706     */
42707    function isTypedArray(value) {
42708      return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
42709    }
42710
42711    /**
42712     * Checks if `value` is `undefined`.
42713     *
42714     * @static
42715     * @memberOf _
42716     * @category Lang
42717     * @param {*} value The value to check.
42718     * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
42719     * @example
42720     *
42721     * _.isUndefined(void 0);
42722     * // => true
42723     *
42724     * _.isUndefined(null);
42725     * // => false
42726     */
42727    function isUndefined(value) {
42728      return value === undefined;
42729    }
42730
42731    /**
42732     * Checks if `value` is less than `other`.
42733     *
42734     * @static
42735     * @memberOf _
42736     * @category Lang
42737     * @param {*} value The value to compare.
42738     * @param {*} other The other value to compare.
42739     * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`.
42740     * @example
42741     *
42742     * _.lt(1, 3);
42743     * // => true
42744     *
42745     * _.lt(3, 3);
42746     * // => false
42747     *
42748     * _.lt(3, 1);
42749     * // => false
42750     */
42751    function lt(value, other) {
42752      return value < other;
42753    }
42754
42755    /**
42756     * Checks if `value` is less than or equal to `other`.
42757     *
42758     * @static
42759     * @memberOf _
42760     * @category Lang
42761     * @param {*} value The value to compare.
42762     * @param {*} other The other value to compare.
42763     * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`.
42764     * @example
42765     *
42766     * _.lte(1, 3);
42767     * // => true
42768     *
42769     * _.lte(3, 3);
42770     * // => true
42771     *
42772     * _.lte(3, 1);
42773     * // => false
42774     */
42775    function lte(value, other) {
42776      return value <= other;
42777    }
42778
42779    /**
42780     * Converts `value` to an array.
42781     *
42782     * @static
42783     * @memberOf _
42784     * @category Lang
42785     * @param {*} value The value to convert.
42786     * @returns {Array} Returns the converted array.
42787     * @example
42788     *
42789     * (function() {
42790     *   return _.toArray(arguments).slice(1);
42791     * }(1, 2, 3));
42792     * // => [2, 3]
42793     */
42794    function toArray(value) {
42795      var length = value ? getLength(value) : 0;
42796      if (!isLength(length)) {
42797        return values(value);
42798      }
42799      if (!length) {
42800        return [];
42801      }
42802      return arrayCopy(value);
42803    }
42804
42805    /**
42806     * Converts `value` to a plain object flattening inherited enumerable
42807     * properties of `value` to own properties of the plain object.
42808     *
42809     * @static
42810     * @memberOf _
42811     * @category Lang
42812     * @param {*} value The value to convert.
42813     * @returns {Object} Returns the converted plain object.
42814     * @example
42815     *
42816     * function Foo() {
42817     *   this.b = 2;
42818     * }
42819     *
42820     * Foo.prototype.c = 3;
42821     *
42822     * _.assign({ 'a': 1 }, new Foo);
42823     * // => { 'a': 1, 'b': 2 }
42824     *
42825     * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
42826     * // => { 'a': 1, 'b': 2, 'c': 3 }
42827     */
42828    function toPlainObject(value) {
42829      return baseCopy(value, keysIn(value));
42830    }
42831
42832    /*------------------------------------------------------------------------*/
42833
42834    /**
42835     * Recursively merges own enumerable properties of the source object(s), that
42836     * don't resolve to `undefined` into the destination object. Subsequent sources
42837     * overwrite property assignments of previous sources. If `customizer` is
42838     * provided it is invoked to produce the merged values of the destination and
42839     * source properties. If `customizer` returns `undefined` merging is handled
42840     * by the method instead. The `customizer` is bound to `thisArg` and invoked
42841     * with five arguments: (objectValue, sourceValue, key, object, source).
42842     *
42843     * @static
42844     * @memberOf _
42845     * @category Object
42846     * @param {Object} object The destination object.
42847     * @param {...Object} [sources] The source objects.
42848     * @param {Function} [customizer] The function to customize assigned values.
42849     * @param {*} [thisArg] The `this` binding of `customizer`.
42850     * @returns {Object} Returns `object`.
42851     * @example
42852     *
42853     * var users = {
42854     *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
42855     * };
42856     *
42857     * var ages = {
42858     *   'data': [{ 'age': 36 }, { 'age': 40 }]
42859     * };
42860     *
42861     * _.merge(users, ages);
42862     * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
42863     *
42864     * // using a customizer callback
42865     * var object = {
42866     *   'fruits': ['apple'],
42867     *   'vegetables': ['beet']
42868     * };
42869     *
42870     * var other = {
42871     *   'fruits': ['banana'],
42872     *   'vegetables': ['carrot']
42873     * };
42874     *
42875     * _.merge(object, other, function(a, b) {
42876     *   if (_.isArray(a)) {
42877     *     return a.concat(b);
42878     *   }
42879     * });
42880     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
42881     */
42882    var merge = createAssigner(baseMerge);
42883
42884    /**
42885     * Assigns own enumerable properties of source object(s) to the destination
42886     * object. Subsequent sources overwrite property assignments of previous sources.
42887     * If `customizer` is provided it is invoked to produce the assigned values.
42888     * The `customizer` is bound to `thisArg` and invoked with five arguments:
42889     * (objectValue, sourceValue, key, object, source).
42890     *
42891     * **Note:** This method mutates `object` and is based on
42892     * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign).
42893     *
42894     * @static
42895     * @memberOf _
42896     * @alias extend
42897     * @category Object
42898     * @param {Object} object The destination object.
42899     * @param {...Object} [sources] The source objects.
42900     * @param {Function} [customizer] The function to customize assigned values.
42901     * @param {*} [thisArg] The `this` binding of `customizer`.
42902     * @returns {Object} Returns `object`.
42903     * @example
42904     *
42905     * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
42906     * // => { 'user': 'fred', 'age': 40 }
42907     *
42908     * // using a customizer callback
42909     * var defaults = _.partialRight(_.assign, function(value, other) {
42910     *   return _.isUndefined(value) ? other : value;
42911     * });
42912     *
42913     * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
42914     * // => { 'user': 'barney', 'age': 36 }
42915     */
42916    var assign = createAssigner(function(object, source, customizer) {
42917      return customizer
42918        ? assignWith(object, source, customizer)
42919        : baseAssign(object, source);
42920    });
42921
42922    /**
42923     * Creates an object that inherits from the given `prototype` object. If a
42924     * `properties` object is provided its own enumerable properties are assigned
42925     * to the created object.
42926     *
42927     * @static
42928     * @memberOf _
42929     * @category Object
42930     * @param {Object} prototype The object to inherit from.
42931     * @param {Object} [properties] The properties to assign to the object.
42932     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
42933     * @returns {Object} Returns the new object.
42934     * @example
42935     *
42936     * function Shape() {
42937     *   this.x = 0;
42938     *   this.y = 0;
42939     * }
42940     *
42941     * function Circle() {
42942     *   Shape.call(this);
42943     * }
42944     *
42945     * Circle.prototype = _.create(Shape.prototype, {
42946     *   'constructor': Circle
42947     * });
42948     *
42949     * var circle = new Circle;
42950     * circle instanceof Circle;
42951     * // => true
42952     *
42953     * circle instanceof Shape;
42954     * // => true
42955     */
42956    function create(prototype, properties, guard) {
42957      var result = baseCreate(prototype);
42958      if (guard && isIterateeCall(prototype, properties, guard)) {
42959        properties = undefined;
42960      }
42961      return properties ? baseAssign(result, properties) : result;
42962    }
42963
42964    /**
42965     * Assigns own enumerable properties of source object(s) to the destination
42966     * object for all destination properties that resolve to `undefined`. Once a
42967     * property is set, additional values of the same property are ignored.
42968     *
42969     * **Note:** This method mutates `object`.
42970     *
42971     * @static
42972     * @memberOf _
42973     * @category Object
42974     * @param {Object} object The destination object.
42975     * @param {...Object} [sources] The source objects.
42976     * @returns {Object} Returns `object`.
42977     * @example
42978     *
42979     * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
42980     * // => { 'user': 'barney', 'age': 36 }
42981     */
42982    var defaults = createDefaults(assign, assignDefaults);
42983
42984    /**
42985     * This method is like `_.defaults` except that it recursively assigns
42986     * default properties.
42987     *
42988     * **Note:** This method mutates `object`.
42989     *
42990     * @static
42991     * @memberOf _
42992     * @category Object
42993     * @param {Object} object The destination object.
42994     * @param {...Object} [sources] The source objects.
42995     * @returns {Object} Returns `object`.
42996     * @example
42997     *
42998     * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
42999     * // => { 'user': { 'name': 'barney', 'age': 36 } }
43000     *
43001     */
43002    var defaultsDeep = createDefaults(merge, mergeDefaults);
43003
43004    /**
43005     * This method is like `_.find` except that it returns the key of the first
43006     * element `predicate` returns truthy for instead of the element itself.
43007     *
43008     * If a property name is provided for `predicate` the created `_.property`
43009     * style callback returns the property value of the given element.
43010     *
43011     * If a value is also provided for `thisArg` the created `_.matchesProperty`
43012     * style callback returns `true` for elements that have a matching property
43013     * value, else `false`.
43014     *
43015     * If an object is provided for `predicate` the created `_.matches` style
43016     * callback returns `true` for elements that have the properties of the given
43017     * object, else `false`.
43018     *
43019     * @static
43020     * @memberOf _
43021     * @category Object
43022     * @param {Object} object The object to search.
43023     * @param {Function|Object|string} [predicate=_.identity] The function invoked
43024     *  per iteration.
43025     * @param {*} [thisArg] The `this` binding of `predicate`.
43026     * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
43027     * @example
43028     *
43029     * var users = {
43030     *   'barney':  { 'age': 36, 'active': true },
43031     *   'fred':    { 'age': 40, 'active': false },
43032     *   'pebbles': { 'age': 1,  'active': true }
43033     * };
43034     *
43035     * _.findKey(users, function(chr) {
43036     *   return chr.age < 40;
43037     * });
43038     * // => 'barney' (iteration order is not guaranteed)
43039     *
43040     * // using the `_.matches` callback shorthand
43041     * _.findKey(users, { 'age': 1, 'active': true });
43042     * // => 'pebbles'
43043     *
43044     * // using the `_.matchesProperty` callback shorthand
43045     * _.findKey(users, 'active', false);
43046     * // => 'fred'
43047     *
43048     * // using the `_.property` callback shorthand
43049     * _.findKey(users, 'active');
43050     * // => 'barney'
43051     */
43052    var findKey = createFindKey(baseForOwn);
43053
43054    /**
43055     * This method is like `_.findKey` except that it iterates over elements of
43056     * a collection in the opposite order.
43057     *
43058     * If a property name is provided for `predicate` the created `_.property`
43059     * style callback returns the property value of the given element.
43060     *
43061     * If a value is also provided for `thisArg` the created `_.matchesProperty`
43062     * style callback returns `true` for elements that have a matching property
43063     * value, else `false`.
43064     *
43065     * If an object is provided for `predicate` the created `_.matches` style
43066     * callback returns `true` for elements that have the properties of the given
43067     * object, else `false`.
43068     *
43069     * @static
43070     * @memberOf _
43071     * @category Object
43072     * @param {Object} object The object to search.
43073     * @param {Function|Object|string} [predicate=_.identity] The function invoked
43074     *  per iteration.
43075     * @param {*} [thisArg] The `this` binding of `predicate`.
43076     * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
43077     * @example
43078     *
43079     * var users = {
43080     *   'barney':  { 'age': 36, 'active': true },
43081     *   'fred':    { 'age': 40, 'active': false },
43082     *   'pebbles': { 'age': 1,  'active': true }
43083     * };
43084     *
43085     * _.findLastKey(users, function(chr) {
43086     *   return chr.age < 40;
43087     * });
43088     * // => returns `pebbles` assuming `_.findKey` returns `barney`
43089     *
43090     * // using the `_.matches` callback shorthand
43091     * _.findLastKey(users, { 'age': 36, 'active': true });
43092     * // => 'barney'
43093     *
43094     * // using the `_.matchesProperty` callback shorthand
43095     * _.findLastKey(users, 'active', false);
43096     * // => 'fred'
43097     *
43098     * // using the `_.property` callback shorthand
43099     * _.findLastKey(users, 'active');
43100     * // => 'pebbles'
43101     */
43102    var findLastKey = createFindKey(baseForOwnRight);
43103
43104    /**
43105     * Iterates over own and inherited enumerable properties of an object invoking
43106     * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked
43107     * with three arguments: (value, key, object). Iteratee functions may exit
43108     * iteration early by explicitly returning `false`.
43109     *
43110     * @static
43111     * @memberOf _
43112     * @category Object
43113     * @param {Object} object The object to iterate over.
43114     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
43115     * @param {*} [thisArg] The `this` binding of `iteratee`.
43116     * @returns {Object} Returns `object`.
43117     * @example
43118     *
43119     * function Foo() {
43120     *   this.a = 1;
43121     *   this.b = 2;
43122     * }
43123     *
43124     * Foo.prototype.c = 3;
43125     *
43126     * _.forIn(new Foo, function(value, key) {
43127     *   console.log(key);
43128     * });
43129     * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed)
43130     */
43131    var forIn = createForIn(baseFor);
43132
43133    /**
43134     * This method is like `_.forIn` except that it iterates over properties of
43135     * `object` in the opposite order.
43136     *
43137     * @static
43138     * @memberOf _
43139     * @category Object
43140     * @param {Object} object The object to iterate over.
43141     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
43142     * @param {*} [thisArg] The `this` binding of `iteratee`.
43143     * @returns {Object} Returns `object`.
43144     * @example
43145     *
43146     * function Foo() {
43147     *   this.a = 1;
43148     *   this.b = 2;
43149     * }
43150     *
43151     * Foo.prototype.c = 3;
43152     *
43153     * _.forInRight(new Foo, function(value, key) {
43154     *   console.log(key);
43155     * });
43156     * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c'
43157     */
43158    var forInRight = createForIn(baseForRight);
43159
43160    /**
43161     * Iterates over own enumerable properties of an object invoking `iteratee`
43162     * for each property. The `iteratee` is bound to `thisArg` and invoked with
43163     * three arguments: (value, key, object). Iteratee functions may exit iteration
43164     * early by explicitly returning `false`.
43165     *
43166     * @static
43167     * @memberOf _
43168     * @category Object
43169     * @param {Object} object The object to iterate over.
43170     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
43171     * @param {*} [thisArg] The `this` binding of `iteratee`.
43172     * @returns {Object} Returns `object`.
43173     * @example
43174     *
43175     * function Foo() {
43176     *   this.a = 1;
43177     *   this.b = 2;
43178     * }
43179     *
43180     * Foo.prototype.c = 3;
43181     *
43182     * _.forOwn(new Foo, function(value, key) {
43183     *   console.log(key);
43184     * });
43185     * // => logs 'a' and 'b' (iteration order is not guaranteed)
43186     */
43187    var forOwn = createForOwn(baseForOwn);
43188
43189    /**
43190     * This method is like `_.forOwn` except that it iterates over properties of
43191     * `object` in the opposite order.
43192     *
43193     * @static
43194     * @memberOf _
43195     * @category Object
43196     * @param {Object} object The object to iterate over.
43197     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
43198     * @param {*} [thisArg] The `this` binding of `iteratee`.
43199     * @returns {Object} Returns `object`.
43200     * @example
43201     *
43202     * function Foo() {
43203     *   this.a = 1;
43204     *   this.b = 2;
43205     * }
43206     *
43207     * Foo.prototype.c = 3;
43208     *
43209     * _.forOwnRight(new Foo, function(value, key) {
43210     *   console.log(key);
43211     * });
43212     * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b'
43213     */
43214    var forOwnRight = createForOwn(baseForOwnRight);
43215
43216    /**
43217     * Creates an array of function property names from all enumerable properties,
43218     * own and inherited, of `object`.
43219     *
43220     * @static
43221     * @memberOf _
43222     * @alias methods
43223     * @category Object
43224     * @param {Object} object The object to inspect.
43225     * @returns {Array} Returns the new array of property names.
43226     * @example
43227     *
43228     * _.functions(_);
43229     * // => ['after', 'ary', 'assign', ...]
43230     */
43231    function functions(object) {
43232      return baseFunctions(object, keysIn(object));
43233    }
43234
43235    /**
43236     * Gets the property value at `path` of `object`. If the resolved value is
43237     * `undefined` the `defaultValue` is used in its place.
43238     *
43239     * @static
43240     * @memberOf _
43241     * @category Object
43242     * @param {Object} object The object to query.
43243     * @param {Array|string} path The path of the property to get.
43244     * @param {*} [defaultValue] The value returned if the resolved value is `undefined`.
43245     * @returns {*} Returns the resolved value.
43246     * @example
43247     *
43248     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
43249     *
43250     * _.get(object, 'a[0].b.c');
43251     * // => 3
43252     *
43253     * _.get(object, ['a', '0', 'b', 'c']);
43254     * // => 3
43255     *
43256     * _.get(object, 'a.b.c', 'default');
43257     * // => 'default'
43258     */
43259    function get(object, path, defaultValue) {
43260      var result = object == null ? undefined : baseGet(object, toPath(path), path + '');
43261      return result === undefined ? defaultValue : result;
43262    }
43263
43264    /**
43265     * Checks if `path` is a direct property.
43266     *
43267     * @static
43268     * @memberOf _
43269     * @category Object
43270     * @param {Object} object The object to query.
43271     * @param {Array|string} path The path to check.
43272     * @returns {boolean} Returns `true` if `path` is a direct property, else `false`.
43273     * @example
43274     *
43275     * var object = { 'a': { 'b': { 'c': 3 } } };
43276     *
43277     * _.has(object, 'a');
43278     * // => true
43279     *
43280     * _.has(object, 'a.b.c');
43281     * // => true
43282     *
43283     * _.has(object, ['a', 'b', 'c']);
43284     * // => true
43285     */
43286    function has(object, path) {
43287      if (object == null) {
43288        return false;
43289      }
43290      var result = hasOwnProperty.call(object, path);
43291      if (!result && !isKey(path)) {
43292        path = toPath(path);
43293        object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
43294        if (object == null) {
43295          return false;
43296        }
43297        path = last(path);
43298        result = hasOwnProperty.call(object, path);
43299      }
43300      return result || (isLength(object.length) && isIndex(path, object.length) &&
43301        (isArray(object) || isArguments(object)));
43302    }
43303
43304    /**
43305     * Creates an object composed of the inverted keys and values of `object`.
43306     * If `object` contains duplicate values, subsequent values overwrite property
43307     * assignments of previous values unless `multiValue` is `true`.
43308     *
43309     * @static
43310     * @memberOf _
43311     * @category Object
43312     * @param {Object} object The object to invert.
43313     * @param {boolean} [multiValue] Allow multiple values per key.
43314     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
43315     * @returns {Object} Returns the new inverted object.
43316     * @example
43317     *
43318     * var object = { 'a': 1, 'b': 2, 'c': 1 };
43319     *
43320     * _.invert(object);
43321     * // => { '1': 'c', '2': 'b' }
43322     *
43323     * // with `multiValue`
43324     * _.invert(object, true);
43325     * // => { '1': ['a', 'c'], '2': ['b'] }
43326     */
43327    function invert(object, multiValue, guard) {
43328      if (guard && isIterateeCall(object, multiValue, guard)) {
43329        multiValue = undefined;
43330      }
43331      var index = -1,
43332          props = keys(object),
43333          length = props.length,
43334          result = {};
43335
43336      while (++index < length) {
43337        var key = props[index],
43338            value = object[key];
43339
43340        if (multiValue) {
43341          if (hasOwnProperty.call(result, value)) {
43342            result[value].push(key);
43343          } else {
43344            result[value] = [key];
43345          }
43346        }
43347        else {
43348          result[value] = key;
43349        }
43350      }
43351      return result;
43352    }
43353
43354    /**
43355     * Creates an array of the own enumerable property names of `object`.
43356     *
43357     * **Note:** Non-object values are coerced to objects. See the
43358     * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
43359     * for more details.
43360     *
43361     * @static
43362     * @memberOf _
43363     * @category Object
43364     * @param {Object} object The object to query.
43365     * @returns {Array} Returns the array of property names.
43366     * @example
43367     *
43368     * function Foo() {
43369     *   this.a = 1;
43370     *   this.b = 2;
43371     * }
43372     *
43373     * Foo.prototype.c = 3;
43374     *
43375     * _.keys(new Foo);
43376     * // => ['a', 'b'] (iteration order is not guaranteed)
43377     *
43378     * _.keys('hi');
43379     * // => ['0', '1']
43380     */
43381    var keys = !nativeKeys ? shimKeys : function(object) {
43382      var Ctor = object == null ? undefined : object.constructor;
43383      if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
43384          (typeof object != 'function' && isArrayLike(object))) {
43385        return shimKeys(object);
43386      }
43387      return isObject(object) ? nativeKeys(object) : [];
43388    };
43389
43390    /**
43391     * Creates an array of the own and inherited enumerable property names of `object`.
43392     *
43393     * **Note:** Non-object values are coerced to objects.
43394     *
43395     * @static
43396     * @memberOf _
43397     * @category Object
43398     * @param {Object} object The object to query.
43399     * @returns {Array} Returns the array of property names.
43400     * @example
43401     *
43402     * function Foo() {
43403     *   this.a = 1;
43404     *   this.b = 2;
43405     * }
43406     *
43407     * Foo.prototype.c = 3;
43408     *
43409     * _.keysIn(new Foo);
43410     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
43411     */
43412    function keysIn(object) {
43413      if (object == null) {
43414        return [];
43415      }
43416      if (!isObject(object)) {
43417        object = Object(object);
43418      }
43419      var length = object.length;
43420      length = (length && isLength(length) &&
43421        (isArray(object) || isArguments(object)) && length) || 0;
43422
43423      var Ctor = object.constructor,
43424          index = -1,
43425          isProto = typeof Ctor == 'function' && Ctor.prototype === object,
43426          result = Array(length),
43427          skipIndexes = length > 0;
43428
43429      while (++index < length) {
43430        result[index] = (index + '');
43431      }
43432      for (var key in object) {
43433        if (!(skipIndexes && isIndex(key, length)) &&
43434            !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
43435          result.push(key);
43436        }
43437      }
43438      return result;
43439    }
43440
43441    /**
43442     * The opposite of `_.mapValues`; this method creates an object with the
43443     * same values as `object` and keys generated by running each own enumerable
43444     * property of `object` through `iteratee`.
43445     *
43446     * @static
43447     * @memberOf _
43448     * @category Object
43449     * @param {Object} object The object to iterate over.
43450     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
43451     *  per iteration.
43452     * @param {*} [thisArg] The `this` binding of `iteratee`.
43453     * @returns {Object} Returns the new mapped object.
43454     * @example
43455     *
43456     * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
43457     *   return key + value;
43458     * });
43459     * // => { 'a1': 1, 'b2': 2 }
43460     */
43461    var mapKeys = createObjectMapper(true);
43462
43463    /**
43464     * Creates an object with the same keys as `object` and values generated by
43465     * running each own enumerable property of `object` through `iteratee`. The
43466     * iteratee function is bound to `thisArg` and invoked with three arguments:
43467     * (value, key, object).
43468     *
43469     * If a property name is provided for `iteratee` the created `_.property`
43470     * style callback returns the property value of the given element.
43471     *
43472     * If a value is also provided for `thisArg` the created `_.matchesProperty`
43473     * style callback returns `true` for elements that have a matching property
43474     * value, else `false`.
43475     *
43476     * If an object is provided for `iteratee` the created `_.matches` style
43477     * callback returns `true` for elements that have the properties of the given
43478     * object, else `false`.
43479     *
43480     * @static
43481     * @memberOf _
43482     * @category Object
43483     * @param {Object} object The object to iterate over.
43484     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
43485     *  per iteration.
43486     * @param {*} [thisArg] The `this` binding of `iteratee`.
43487     * @returns {Object} Returns the new mapped object.
43488     * @example
43489     *
43490     * _.mapValues({ 'a': 1, 'b': 2 }, function(n) {
43491     *   return n * 3;
43492     * });
43493     * // => { 'a': 3, 'b': 6 }
43494     *
43495     * var users = {
43496     *   'fred':    { 'user': 'fred',    'age': 40 },
43497     *   'pebbles': { 'user': 'pebbles', 'age': 1 }
43498     * };
43499     *
43500     * // using the `_.property` callback shorthand
43501     * _.mapValues(users, 'age');
43502     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
43503     */
43504    var mapValues = createObjectMapper();
43505
43506    /**
43507     * The opposite of `_.pick`; this method creates an object composed of the
43508     * own and inherited enumerable properties of `object` that are not omitted.
43509     *
43510     * @static
43511     * @memberOf _
43512     * @category Object
43513     * @param {Object} object The source object.
43514     * @param {Function|...(string|string[])} [predicate] The function invoked per
43515     *  iteration or property names to omit, specified as individual property
43516     *  names or arrays of property names.
43517     * @param {*} [thisArg] The `this` binding of `predicate`.
43518     * @returns {Object} Returns the new object.
43519     * @example
43520     *
43521     * var object = { 'user': 'fred', 'age': 40 };
43522     *
43523     * _.omit(object, 'age');
43524     * // => { 'user': 'fred' }
43525     *
43526     * _.omit(object, _.isNumber);
43527     * // => { 'user': 'fred' }
43528     */
43529    var omit = restParam(function(object, props) {
43530      if (object == null) {
43531        return {};
43532      }
43533      if (typeof props[0] != 'function') {
43534        var props = arrayMap(baseFlatten(props), String);
43535        return pickByArray(object, baseDifference(keysIn(object), props));
43536      }
43537      var predicate = bindCallback(props[0], props[1], 3);
43538      return pickByCallback(object, function(value, key, object) {
43539        return !predicate(value, key, object);
43540      });
43541    });
43542
43543    /**
43544     * Creates a two dimensional array of the key-value pairs for `object`,
43545     * e.g. `[[key1, value1], [key2, value2]]`.
43546     *
43547     * @static
43548     * @memberOf _
43549     * @category Object
43550     * @param {Object} object The object to query.
43551     * @returns {Array} Returns the new array of key-value pairs.
43552     * @example
43553     *
43554     * _.pairs({ 'barney': 36, 'fred': 40 });
43555     * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
43556     */
43557    function pairs(object) {
43558      object = toObject(object);
43559
43560      var index = -1,
43561          props = keys(object),
43562          length = props.length,
43563          result = Array(length);
43564
43565      while (++index < length) {
43566        var key = props[index];
43567        result[index] = [key, object[key]];
43568      }
43569      return result;
43570    }
43571
43572    /**
43573     * Creates an object composed of the picked `object` properties. Property
43574     * names may be specified as individual arguments or as arrays of property
43575     * names. If `predicate` is provided it is invoked for each property of `object`
43576     * picking the properties `predicate` returns truthy for. The predicate is
43577     * bound to `thisArg` and invoked with three arguments: (value, key, object).
43578     *
43579     * @static
43580     * @memberOf _
43581     * @category Object
43582     * @param {Object} object The source object.
43583     * @param {Function|...(string|string[])} [predicate] The function invoked per
43584     *  iteration or property names to pick, specified as individual property
43585     *  names or arrays of property names.
43586     * @param {*} [thisArg] The `this` binding of `predicate`.
43587     * @returns {Object} Returns the new object.
43588     * @example
43589     *
43590     * var object = { 'user': 'fred', 'age': 40 };
43591     *
43592     * _.pick(object, 'user');
43593     * // => { 'user': 'fred' }
43594     *
43595     * _.pick(object, _.isString);
43596     * // => { 'user': 'fred' }
43597     */
43598    var pick = restParam(function(object, props) {
43599      if (object == null) {
43600        return {};
43601      }
43602      return typeof props[0] == 'function'
43603        ? pickByCallback(object, bindCallback(props[0], props[1], 3))
43604        : pickByArray(object, baseFlatten(props));
43605    });
43606
43607    /**
43608     * This method is like `_.get` except that if the resolved value is a function
43609     * it is invoked with the `this` binding of its parent object and its result
43610     * is returned.
43611     *
43612     * @static
43613     * @memberOf _
43614     * @category Object
43615     * @param {Object} object The object to query.
43616     * @param {Array|string} path The path of the property to resolve.
43617     * @param {*} [defaultValue] The value returned if the resolved value is `undefined`.
43618     * @returns {*} Returns the resolved value.
43619     * @example
43620     *
43621     * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
43622     *
43623     * _.result(object, 'a[0].b.c1');
43624     * // => 3
43625     *
43626     * _.result(object, 'a[0].b.c2');
43627     * // => 4
43628     *
43629     * _.result(object, 'a.b.c', 'default');
43630     * // => 'default'
43631     *
43632     * _.result(object, 'a.b.c', _.constant('default'));
43633     * // => 'default'
43634     */
43635    function result(object, path, defaultValue) {
43636      var result = object == null ? undefined : object[path];
43637      if (result === undefined) {
43638        if (object != null && !isKey(path, object)) {
43639          path = toPath(path);
43640          object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
43641          result = object == null ? undefined : object[last(path)];
43642        }
43643        result = result === undefined ? defaultValue : result;
43644      }
43645      return isFunction(result) ? result.call(object) : result;
43646    }
43647
43648    /**
43649     * Sets the property value of `path` on `object`. If a portion of `path`
43650     * does not exist it is created.
43651     *
43652     * @static
43653     * @memberOf _
43654     * @category Object
43655     * @param {Object} object The object to augment.
43656     * @param {Array|string} path The path of the property to set.
43657     * @param {*} value The value to set.
43658     * @returns {Object} Returns `object`.
43659     * @example
43660     *
43661     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
43662     *
43663     * _.set(object, 'a[0].b.c', 4);
43664     * console.log(object.a[0].b.c);
43665     * // => 4
43666     *
43667     * _.set(object, 'x[0].y.z', 5);
43668     * console.log(object.x[0].y.z);
43669     * // => 5
43670     */
43671    function set(object, path, value) {
43672      if (object == null) {
43673        return object;
43674      }
43675      var pathKey = (path + '');
43676      path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path);
43677
43678      var index = -1,
43679          length = path.length,
43680          lastIndex = length - 1,
43681          nested = object;
43682
43683      while (nested != null && ++index < length) {
43684        var key = path[index];
43685        if (isObject(nested)) {
43686          if (index == lastIndex) {
43687            nested[key] = value;
43688          } else if (nested[key] == null) {
43689            nested[key] = isIndex(path[index + 1]) ? [] : {};
43690          }
43691        }
43692        nested = nested[key];
43693      }
43694      return object;
43695    }
43696
43697    /**
43698     * An alternative to `_.reduce`; this method transforms `object` to a new
43699     * `accumulator` object which is the result of running each of its own enumerable
43700     * properties through `iteratee`, with each invocation potentially mutating
43701     * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked
43702     * with four arguments: (accumulator, value, key, object). Iteratee functions
43703     * may exit iteration early by explicitly returning `false`.
43704     *
43705     * @static
43706     * @memberOf _
43707     * @category Object
43708     * @param {Array|Object} object The object to iterate over.
43709     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
43710     * @param {*} [accumulator] The custom accumulator value.
43711     * @param {*} [thisArg] The `this` binding of `iteratee`.
43712     * @returns {*} Returns the accumulated value.
43713     * @example
43714     *
43715     * _.transform([2, 3, 4], function(result, n) {
43716     *   result.push(n *= n);
43717     *   return n % 2 == 0;
43718     * });
43719     * // => [4, 9]
43720     *
43721     * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) {
43722     *   result[key] = n * 3;
43723     * });
43724     * // => { 'a': 3, 'b': 6 }
43725     */
43726    function transform(object, iteratee, accumulator, thisArg) {
43727      var isArr = isArray(object) || isTypedArray(object);
43728      iteratee = getCallback(iteratee, thisArg, 4);
43729
43730      if (accumulator == null) {
43731        if (isArr || isObject(object)) {
43732          var Ctor = object.constructor;
43733          if (isArr) {
43734            accumulator = isArray(object) ? new Ctor : [];
43735          } else {
43736            accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined);
43737          }
43738        } else {
43739          accumulator = {};
43740        }
43741      }
43742      (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {
43743        return iteratee(accumulator, value, index, object);
43744      });
43745      return accumulator;
43746    }
43747
43748    /**
43749     * Creates an array of the own enumerable property values of `object`.
43750     *
43751     * **Note:** Non-object values are coerced to objects.
43752     *
43753     * @static
43754     * @memberOf _
43755     * @category Object
43756     * @param {Object} object The object to query.
43757     * @returns {Array} Returns the array of property values.
43758     * @example
43759     *
43760     * function Foo() {
43761     *   this.a = 1;
43762     *   this.b = 2;
43763     * }
43764     *
43765     * Foo.prototype.c = 3;
43766     *
43767     * _.values(new Foo);
43768     * // => [1, 2] (iteration order is not guaranteed)
43769     *
43770     * _.values('hi');
43771     * // => ['h', 'i']
43772     */
43773    function values(object) {
43774      return baseValues(object, keys(object));
43775    }
43776
43777    /**
43778     * Creates an array of the own and inherited enumerable property values
43779     * of `object`.
43780     *
43781     * **Note:** Non-object values are coerced to objects.
43782     *
43783     * @static
43784     * @memberOf _
43785     * @category Object
43786     * @param {Object} object The object to query.
43787     * @returns {Array} Returns the array of property values.
43788     * @example
43789     *
43790     * function Foo() {
43791     *   this.a = 1;
43792     *   this.b = 2;
43793     * }
43794     *
43795     * Foo.prototype.c = 3;
43796     *
43797     * _.valuesIn(new Foo);
43798     * // => [1, 2, 3] (iteration order is not guaranteed)
43799     */
43800    function valuesIn(object) {
43801      return baseValues(object, keysIn(object));
43802    }
43803
43804    /*------------------------------------------------------------------------*/
43805
43806    /**
43807     * Checks if `n` is between `start` and up to but not including, `end`. If
43808     * `end` is not specified it is set to `start` with `start` then set to `0`.
43809     *
43810     * @static
43811     * @memberOf _
43812     * @category Number
43813     * @param {number} n The number to check.
43814     * @param {number} [start=0] The start of the range.
43815     * @param {number} end The end of the range.
43816     * @returns {boolean} Returns `true` if `n` is in the range, else `false`.
43817     * @example
43818     *
43819     * _.inRange(3, 2, 4);
43820     * // => true
43821     *
43822     * _.inRange(4, 8);
43823     * // => true
43824     *
43825     * _.inRange(4, 2);
43826     * // => false
43827     *
43828     * _.inRange(2, 2);
43829     * // => false
43830     *
43831     * _.inRange(1.2, 2);
43832     * // => true
43833     *
43834     * _.inRange(5.2, 4);
43835     * // => false
43836     */
43837    function inRange(value, start, end) {
43838      start = +start || 0;
43839      if (end === undefined) {
43840        end = start;
43841        start = 0;
43842      } else {
43843        end = +end || 0;
43844      }
43845      return value >= nativeMin(start, end) && value < nativeMax(start, end);
43846    }
43847
43848    /**
43849     * Produces a random number between `min` and `max` (inclusive). If only one
43850     * argument is provided a number between `0` and the given number is returned.
43851     * If `floating` is `true`, or either `min` or `max` are floats, a floating-point
43852     * number is returned instead of an integer.
43853     *
43854     * @static
43855     * @memberOf _
43856     * @category Number
43857     * @param {number} [min=0] The minimum possible value.
43858     * @param {number} [max=1] The maximum possible value.
43859     * @param {boolean} [floating] Specify returning a floating-point number.
43860     * @returns {number} Returns the random number.
43861     * @example
43862     *
43863     * _.random(0, 5);
43864     * // => an integer between 0 and 5
43865     *
43866     * _.random(5);
43867     * // => also an integer between 0 and 5
43868     *
43869     * _.random(5, true);
43870     * // => a floating-point number between 0 and 5
43871     *
43872     * _.random(1.2, 5.2);
43873     * // => a floating-point number between 1.2 and 5.2
43874     */
43875    function random(min, max, floating) {
43876      if (floating && isIterateeCall(min, max, floating)) {
43877        max = floating = undefined;
43878      }
43879      var noMin = min == null,
43880          noMax = max == null;
43881
43882      if (floating == null) {
43883        if (noMax && typeof min == 'boolean') {
43884          floating = min;
43885          min = 1;
43886        }
43887        else if (typeof max == 'boolean') {
43888          floating = max;
43889          noMax = true;
43890        }
43891      }
43892      if (noMin && noMax) {
43893        max = 1;
43894        noMax = false;
43895      }
43896      min = +min || 0;
43897      if (noMax) {
43898        max = min;
43899        min = 0;
43900      } else {
43901        max = +max || 0;
43902      }
43903      if (floating || min % 1 || max % 1) {
43904        var rand = nativeRandom();
43905        return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max);
43906      }
43907      return baseRandom(min, max);
43908    }
43909
43910    /*------------------------------------------------------------------------*/
43911
43912    /**
43913     * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
43914     *
43915     * @static
43916     * @memberOf _
43917     * @category String
43918     * @param {string} [string=''] The string to convert.
43919     * @returns {string} Returns the camel cased string.
43920     * @example
43921     *
43922     * _.camelCase('Foo Bar');
43923     * // => 'fooBar'
43924     *
43925     * _.camelCase('--foo-bar');
43926     * // => 'fooBar'
43927     *
43928     * _.camelCase('__foo_bar__');
43929     * // => 'fooBar'
43930     */
43931    var camelCase = createCompounder(function(result, word, index) {
43932      word = word.toLowerCase();
43933      return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word);
43934    });
43935
43936    /**
43937     * Capitalizes the first character of `string`.
43938     *
43939     * @static
43940     * @memberOf _
43941     * @category String
43942     * @param {string} [string=''] The string to capitalize.
43943     * @returns {string} Returns the capitalized string.
43944     * @example
43945     *
43946     * _.capitalize('fred');
43947     * // => 'Fred'
43948     */
43949    function capitalize(string) {
43950      string = baseToString(string);
43951      return string && (string.charAt(0).toUpperCase() + string.slice(1));
43952    }
43953
43954    /**
43955     * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
43956     * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
43957     *
43958     * @static
43959     * @memberOf _
43960     * @category String
43961     * @param {string} [string=''] The string to deburr.
43962     * @returns {string} Returns the deburred string.
43963     * @example
43964     *
43965     * _.deburr('déjà vu');
43966     * // => 'deja vu'
43967     */
43968    function deburr(string) {
43969      string = baseToString(string);
43970      return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, '');
43971    }
43972
43973    /**
43974     * Checks if `string` ends with the given target string.
43975     *
43976     * @static
43977     * @memberOf _
43978     * @category String
43979     * @param {string} [string=''] The string to search.
43980     * @param {string} [target] The string to search for.
43981     * @param {number} [position=string.length] The position to search from.
43982     * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`.
43983     * @example
43984     *
43985     * _.endsWith('abc', 'c');
43986     * // => true
43987     *
43988     * _.endsWith('abc', 'b');
43989     * // => false
43990     *
43991     * _.endsWith('abc', 'b', 2);
43992     * // => true
43993     */
43994    function endsWith(string, target, position) {
43995      string = baseToString(string);
43996      target = (target + '');
43997
43998      var length = string.length;
43999      position = position === undefined
44000        ? length
44001        : nativeMin(position < 0 ? 0 : (+position || 0), length);
44002
44003      position -= target.length;
44004      return position >= 0 && string.indexOf(target, position) == position;
44005    }
44006
44007    /**
44008     * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to
44009     * their corresponding HTML entities.
44010     *
44011     * **Note:** No other characters are escaped. To escape additional characters
44012     * use a third-party library like [_he_](https://mths.be/he).
44013     *
44014     * Though the ">" character is escaped for symmetry, characters like
44015     * ">" and "/" don't need escaping in HTML and have no special meaning
44016     * unless they're part of a tag or unquoted attribute value.
44017     * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
44018     * (under "semi-related fun fact") for more details.
44019     *
44020     * Backticks are escaped because in Internet Explorer < 9, they can break out
44021     * of attribute values or HTML comments. See [#59](https://html5sec.org/#59),
44022     * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and
44023     * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/)
44024     * for more details.
44025     *
44026     * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping)
44027     * to reduce XSS vectors.
44028     *
44029     * @static
44030     * @memberOf _
44031     * @category String
44032     * @param {string} [string=''] The string to escape.
44033     * @returns {string} Returns the escaped string.
44034     * @example
44035     *
44036     * _.escape('fred, barney, & pebbles');
44037     * // => 'fred, barney, &amp; pebbles'
44038     */
44039    function escape(string) {
44040      // Reset `lastIndex` because in IE < 9 `String#replace` does not.
44041      string = baseToString(string);
44042      return (string && reHasUnescapedHtml.test(string))
44043        ? string.replace(reUnescapedHtml, escapeHtmlChar)
44044        : string;
44045    }
44046
44047    /**
44048     * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
44049     * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
44050     *
44051     * @static
44052     * @memberOf _
44053     * @category String
44054     * @param {string} [string=''] The string to escape.
44055     * @returns {string} Returns the escaped string.
44056     * @example
44057     *
44058     * _.escapeRegExp('[lodash](https://lodash.com/)');
44059     * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
44060     */
44061    function escapeRegExp(string) {
44062      string = baseToString(string);
44063      return (string && reHasRegExpChars.test(string))
44064        ? string.replace(reRegExpChars, escapeRegExpChar)
44065        : (string || '(?:)');
44066    }
44067
44068    /**
44069     * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).
44070     *
44071     * @static
44072     * @memberOf _
44073     * @category String
44074     * @param {string} [string=''] The string to convert.
44075     * @returns {string} Returns the kebab cased string.
44076     * @example
44077     *
44078     * _.kebabCase('Foo Bar');
44079     * // => 'foo-bar'
44080     *
44081     * _.kebabCase('fooBar');
44082     * // => 'foo-bar'
44083     *
44084     * _.kebabCase('__foo_bar__');
44085     * // => 'foo-bar'
44086     */
44087    var kebabCase = createCompounder(function(result, word, index) {
44088      return result + (index ? '-' : '') + word.toLowerCase();
44089    });
44090
44091    /**
44092     * Pads `string` on the left and right sides if it's shorter than `length`.
44093     * Padding characters are truncated if they can't be evenly divided by `length`.
44094     *
44095     * @static
44096     * @memberOf _
44097     * @category String
44098     * @param {string} [string=''] The string to pad.
44099     * @param {number} [length=0] The padding length.
44100     * @param {string} [chars=' '] The string used as padding.
44101     * @returns {string} Returns the padded string.
44102     * @example
44103     *
44104     * _.pad('abc', 8);
44105     * // => '  abc   '
44106     *
44107     * _.pad('abc', 8, '_-');
44108     * // => '_-abc_-_'
44109     *
44110     * _.pad('abc', 3);
44111     * // => 'abc'
44112     */
44113    function pad(string, length, chars) {
44114      string = baseToString(string);
44115      length = +length;
44116
44117      var strLength = string.length;
44118      if (strLength >= length || !nativeIsFinite(length)) {
44119        return string;
44120      }
44121      var mid = (length - strLength) / 2,
44122          leftLength = nativeFloor(mid),
44123          rightLength = nativeCeil(mid);
44124
44125      chars = createPadding('', rightLength, chars);
44126      return chars.slice(0, leftLength) + string + chars;
44127    }
44128
44129    /**
44130     * Pads `string` on the left side if it's shorter than `length`. Padding
44131     * characters are truncated if they exceed `length`.
44132     *
44133     * @static
44134     * @memberOf _
44135     * @category String
44136     * @param {string} [string=''] The string to pad.
44137     * @param {number} [length=0] The padding length.
44138     * @param {string} [chars=' '] The string used as padding.
44139     * @returns {string} Returns the padded string.
44140     * @example
44141     *
44142     * _.padLeft('abc', 6);
44143     * // => '   abc'
44144     *
44145     * _.padLeft('abc', 6, '_-');
44146     * // => '_-_abc'
44147     *
44148     * _.padLeft('abc', 3);
44149     * // => 'abc'
44150     */
44151    var padLeft = createPadDir();
44152
44153    /**
44154     * Pads `string` on the right side if it's shorter than `length`. Padding
44155     * characters are truncated if they exceed `length`.
44156     *
44157     * @static
44158     * @memberOf _
44159     * @category String
44160     * @param {string} [string=''] The string to pad.
44161     * @param {number} [length=0] The padding length.
44162     * @param {string} [chars=' '] The string used as padding.
44163     * @returns {string} Returns the padded string.
44164     * @example
44165     *
44166     * _.padRight('abc', 6);
44167     * // => 'abc   '
44168     *
44169     * _.padRight('abc', 6, '_-');
44170     * // => 'abc_-_'
44171     *
44172     * _.padRight('abc', 3);
44173     * // => 'abc'
44174     */
44175    var padRight = createPadDir(true);
44176
44177    /**
44178     * Converts `string` to an integer of the specified radix. If `radix` is
44179     * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal,
44180     * in which case a `radix` of `16` is used.
44181     *
44182     * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E)
44183     * of `parseInt`.
44184     *
44185     * @static
44186     * @memberOf _
44187     * @category String
44188     * @param {string} string The string to convert.
44189     * @param {number} [radix] The radix to interpret `value` by.
44190     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44191     * @returns {number} Returns the converted integer.
44192     * @example
44193     *
44194     * _.parseInt('08');
44195     * // => 8
44196     *
44197     * _.map(['6', '08', '10'], _.parseInt);
44198     * // => [6, 8, 10]
44199     */
44200    function parseInt(string, radix, guard) {
44201      // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`.
44202      // Chrome fails to trim leading <BOM> whitespace characters.
44203      // See https://code.google.com/p/v8/issues/detail?id=3109 for more details.
44204      if (guard ? isIterateeCall(string, radix, guard) : radix == null) {
44205        radix = 0;
44206      } else if (radix) {
44207        radix = +radix;
44208      }
44209      string = trim(string);
44210      return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10));
44211    }
44212
44213    /**
44214     * Repeats the given string `n` times.
44215     *
44216     * @static
44217     * @memberOf _
44218     * @category String
44219     * @param {string} [string=''] The string to repeat.
44220     * @param {number} [n=0] The number of times to repeat the string.
44221     * @returns {string} Returns the repeated string.
44222     * @example
44223     *
44224     * _.repeat('*', 3);
44225     * // => '***'
44226     *
44227     * _.repeat('abc', 2);
44228     * // => 'abcabc'
44229     *
44230     * _.repeat('abc', 0);
44231     * // => ''
44232     */
44233    function repeat(string, n) {
44234      var result = '';
44235      string = baseToString(string);
44236      n = +n;
44237      if (n < 1 || !string || !nativeIsFinite(n)) {
44238        return result;
44239      }
44240      // Leverage the exponentiation by squaring algorithm for a faster repeat.
44241      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
44242      do {
44243        if (n % 2) {
44244          result += string;
44245        }
44246        n = nativeFloor(n / 2);
44247        string += string;
44248      } while (n);
44249
44250      return result;
44251    }
44252
44253    /**
44254     * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case).
44255     *
44256     * @static
44257     * @memberOf _
44258     * @category String
44259     * @param {string} [string=''] The string to convert.
44260     * @returns {string} Returns the snake cased string.
44261     * @example
44262     *
44263     * _.snakeCase('Foo Bar');
44264     * // => 'foo_bar'
44265     *
44266     * _.snakeCase('fooBar');
44267     * // => 'foo_bar'
44268     *
44269     * _.snakeCase('--foo-bar');
44270     * // => 'foo_bar'
44271     */
44272    var snakeCase = createCompounder(function(result, word, index) {
44273      return result + (index ? '_' : '') + word.toLowerCase();
44274    });
44275
44276    /**
44277     * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
44278     *
44279     * @static
44280     * @memberOf _
44281     * @category String
44282     * @param {string} [string=''] The string to convert.
44283     * @returns {string} Returns the start cased string.
44284     * @example
44285     *
44286     * _.startCase('--foo-bar');
44287     * // => 'Foo Bar'
44288     *
44289     * _.startCase('fooBar');
44290     * // => 'Foo Bar'
44291     *
44292     * _.startCase('__foo_bar__');
44293     * // => 'Foo Bar'
44294     */
44295    var startCase = createCompounder(function(result, word, index) {
44296      return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1));
44297    });
44298
44299    /**
44300     * Checks if `string` starts with the given target string.
44301     *
44302     * @static
44303     * @memberOf _
44304     * @category String
44305     * @param {string} [string=''] The string to search.
44306     * @param {string} [target] The string to search for.
44307     * @param {number} [position=0] The position to search from.
44308     * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`.
44309     * @example
44310     *
44311     * _.startsWith('abc', 'a');
44312     * // => true
44313     *
44314     * _.startsWith('abc', 'b');
44315     * // => false
44316     *
44317     * _.startsWith('abc', 'b', 1);
44318     * // => true
44319     */
44320    function startsWith(string, target, position) {
44321      string = baseToString(string);
44322      position = position == null
44323        ? 0
44324        : nativeMin(position < 0 ? 0 : (+position || 0), string.length);
44325
44326      return string.lastIndexOf(target, position) == position;
44327    }
44328
44329    /**
44330     * Creates a compiled template function that can interpolate data properties
44331     * in "interpolate" delimiters, HTML-escape interpolated data properties in
44332     * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
44333     * properties may be accessed as free variables in the template. If a setting
44334     * object is provided it takes precedence over `_.templateSettings` values.
44335     *
44336     * **Note:** In the development build `_.template` utilizes
44337     * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
44338     * for easier debugging.
44339     *
44340     * For more information on precompiling templates see
44341     * [lodash's custom builds documentation](https://lodash.com/custom-builds).
44342     *
44343     * For more information on Chrome extension sandboxes see
44344     * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
44345     *
44346     * @static
44347     * @memberOf _
44348     * @category String
44349     * @param {string} [string=''] The template string.
44350     * @param {Object} [options] The options object.
44351     * @param {RegExp} [options.escape] The HTML "escape" delimiter.
44352     * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
44353     * @param {Object} [options.imports] An object to import into the template as free variables.
44354     * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
44355     * @param {string} [options.sourceURL] The sourceURL of the template's compiled source.
44356     * @param {string} [options.variable] The data object variable name.
44357     * @param- {Object} [otherOptions] Enables the legacy `options` param signature.
44358     * @returns {Function} Returns the compiled template function.
44359     * @example
44360     *
44361     * // using the "interpolate" delimiter to create a compiled template
44362     * var compiled = _.template('hello <%= user %>!');
44363     * compiled({ 'user': 'fred' });
44364     * // => 'hello fred!'
44365     *
44366     * // using the HTML "escape" delimiter to escape data property values
44367     * var compiled = _.template('<b><%- value %></b>');
44368     * compiled({ 'value': '<script>' });
44369     * // => '<b>&lt;script&gt;</b>'
44370     *
44371     * // using the "evaluate" delimiter to execute JavaScript and generate HTML
44372     * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>');
44373     * compiled({ 'users': ['fred', 'barney'] });
44374     * // => '<li>fred</li><li>barney</li>'
44375     *
44376     * // using the internal `print` function in "evaluate" delimiters
44377     * var compiled = _.template('<% print("hello " + user); %>!');
44378     * compiled({ 'user': 'barney' });
44379     * // => 'hello barney!'
44380     *
44381     * // using the ES delimiter as an alternative to the default "interpolate" delimiter
44382     * var compiled = _.template('hello ${ user }!');
44383     * compiled({ 'user': 'pebbles' });
44384     * // => 'hello pebbles!'
44385     *
44386     * // using custom template delimiters
44387     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
44388     * var compiled = _.template('hello {{ user }}!');
44389     * compiled({ 'user': 'mustache' });
44390     * // => 'hello mustache!'
44391     *
44392     * // using backslashes to treat delimiters as plain text
44393     * var compiled = _.template('<%= "\\<%- value %\\>" %>');
44394     * compiled({ 'value': 'ignored' });
44395     * // => '<%- value %>'
44396     *
44397     * // using the `imports` option to import `jQuery` as `jq`
44398     * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>';
44399     * var compiled = _.template(text, { 'imports': { 'jq': jQuery } });
44400     * compiled({ 'users': ['fred', 'barney'] });
44401     * // => '<li>fred</li><li>barney</li>'
44402     *
44403     * // using the `sourceURL` option to specify a custom sourceURL for the template
44404     * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' });
44405     * compiled(data);
44406     * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
44407     *
44408     * // using the `variable` option to ensure a with-statement isn't used in the compiled template
44409     * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });
44410     * compiled.source;
44411     * // => function(data) {
44412     * //   var __t, __p = '';
44413     * //   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';
44414     * //   return __p;
44415     * // }
44416     *
44417     * // using the `source` property to inline compiled templates for meaningful
44418     * // line numbers in error messages and a stack trace
44419     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
44420     *   var JST = {\
44421     *     "main": ' + _.template(mainText).source + '\
44422     *   };\
44423     * ');
44424     */
44425    function template(string, options, otherOptions) {
44426      // Based on John Resig's `tmpl` implementation (http://ejohn.org/blog/javascript-micro-templating/)
44427      // and Laura Doktorova's doT.js (https://github.com/olado/doT).
44428      var settings = lodash.templateSettings;
44429
44430      if (otherOptions && isIterateeCall(string, options, otherOptions)) {
44431        options = otherOptions = undefined;
44432      }
44433      string = baseToString(string);
44434      options = assignWith(baseAssign({}, otherOptions || options), settings, assignOwnDefaults);
44435
44436      var imports = assignWith(baseAssign({}, options.imports), settings.imports, assignOwnDefaults),
44437          importsKeys = keys(imports),
44438          importsValues = baseValues(imports, importsKeys);
44439
44440      var isEscaping,
44441          isEvaluating,
44442          index = 0,
44443          interpolate = options.interpolate || reNoMatch,
44444          source = "__p += '";
44445
44446      // Compile the regexp to match each delimiter.
44447      var reDelimiters = RegExp(
44448        (options.escape || reNoMatch).source + '|' +
44449        interpolate.source + '|' +
44450        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
44451        (options.evaluate || reNoMatch).source + '|$'
44452      , 'g');
44453
44454      // Use a sourceURL for easier debugging.
44455      var sourceURL = '//# sourceURL=' +
44456        ('sourceURL' in options
44457          ? options.sourceURL
44458          : ('lodash.templateSources[' + (++templateCounter) + ']')
44459        ) + '\n';
44460
44461      string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
44462        interpolateValue || (interpolateValue = esTemplateValue);
44463
44464        // Escape characters that can't be included in string literals.
44465        source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);
44466
44467        // Replace delimiters with snippets.
44468        if (escapeValue) {
44469          isEscaping = true;
44470          source += "' +\n__e(" + escapeValue + ") +\n'";
44471        }
44472        if (evaluateValue) {
44473          isEvaluating = true;
44474          source += "';\n" + evaluateValue + ";\n__p += '";
44475        }
44476        if (interpolateValue) {
44477          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
44478        }
44479        index = offset + match.length;
44480
44481        // The JS engine embedded in Adobe products requires returning the `match`
44482        // string in order to produce the correct `offset` value.
44483        return match;
44484      });
44485
44486      source += "';\n";
44487
44488      // If `variable` is not specified wrap a with-statement around the generated
44489      // code to add the data object to the top of the scope chain.
44490      var variable = options.variable;
44491      if (!variable) {
44492        source = 'with (obj) {\n' + source + '\n}\n';
44493      }
44494      // Cleanup code by stripping empty strings.
44495      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
44496        .replace(reEmptyStringMiddle, '$1')
44497        .replace(reEmptyStringTrailing, '$1;');
44498
44499      // Frame code as the function body.
44500      source = 'function(' + (variable || 'obj') + ') {\n' +
44501        (variable
44502          ? ''
44503          : 'obj || (obj = {});\n'
44504        ) +
44505        "var __t, __p = ''" +
44506        (isEscaping
44507           ? ', __e = _.escape'
44508           : ''
44509        ) +
44510        (isEvaluating
44511          ? ', __j = Array.prototype.join;\n' +
44512            "function print() { __p += __j.call(arguments, '') }\n"
44513          : ';\n'
44514        ) +
44515        source +
44516        'return __p\n}';
44517
44518      var result = attempt(function() {
44519        return Function(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues);
44520      });
44521
44522      // Provide the compiled function's source by its `toString` method or
44523      // the `source` property as a convenience for inlining compiled templates.
44524      result.source = source;
44525      if (isError(result)) {
44526        throw result;
44527      }
44528      return result;
44529    }
44530
44531    /**
44532     * Removes leading and trailing whitespace or specified characters from `string`.
44533     *
44534     * @static
44535     * @memberOf _
44536     * @category String
44537     * @param {string} [string=''] The string to trim.
44538     * @param {string} [chars=whitespace] The characters to trim.
44539     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44540     * @returns {string} Returns the trimmed string.
44541     * @example
44542     *
44543     * _.trim('  abc  ');
44544     * // => 'abc'
44545     *
44546     * _.trim('-_-abc-_-', '_-');
44547     * // => 'abc'
44548     *
44549     * _.map(['  foo  ', '  bar  '], _.trim);
44550     * // => ['foo', 'bar']
44551     */
44552    function trim(string, chars, guard) {
44553      var value = string;
44554      string = baseToString(string);
44555      if (!string) {
44556        return string;
44557      }
44558      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
44559        return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1);
44560      }
44561      chars = (chars + '');
44562      return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1);
44563    }
44564
44565    /**
44566     * Removes leading whitespace or specified characters from `string`.
44567     *
44568     * @static
44569     * @memberOf _
44570     * @category String
44571     * @param {string} [string=''] The string to trim.
44572     * @param {string} [chars=whitespace] The characters to trim.
44573     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44574     * @returns {string} Returns the trimmed string.
44575     * @example
44576     *
44577     * _.trimLeft('  abc  ');
44578     * // => 'abc  '
44579     *
44580     * _.trimLeft('-_-abc-_-', '_-');
44581     * // => 'abc-_-'
44582     */
44583    function trimLeft(string, chars, guard) {
44584      var value = string;
44585      string = baseToString(string);
44586      if (!string) {
44587        return string;
44588      }
44589      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
44590        return string.slice(trimmedLeftIndex(string));
44591      }
44592      return string.slice(charsLeftIndex(string, (chars + '')));
44593    }
44594
44595    /**
44596     * Removes trailing whitespace or specified characters from `string`.
44597     *
44598     * @static
44599     * @memberOf _
44600     * @category String
44601     * @param {string} [string=''] The string to trim.
44602     * @param {string} [chars=whitespace] The characters to trim.
44603     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44604     * @returns {string} Returns the trimmed string.
44605     * @example
44606     *
44607     * _.trimRight('  abc  ');
44608     * // => '  abc'
44609     *
44610     * _.trimRight('-_-abc-_-', '_-');
44611     * // => '-_-abc'
44612     */
44613    function trimRight(string, chars, guard) {
44614      var value = string;
44615      string = baseToString(string);
44616      if (!string) {
44617        return string;
44618      }
44619      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
44620        return string.slice(0, trimmedRightIndex(string) + 1);
44621      }
44622      return string.slice(0, charsRightIndex(string, (chars + '')) + 1);
44623    }
44624
44625    /**
44626     * Truncates `string` if it's longer than the given maximum string length.
44627     * The last characters of the truncated string are replaced with the omission
44628     * string which defaults to "...".
44629     *
44630     * @static
44631     * @memberOf _
44632     * @category String
44633     * @param {string} [string=''] The string to truncate.
44634     * @param {Object|number} [options] The options object or maximum string length.
44635     * @param {number} [options.length=30] The maximum string length.
44636     * @param {string} [options.omission='...'] The string to indicate text is omitted.
44637     * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
44638     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44639     * @returns {string} Returns the truncated string.
44640     * @example
44641     *
44642     * _.trunc('hi-diddly-ho there, neighborino');
44643     * // => 'hi-diddly-ho there, neighbo...'
44644     *
44645     * _.trunc('hi-diddly-ho there, neighborino', 24);
44646     * // => 'hi-diddly-ho there, n...'
44647     *
44648     * _.trunc('hi-diddly-ho there, neighborino', {
44649     *   'length': 24,
44650     *   'separator': ' '
44651     * });
44652     * // => 'hi-diddly-ho there,...'
44653     *
44654     * _.trunc('hi-diddly-ho there, neighborino', {
44655     *   'length': 24,
44656     *   'separator': /,? +/
44657     * });
44658     * // => 'hi-diddly-ho there...'
44659     *
44660     * _.trunc('hi-diddly-ho there, neighborino', {
44661     *   'omission': ' [...]'
44662     * });
44663     * // => 'hi-diddly-ho there, neig [...]'
44664     */
44665    function trunc(string, options, guard) {
44666      if (guard && isIterateeCall(string, options, guard)) {
44667        options = undefined;
44668      }
44669      var length = DEFAULT_TRUNC_LENGTH,
44670          omission = DEFAULT_TRUNC_OMISSION;
44671
44672      if (options != null) {
44673        if (isObject(options)) {
44674          var separator = 'separator' in options ? options.separator : separator;
44675          length = 'length' in options ? (+options.length || 0) : length;
44676          omission = 'omission' in options ? baseToString(options.omission) : omission;
44677        } else {
44678          length = +options || 0;
44679        }
44680      }
44681      string = baseToString(string);
44682      if (length >= string.length) {
44683        return string;
44684      }
44685      var end = length - omission.length;
44686      if (end < 1) {
44687        return omission;
44688      }
44689      var result = string.slice(0, end);
44690      if (separator == null) {
44691        return result + omission;
44692      }
44693      if (isRegExp(separator)) {
44694        if (string.slice(end).search(separator)) {
44695          var match,
44696              newEnd,
44697              substring = string.slice(0, end);
44698
44699          if (!separator.global) {
44700            separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g');
44701          }
44702          separator.lastIndex = 0;
44703          while ((match = separator.exec(substring))) {
44704            newEnd = match.index;
44705          }
44706          result = result.slice(0, newEnd == null ? end : newEnd);
44707        }
44708      } else if (string.indexOf(separator, end) != end) {
44709        var index = result.lastIndexOf(separator);
44710        if (index > -1) {
44711          result = result.slice(0, index);
44712        }
44713      }
44714      return result + omission;
44715    }
44716
44717    /**
44718     * The inverse of `_.escape`; this method converts the HTML entities
44719     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, `&#39;`, and `&#96;` in `string` to their
44720     * corresponding characters.
44721     *
44722     * **Note:** No other HTML entities are unescaped. To unescape additional HTML
44723     * entities use a third-party library like [_he_](https://mths.be/he).
44724     *
44725     * @static
44726     * @memberOf _
44727     * @category String
44728     * @param {string} [string=''] The string to unescape.
44729     * @returns {string} Returns the unescaped string.
44730     * @example
44731     *
44732     * _.unescape('fred, barney, &amp; pebbles');
44733     * // => 'fred, barney, & pebbles'
44734     */
44735    function unescape(string) {
44736      string = baseToString(string);
44737      return (string && reHasEscapedHtml.test(string))
44738        ? string.replace(reEscapedHtml, unescapeHtmlChar)
44739        : string;
44740    }
44741
44742    /**
44743     * Splits `string` into an array of its words.
44744     *
44745     * @static
44746     * @memberOf _
44747     * @category String
44748     * @param {string} [string=''] The string to inspect.
44749     * @param {RegExp|string} [pattern] The pattern to match words.
44750     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44751     * @returns {Array} Returns the words of `string`.
44752     * @example
44753     *
44754     * _.words('fred, barney, & pebbles');
44755     * // => ['fred', 'barney', 'pebbles']
44756     *
44757     * _.words('fred, barney, & pebbles', /[^, ]+/g);
44758     * // => ['fred', 'barney', '&', 'pebbles']
44759     */
44760    function words(string, pattern, guard) {
44761      if (guard && isIterateeCall(string, pattern, guard)) {
44762        pattern = undefined;
44763      }
44764      string = baseToString(string);
44765      return string.match(pattern || reWords) || [];
44766    }
44767
44768    /*------------------------------------------------------------------------*/
44769
44770    /**
44771     * Attempts to invoke `func`, returning either the result or the caught error
44772     * object. Any additional arguments are provided to `func` when it is invoked.
44773     *
44774     * @static
44775     * @memberOf _
44776     * @category Utility
44777     * @param {Function} func The function to attempt.
44778     * @returns {*} Returns the `func` result or error object.
44779     * @example
44780     *
44781     * // avoid throwing errors for invalid selectors
44782     * var elements = _.attempt(function(selector) {
44783     *   return document.querySelectorAll(selector);
44784     * }, '>_>');
44785     *
44786     * if (_.isError(elements)) {
44787     *   elements = [];
44788     * }
44789     */
44790    var attempt = restParam(function(func, args) {
44791      try {
44792        return func.apply(undefined, args);
44793      } catch(e) {
44794        return isError(e) ? e : new Error(e);
44795      }
44796    });
44797
44798    /**
44799     * Creates a function that invokes `func` with the `this` binding of `thisArg`
44800     * and arguments of the created function. If `func` is a property name the
44801     * created callback returns the property value for a given element. If `func`
44802     * is an object the created callback returns `true` for elements that contain
44803     * the equivalent object properties, otherwise it returns `false`.
44804     *
44805     * @static
44806     * @memberOf _
44807     * @alias iteratee
44808     * @category Utility
44809     * @param {*} [func=_.identity] The value to convert to a callback.
44810     * @param {*} [thisArg] The `this` binding of `func`.
44811     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
44812     * @returns {Function} Returns the callback.
44813     * @example
44814     *
44815     * var users = [
44816     *   { 'user': 'barney', 'age': 36 },
44817     *   { 'user': 'fred',   'age': 40 }
44818     * ];
44819     *
44820     * // wrap to create custom callback shorthands
44821     * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
44822     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
44823     *   if (!match) {
44824     *     return callback(func, thisArg);
44825     *   }
44826     *   return function(object) {
44827     *     return match[2] == 'gt'
44828     *       ? object[match[1]] > match[3]
44829     *       : object[match[1]] < match[3];
44830     *   };
44831     * });
44832     *
44833     * _.filter(users, 'age__gt36');
44834     * // => [{ 'user': 'fred', 'age': 40 }]
44835     */
44836    function callback(func, thisArg, guard) {
44837      if (guard && isIterateeCall(func, thisArg, guard)) {
44838        thisArg = undefined;
44839      }
44840      return isObjectLike(func)
44841        ? matches(func)
44842        : baseCallback(func, thisArg);
44843    }
44844
44845    /**
44846     * Creates a function that returns `value`.
44847     *
44848     * @static
44849     * @memberOf _
44850     * @category Utility
44851     * @param {*} value The value to return from the new function.
44852     * @returns {Function} Returns the new function.
44853     * @example
44854     *
44855     * var object = { 'user': 'fred' };
44856     * var getter = _.constant(object);
44857     *
44858     * getter() === object;
44859     * // => true
44860     */
44861    function constant(value) {
44862      return function() {
44863        return value;
44864      };
44865    }
44866
44867    /**
44868     * This method returns the first argument provided to it.
44869     *
44870     * @static
44871     * @memberOf _
44872     * @category Utility
44873     * @param {*} value Any value.
44874     * @returns {*} Returns `value`.
44875     * @example
44876     *
44877     * var object = { 'user': 'fred' };
44878     *
44879     * _.identity(object) === object;
44880     * // => true
44881     */
44882    function identity(value) {
44883      return value;
44884    }
44885
44886    /**
44887     * Creates a function that performs a deep comparison between a given object
44888     * and `source`, returning `true` if the given object has equivalent property
44889     * values, else `false`.
44890     *
44891     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
44892     * numbers, `Object` objects, regexes, and strings. Objects are compared by
44893     * their own, not inherited, enumerable properties. For comparing a single
44894     * own or inherited property value see `_.matchesProperty`.
44895     *
44896     * @static
44897     * @memberOf _
44898     * @category Utility
44899     * @param {Object} source The object of property values to match.
44900     * @returns {Function} Returns the new function.
44901     * @example
44902     *
44903     * var users = [
44904     *   { 'user': 'barney', 'age': 36, 'active': true },
44905     *   { 'user': 'fred',   'age': 40, 'active': false }
44906     * ];
44907     *
44908     * _.filter(users, _.matches({ 'age': 40, 'active': false }));
44909     * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
44910     */
44911    function matches(source) {
44912      return baseMatches(baseClone(source, true));
44913    }
44914
44915    /**
44916     * Creates a function that compares the property value of `path` on a given
44917     * object to `value`.
44918     *
44919     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
44920     * numbers, `Object` objects, regexes, and strings. Objects are compared by
44921     * their own, not inherited, enumerable properties.
44922     *
44923     * @static
44924     * @memberOf _
44925     * @category Utility
44926     * @param {Array|string} path The path of the property to get.
44927     * @param {*} srcValue The value to match.
44928     * @returns {Function} Returns the new function.
44929     * @example
44930     *
44931     * var users = [
44932     *   { 'user': 'barney' },
44933     *   { 'user': 'fred' }
44934     * ];
44935     *
44936     * _.find(users, _.matchesProperty('user', 'fred'));
44937     * // => { 'user': 'fred' }
44938     */
44939    function matchesProperty(path, srcValue) {
44940      return baseMatchesProperty(path, baseClone(srcValue, true));
44941    }
44942
44943    /**
44944     * Creates a function that invokes the method at `path` on a given object.
44945     * Any additional arguments are provided to the invoked method.
44946     *
44947     * @static
44948     * @memberOf _
44949     * @category Utility
44950     * @param {Array|string} path The path of the method to invoke.
44951     * @param {...*} [args] The arguments to invoke the method with.
44952     * @returns {Function} Returns the new function.
44953     * @example
44954     *
44955     * var objects = [
44956     *   { 'a': { 'b': { 'c': _.constant(2) } } },
44957     *   { 'a': { 'b': { 'c': _.constant(1) } } }
44958     * ];
44959     *
44960     * _.map(objects, _.method('a.b.c'));
44961     * // => [2, 1]
44962     *
44963     * _.invoke(_.sortBy(objects, _.method(['a', 'b', 'c'])), 'a.b.c');
44964     * // => [1, 2]
44965     */
44966    var method = restParam(function(path, args) {
44967      return function(object) {
44968        return invokePath(object, path, args);
44969      };
44970    });
44971
44972    /**
44973     * The opposite of `_.method`; this method creates a function that invokes
44974     * the method at a given path on `object`. Any additional arguments are
44975     * provided to the invoked method.
44976     *
44977     * @static
44978     * @memberOf _
44979     * @category Utility
44980     * @param {Object} object The object to query.
44981     * @param {...*} [args] The arguments to invoke the method with.
44982     * @returns {Function} Returns the new function.
44983     * @example
44984     *
44985     * var array = _.times(3, _.constant),
44986     *     object = { 'a': array, 'b': array, 'c': array };
44987     *
44988     * _.map(['a[2]', 'c[0]'], _.methodOf(object));
44989     * // => [2, 0]
44990     *
44991     * _.map([['a', '2'], ['c', '0']], _.methodOf(object));
44992     * // => [2, 0]
44993     */
44994    var methodOf = restParam(function(object, args) {
44995      return function(path) {
44996        return invokePath(object, path, args);
44997      };
44998    });
44999
45000    /**
45001     * Adds all own enumerable function properties of a source object to the
45002     * destination object. If `object` is a function then methods are added to
45003     * its prototype as well.
45004     *
45005     * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
45006     * avoid conflicts caused by modifying the original.
45007     *
45008     * @static
45009     * @memberOf _
45010     * @category Utility
45011     * @param {Function|Object} [object=lodash] The destination object.
45012     * @param {Object} source The object of functions to add.
45013     * @param {Object} [options] The options object.
45014     * @param {boolean} [options.chain=true] Specify whether the functions added
45015     *  are chainable.
45016     * @returns {Function|Object} Returns `object`.
45017     * @example
45018     *
45019     * function vowels(string) {
45020     *   return _.filter(string, function(v) {
45021     *     return /[aeiou]/i.test(v);
45022     *   });
45023     * }
45024     *
45025     * _.mixin({ 'vowels': vowels });
45026     * _.vowels('fred');
45027     * // => ['e']
45028     *
45029     * _('fred').vowels().value();
45030     * // => ['e']
45031     *
45032     * _.mixin({ 'vowels': vowels }, { 'chain': false });
45033     * _('fred').vowels();
45034     * // => ['e']
45035     */
45036    function mixin(object, source, options) {
45037      if (options == null) {
45038        var isObj = isObject(source),
45039            props = isObj ? keys(source) : undefined,
45040            methodNames = (props && props.length) ? baseFunctions(source, props) : undefined;
45041
45042        if (!(methodNames ? methodNames.length : isObj)) {
45043          methodNames = false;
45044          options = source;
45045          source = object;
45046          object = this;
45047        }
45048      }
45049      if (!methodNames) {
45050        methodNames = baseFunctions(source, keys(source));
45051      }
45052      var chain = true,
45053          index = -1,
45054          isFunc = isFunction(object),
45055          length = methodNames.length;
45056
45057      if (options === false) {
45058        chain = false;
45059      } else if (isObject(options) && 'chain' in options) {
45060        chain = options.chain;
45061      }
45062      while (++index < length) {
45063        var methodName = methodNames[index],
45064            func = source[methodName];
45065
45066        object[methodName] = func;
45067        if (isFunc) {
45068          object.prototype[methodName] = (function(func) {
45069            return function() {
45070              var chainAll = this.__chain__;
45071              if (chain || chainAll) {
45072                var result = object(this.__wrapped__),
45073                    actions = result.__actions__ = arrayCopy(this.__actions__);
45074
45075                actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
45076                result.__chain__ = chainAll;
45077                return result;
45078              }
45079              return func.apply(object, arrayPush([this.value()], arguments));
45080            };
45081          }(func));
45082        }
45083      }
45084      return object;
45085    }
45086
45087    /**
45088     * Reverts the `_` variable to its previous value and returns a reference to
45089     * the `lodash` function.
45090     *
45091     * @static
45092     * @memberOf _
45093     * @category Utility
45094     * @returns {Function} Returns the `lodash` function.
45095     * @example
45096     *
45097     * var lodash = _.noConflict();
45098     */
45099    function noConflict() {
45100      root._ = oldDash;
45101      return this;
45102    }
45103
45104    /**
45105     * A no-operation function that returns `undefined` regardless of the
45106     * arguments it receives.
45107     *
45108     * @static
45109     * @memberOf _
45110     * @category Utility
45111     * @example
45112     *
45113     * var object = { 'user': 'fred' };
45114     *
45115     * _.noop(object) === undefined;
45116     * // => true
45117     */
45118    function noop() {
45119      // No operation performed.
45120    }
45121
45122    /**
45123     * Creates a function that returns the property value at `path` on a
45124     * given object.
45125     *
45126     * @static
45127     * @memberOf _
45128     * @category Utility
45129     * @param {Array|string} path The path of the property to get.
45130     * @returns {Function} Returns the new function.
45131     * @example
45132     *
45133     * var objects = [
45134     *   { 'a': { 'b': { 'c': 2 } } },
45135     *   { 'a': { 'b': { 'c': 1 } } }
45136     * ];
45137     *
45138     * _.map(objects, _.property('a.b.c'));
45139     * // => [2, 1]
45140     *
45141     * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
45142     * // => [1, 2]
45143     */
45144    function property(path) {
45145      return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
45146    }
45147
45148    /**
45149     * The opposite of `_.property`; this method creates a function that returns
45150     * the property value at a given path on `object`.
45151     *
45152     * @static
45153     * @memberOf _
45154     * @category Utility
45155     * @param {Object} object The object to query.
45156     * @returns {Function} Returns the new function.
45157     * @example
45158     *
45159     * var array = [0, 1, 2],
45160     *     object = { 'a': array, 'b': array, 'c': array };
45161     *
45162     * _.map(['a[2]', 'c[0]'], _.propertyOf(object));
45163     * // => [2, 0]
45164     *
45165     * _.map([['a', '2'], ['c', '0']], _.propertyOf(object));
45166     * // => [2, 0]
45167     */
45168    function propertyOf(object) {
45169      return function(path) {
45170        return baseGet(object, toPath(path), path + '');
45171      };
45172    }
45173
45174    /**
45175     * Creates an array of numbers (positive and/or negative) progressing from
45176     * `start` up to, but not including, `end`. If `end` is not specified it is
45177     * set to `start` with `start` then set to `0`. If `end` is less than `start`
45178     * a zero-length range is created unless a negative `step` is specified.
45179     *
45180     * @static
45181     * @memberOf _
45182     * @category Utility
45183     * @param {number} [start=0] The start of the range.
45184     * @param {number} end The end of the range.
45185     * @param {number} [step=1] The value to increment or decrement by.
45186     * @returns {Array} Returns the new array of numbers.
45187     * @example
45188     *
45189     * _.range(4);
45190     * // => [0, 1, 2, 3]
45191     *
45192     * _.range(1, 5);
45193     * // => [1, 2, 3, 4]
45194     *
45195     * _.range(0, 20, 5);
45196     * // => [0, 5, 10, 15]
45197     *
45198     * _.range(0, -4, -1);
45199     * // => [0, -1, -2, -3]
45200     *
45201     * _.range(1, 4, 0);
45202     * // => [1, 1, 1]
45203     *
45204     * _.range(0);
45205     * // => []
45206     */
45207    function range(start, end, step) {
45208      if (step && isIterateeCall(start, end, step)) {
45209        end = step = undefined;
45210      }
45211      start = +start || 0;
45212      step = step == null ? 1 : (+step || 0);
45213
45214      if (end == null) {
45215        end = start;
45216        start = 0;
45217      } else {
45218        end = +end || 0;
45219      }
45220      // Use `Array(length)` so engines like Chakra and V8 avoid slower modes.
45221      // See https://youtu.be/XAqIpGU8ZZk#t=17m25s for more details.
45222      var index = -1,
45223          length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
45224          result = Array(length);
45225
45226      while (++index < length) {
45227        result[index] = start;
45228        start += step;
45229      }
45230      return result;
45231    }
45232
45233    /**
45234     * Invokes the iteratee function `n` times, returning an array of the results
45235     * of each invocation. The `iteratee` is bound to `thisArg` and invoked with
45236     * one argument; (index).
45237     *
45238     * @static
45239     * @memberOf _
45240     * @category Utility
45241     * @param {number} n The number of times to invoke `iteratee`.
45242     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
45243     * @param {*} [thisArg] The `this` binding of `iteratee`.
45244     * @returns {Array} Returns the array of results.
45245     * @example
45246     *
45247     * var diceRolls = _.times(3, _.partial(_.random, 1, 6, false));
45248     * // => [3, 6, 4]
45249     *
45250     * _.times(3, function(n) {
45251     *   mage.castSpell(n);
45252     * });
45253     * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2`
45254     *
45255     * _.times(3, function(n) {
45256     *   this.cast(n);
45257     * }, mage);
45258     * // => also invokes `mage.castSpell(n)` three times
45259     */
45260    function times(n, iteratee, thisArg) {
45261      n = nativeFloor(n);
45262
45263      // Exit early to avoid a JSC JIT bug in Safari 8
45264      // where `Array(0)` is treated as `Array(1)`.
45265      if (n < 1 || !nativeIsFinite(n)) {
45266        return [];
45267      }
45268      var index = -1,
45269          result = Array(nativeMin(n, MAX_ARRAY_LENGTH));
45270
45271      iteratee = bindCallback(iteratee, thisArg, 1);
45272      while (++index < n) {
45273        if (index < MAX_ARRAY_LENGTH) {
45274          result[index] = iteratee(index);
45275        } else {
45276          iteratee(index);
45277        }
45278      }
45279      return result;
45280    }
45281
45282    /**
45283     * Generates a unique ID. If `prefix` is provided the ID is appended to it.
45284     *
45285     * @static
45286     * @memberOf _
45287     * @category Utility
45288     * @param {string} [prefix] The value to prefix the ID with.
45289     * @returns {string} Returns the unique ID.
45290     * @example
45291     *
45292     * _.uniqueId('contact_');
45293     * // => 'contact_104'
45294     *
45295     * _.uniqueId();
45296     * // => '105'
45297     */
45298    function uniqueId(prefix) {
45299      var id = ++idCounter;
45300      return baseToString(prefix) + id;
45301    }
45302
45303    /*------------------------------------------------------------------------*/
45304
45305    /**
45306     * Adds two numbers.
45307     *
45308     * @static
45309     * @memberOf _
45310     * @category Math
45311     * @param {number} augend The first number to add.
45312     * @param {number} addend The second number to add.
45313     * @returns {number} Returns the sum.
45314     * @example
45315     *
45316     * _.add(6, 4);
45317     * // => 10
45318     */
45319    function add(augend, addend) {
45320      return (+augend || 0) + (+addend || 0);
45321    }
45322
45323    /**
45324     * Calculates `n` rounded up to `precision`.
45325     *
45326     * @static
45327     * @memberOf _
45328     * @category Math
45329     * @param {number} n The number to round up.
45330     * @param {number} [precision=0] The precision to round up to.
45331     * @returns {number} Returns the rounded up number.
45332     * @example
45333     *
45334     * _.ceil(4.006);
45335     * // => 5
45336     *
45337     * _.ceil(6.004, 2);
45338     * // => 6.01
45339     *
45340     * _.ceil(6040, -2);
45341     * // => 6100
45342     */
45343    var ceil = createRound('ceil');
45344
45345    /**
45346     * Calculates `n` rounded down to `precision`.
45347     *
45348     * @static
45349     * @memberOf _
45350     * @category Math
45351     * @param {number} n The number to round down.
45352     * @param {number} [precision=0] The precision to round down to.
45353     * @returns {number} Returns the rounded down number.
45354     * @example
45355     *
45356     * _.floor(4.006);
45357     * // => 4
45358     *
45359     * _.floor(0.046, 2);
45360     * // => 0.04
45361     *
45362     * _.floor(4060, -2);
45363     * // => 4000
45364     */
45365    var floor = createRound('floor');
45366
45367    /**
45368     * Gets the maximum value of `collection`. If `collection` is empty or falsey
45369     * `-Infinity` is returned. If an iteratee function is provided it is invoked
45370     * for each value in `collection` to generate the criterion by which the value
45371     * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
45372     * arguments: (value, index, collection).
45373     *
45374     * If a property name is provided for `iteratee` the created `_.property`
45375     * style callback returns the property value of the given element.
45376     *
45377     * If a value is also provided for `thisArg` the created `_.matchesProperty`
45378     * style callback returns `true` for elements that have a matching property
45379     * value, else `false`.
45380     *
45381     * If an object is provided for `iteratee` the created `_.matches` style
45382     * callback returns `true` for elements that have the properties of the given
45383     * object, else `false`.
45384     *
45385     * @static
45386     * @memberOf _
45387     * @category Math
45388     * @param {Array|Object|string} collection The collection to iterate over.
45389     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
45390     * @param {*} [thisArg] The `this` binding of `iteratee`.
45391     * @returns {*} Returns the maximum value.
45392     * @example
45393     *
45394     * _.max([4, 2, 8, 6]);
45395     * // => 8
45396     *
45397     * _.max([]);
45398     * // => -Infinity
45399     *
45400     * var users = [
45401     *   { 'user': 'barney', 'age': 36 },
45402     *   { 'user': 'fred',   'age': 40 }
45403     * ];
45404     *
45405     * _.max(users, function(chr) {
45406     *   return chr.age;
45407     * });
45408     * // => { 'user': 'fred', 'age': 40 }
45409     *
45410     * // using the `_.property` callback shorthand
45411     * _.max(users, 'age');
45412     * // => { 'user': 'fred', 'age': 40 }
45413     */
45414    var max = createExtremum(gt, NEGATIVE_INFINITY);
45415
45416    /**
45417     * Gets the minimum value of `collection`. If `collection` is empty or falsey
45418     * `Infinity` is returned. If an iteratee function is provided it is invoked
45419     * for each value in `collection` to generate the criterion by which the value
45420     * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
45421     * arguments: (value, index, collection).
45422     *
45423     * If a property name is provided for `iteratee` the created `_.property`
45424     * style callback returns the property value of the given element.
45425     *
45426     * If a value is also provided for `thisArg` the created `_.matchesProperty`
45427     * style callback returns `true` for elements that have a matching property
45428     * value, else `false`.
45429     *
45430     * If an object is provided for `iteratee` the created `_.matches` style
45431     * callback returns `true` for elements that have the properties of the given
45432     * object, else `false`.
45433     *
45434     * @static
45435     * @memberOf _
45436     * @category Math
45437     * @param {Array|Object|string} collection The collection to iterate over.
45438     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
45439     * @param {*} [thisArg] The `this` binding of `iteratee`.
45440     * @returns {*} Returns the minimum value.
45441     * @example
45442     *
45443     * _.min([4, 2, 8, 6]);
45444     * // => 2
45445     *
45446     * _.min([]);
45447     * // => Infinity
45448     *
45449     * var users = [
45450     *   { 'user': 'barney', 'age': 36 },
45451     *   { 'user': 'fred',   'age': 40 }
45452     * ];
45453     *
45454     * _.min(users, function(chr) {
45455     *   return chr.age;
45456     * });
45457     * // => { 'user': 'barney', 'age': 36 }
45458     *
45459     * // using the `_.property` callback shorthand
45460     * _.min(users, 'age');
45461     * // => { 'user': 'barney', 'age': 36 }
45462     */
45463    var min = createExtremum(lt, POSITIVE_INFINITY);
45464
45465    /**
45466     * Calculates `n` rounded to `precision`.
45467     *
45468     * @static
45469     * @memberOf _
45470     * @category Math
45471     * @param {number} n The number to round.
45472     * @param {number} [precision=0] The precision to round to.
45473     * @returns {number} Returns the rounded number.
45474     * @example
45475     *
45476     * _.round(4.006);
45477     * // => 4
45478     *
45479     * _.round(4.006, 2);
45480     * // => 4.01
45481     *
45482     * _.round(4060, -2);
45483     * // => 4100
45484     */
45485    var round = createRound('round');
45486
45487    /**
45488     * Gets the sum of the values in `collection`.
45489     *
45490     * @static
45491     * @memberOf _
45492     * @category Math
45493     * @param {Array|Object|string} collection The collection to iterate over.
45494     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
45495     * @param {*} [thisArg] The `this` binding of `iteratee`.
45496     * @returns {number} Returns the sum.
45497     * @example
45498     *
45499     * _.sum([4, 6]);
45500     * // => 10
45501     *
45502     * _.sum({ 'a': 4, 'b': 6 });
45503     * // => 10
45504     *
45505     * var objects = [
45506     *   { 'n': 4 },
45507     *   { 'n': 6 }
45508     * ];
45509     *
45510     * _.sum(objects, function(object) {
45511     *   return object.n;
45512     * });
45513     * // => 10
45514     *
45515     * // using the `_.property` callback shorthand
45516     * _.sum(objects, 'n');
45517     * // => 10
45518     */
45519    function sum(collection, iteratee, thisArg) {
45520      if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
45521        iteratee = undefined;
45522      }
45523      iteratee = getCallback(iteratee, thisArg, 3);
45524      return iteratee.length == 1
45525        ? arraySum(isArray(collection) ? collection : toIterable(collection), iteratee)
45526        : baseSum(collection, iteratee);
45527    }
45528
45529    /*------------------------------------------------------------------------*/
45530
45531    // Ensure wrappers are instances of `baseLodash`.
45532    lodash.prototype = baseLodash.prototype;
45533
45534    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
45535    LodashWrapper.prototype.constructor = LodashWrapper;
45536
45537    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
45538    LazyWrapper.prototype.constructor = LazyWrapper;
45539
45540    // Add functions to the `Map` cache.
45541    MapCache.prototype['delete'] = mapDelete;
45542    MapCache.prototype.get = mapGet;
45543    MapCache.prototype.has = mapHas;
45544    MapCache.prototype.set = mapSet;
45545
45546    // Add functions to the `Set` cache.
45547    SetCache.prototype.push = cachePush;
45548
45549    // Assign cache to `_.memoize`.
45550    memoize.Cache = MapCache;
45551
45552    // Add functions that return wrapped values when chaining.
45553    lodash.after = after;
45554    lodash.ary = ary;
45555    lodash.assign = assign;
45556    lodash.at = at;
45557    lodash.before = before;
45558    lodash.bind = bind;
45559    lodash.bindAll = bindAll;
45560    lodash.bindKey = bindKey;
45561    lodash.callback = callback;
45562    lodash.chain = chain;
45563    lodash.chunk = chunk;
45564    lodash.compact = compact;
45565    lodash.constant = constant;
45566    lodash.countBy = countBy;
45567    lodash.create = create;
45568    lodash.curry = curry;
45569    lodash.curryRight = curryRight;
45570    lodash.debounce = debounce;
45571    lodash.defaults = defaults;
45572    lodash.defaultsDeep = defaultsDeep;
45573    lodash.defer = defer;
45574    lodash.delay = delay;
45575    lodash.difference = difference;
45576    lodash.drop = drop;
45577    lodash.dropRight = dropRight;
45578    lodash.dropRightWhile = dropRightWhile;
45579    lodash.dropWhile = dropWhile;
45580    lodash.fill = fill;
45581    lodash.filter = filter;
45582    lodash.flatten = flatten;
45583    lodash.flattenDeep = flattenDeep;
45584    lodash.flow = flow;
45585    lodash.flowRight = flowRight;
45586    lodash.forEach = forEach;
45587    lodash.forEachRight = forEachRight;
45588    lodash.forIn = forIn;
45589    lodash.forInRight = forInRight;
45590    lodash.forOwn = forOwn;
45591    lodash.forOwnRight = forOwnRight;
45592    lodash.functions = functions;
45593    lodash.groupBy = groupBy;
45594    lodash.indexBy = indexBy;
45595    lodash.initial = initial;
45596    lodash.intersection = intersection;
45597    lodash.invert = invert;
45598    lodash.invoke = invoke;
45599    lodash.keys = keys;
45600    lodash.keysIn = keysIn;
45601    lodash.map = map;
45602    lodash.mapKeys = mapKeys;
45603    lodash.mapValues = mapValues;
45604    lodash.matches = matches;
45605    lodash.matchesProperty = matchesProperty;
45606    lodash.memoize = memoize;
45607    lodash.merge = merge;
45608    lodash.method = method;
45609    lodash.methodOf = methodOf;
45610    lodash.mixin = mixin;
45611    lodash.modArgs = modArgs;
45612    lodash.negate = negate;
45613    lodash.omit = omit;
45614    lodash.once = once;
45615    lodash.pairs = pairs;
45616    lodash.partial = partial;
45617    lodash.partialRight = partialRight;
45618    lodash.partition = partition;
45619    lodash.pick = pick;
45620    lodash.pluck = pluck;
45621    lodash.property = property;
45622    lodash.propertyOf = propertyOf;
45623    lodash.pull = pull;
45624    lodash.pullAt = pullAt;
45625    lodash.range = range;
45626    lodash.rearg = rearg;
45627    lodash.reject = reject;
45628    lodash.remove = remove;
45629    lodash.rest = rest;
45630    lodash.restParam = restParam;
45631    lodash.set = set;
45632    lodash.shuffle = shuffle;
45633    lodash.slice = slice;
45634    lodash.sortBy = sortBy;
45635    lodash.sortByAll = sortByAll;
45636    lodash.sortByOrder = sortByOrder;
45637    lodash.spread = spread;
45638    lodash.take = take;
45639    lodash.takeRight = takeRight;
45640    lodash.takeRightWhile = takeRightWhile;
45641    lodash.takeWhile = takeWhile;
45642    lodash.tap = tap;
45643    lodash.throttle = throttle;
45644    lodash.thru = thru;
45645    lodash.times = times;
45646    lodash.toArray = toArray;
45647    lodash.toPlainObject = toPlainObject;
45648    lodash.transform = transform;
45649    lodash.union = union;
45650    lodash.uniq = uniq;
45651    lodash.unzip = unzip;
45652    lodash.unzipWith = unzipWith;
45653    lodash.values = values;
45654    lodash.valuesIn = valuesIn;
45655    lodash.where = where;
45656    lodash.without = without;
45657    lodash.wrap = wrap;
45658    lodash.xor = xor;
45659    lodash.zip = zip;
45660    lodash.zipObject = zipObject;
45661    lodash.zipWith = zipWith;
45662
45663    // Add aliases.
45664    lodash.backflow = flowRight;
45665    lodash.collect = map;
45666    lodash.compose = flowRight;
45667    lodash.each = forEach;
45668    lodash.eachRight = forEachRight;
45669    lodash.extend = assign;
45670    lodash.iteratee = callback;
45671    lodash.methods = functions;
45672    lodash.object = zipObject;
45673    lodash.select = filter;
45674    lodash.tail = rest;
45675    lodash.unique = uniq;
45676
45677    // Add functions to `lodash.prototype`.
45678    mixin(lodash, lodash);
45679
45680    /*------------------------------------------------------------------------*/
45681
45682    // Add functions that return unwrapped values when chaining.
45683    lodash.add = add;
45684    lodash.attempt = attempt;
45685    lodash.camelCase = camelCase;
45686    lodash.capitalize = capitalize;
45687    lodash.ceil = ceil;
45688    lodash.clone = clone;
45689    lodash.cloneDeep = cloneDeep;
45690    lodash.deburr = deburr;
45691    lodash.endsWith = endsWith;
45692    lodash.escape = escape;
45693    lodash.escapeRegExp = escapeRegExp;
45694    lodash.every = every;
45695    lodash.find = find;
45696    lodash.findIndex = findIndex;
45697    lodash.findKey = findKey;
45698    lodash.findLast = findLast;
45699    lodash.findLastIndex = findLastIndex;
45700    lodash.findLastKey = findLastKey;
45701    lodash.findWhere = findWhere;
45702    lodash.first = first;
45703    lodash.floor = floor;
45704    lodash.get = get;
45705    lodash.gt = gt;
45706    lodash.gte = gte;
45707    lodash.has = has;
45708    lodash.identity = identity;
45709    lodash.includes = includes;
45710    lodash.indexOf = indexOf;
45711    lodash.inRange = inRange;
45712    lodash.isArguments = isArguments;
45713    lodash.isArray = isArray;
45714    lodash.isBoolean = isBoolean;
45715    lodash.isDate = isDate;
45716    lodash.isElement = isElement;
45717    lodash.isEmpty = isEmpty;
45718    lodash.isEqual = isEqual;
45719    lodash.isError = isError;
45720    lodash.isFinite = isFinite;
45721    lodash.isFunction = isFunction;
45722    lodash.isMatch = isMatch;
45723    lodash.isNaN = isNaN;
45724    lodash.isNative = isNative;
45725    lodash.isNull = isNull;
45726    lodash.isNumber = isNumber;
45727    lodash.isObject = isObject;
45728    lodash.isPlainObject = isPlainObject;
45729    lodash.isRegExp = isRegExp;
45730    lodash.isString = isString;
45731    lodash.isTypedArray = isTypedArray;
45732    lodash.isUndefined = isUndefined;
45733    lodash.kebabCase = kebabCase;
45734    lodash.last = last;
45735    lodash.lastIndexOf = lastIndexOf;
45736    lodash.lt = lt;
45737    lodash.lte = lte;
45738    lodash.max = max;
45739    lodash.min = min;
45740    lodash.noConflict = noConflict;
45741    lodash.noop = noop;
45742    lodash.now = now;
45743    lodash.pad = pad;
45744    lodash.padLeft = padLeft;
45745    lodash.padRight = padRight;
45746    lodash.parseInt = parseInt;
45747    lodash.random = random;
45748    lodash.reduce = reduce;
45749    lodash.reduceRight = reduceRight;
45750    lodash.repeat = repeat;
45751    lodash.result = result;
45752    lodash.round = round;
45753    lodash.runInContext = runInContext;
45754    lodash.size = size;
45755    lodash.snakeCase = snakeCase;
45756    lodash.some = some;
45757    lodash.sortedIndex = sortedIndex;
45758    lodash.sortedLastIndex = sortedLastIndex;
45759    lodash.startCase = startCase;
45760    lodash.startsWith = startsWith;
45761    lodash.sum = sum;
45762    lodash.template = template;
45763    lodash.trim = trim;
45764    lodash.trimLeft = trimLeft;
45765    lodash.trimRight = trimRight;
45766    lodash.trunc = trunc;
45767    lodash.unescape = unescape;
45768    lodash.uniqueId = uniqueId;
45769    lodash.words = words;
45770
45771    // Add aliases.
45772    lodash.all = every;
45773    lodash.any = some;
45774    lodash.contains = includes;
45775    lodash.eq = isEqual;
45776    lodash.detect = find;
45777    lodash.foldl = reduce;
45778    lodash.foldr = reduceRight;
45779    lodash.head = first;
45780    lodash.include = includes;
45781    lodash.inject = reduce;
45782
45783    mixin(lodash, (function() {
45784      var source = {};
45785      baseForOwn(lodash, function(func, methodName) {
45786        if (!lodash.prototype[methodName]) {
45787          source[methodName] = func;
45788        }
45789      });
45790      return source;
45791    }()), false);
45792
45793    /*------------------------------------------------------------------------*/
45794
45795    // Add functions capable of returning wrapped and unwrapped values when chaining.
45796    lodash.sample = sample;
45797
45798    lodash.prototype.sample = function(n) {
45799      if (!this.__chain__ && n == null) {
45800        return sample(this.value());
45801      }
45802      return this.thru(function(value) {
45803        return sample(value, n);
45804      });
45805    };
45806
45807    /*------------------------------------------------------------------------*/
45808
45809    /**
45810     * The semantic version number.
45811     *
45812     * @static
45813     * @memberOf _
45814     * @type string
45815     */
45816    lodash.VERSION = VERSION;
45817
45818    // Assign default placeholders.
45819    arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {
45820      lodash[methodName].placeholder = lodash;
45821    });
45822
45823    // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
45824    arrayEach(['drop', 'take'], function(methodName, index) {
45825      LazyWrapper.prototype[methodName] = function(n) {
45826        var filtered = this.__filtered__;
45827        if (filtered && !index) {
45828          return new LazyWrapper(this);
45829        }
45830        n = n == null ? 1 : nativeMax(nativeFloor(n) || 0, 0);
45831
45832        var result = this.clone();
45833        if (filtered) {
45834          result.__takeCount__ = nativeMin(result.__takeCount__, n);
45835        } else {
45836          result.__views__.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
45837        }
45838        return result;
45839      };
45840
45841      LazyWrapper.prototype[methodName + 'Right'] = function(n) {
45842        return this.reverse()[methodName](n).reverse();
45843      };
45844    });
45845
45846    // Add `LazyWrapper` methods that accept an `iteratee` value.
45847    arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
45848      var type = index + 1,
45849          isFilter = type != LAZY_MAP_FLAG;
45850
45851      LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
45852        var result = this.clone();
45853        result.__iteratees__.push({ 'iteratee': getCallback(iteratee, thisArg, 1), 'type': type });
45854        result.__filtered__ = result.__filtered__ || isFilter;
45855        return result;
45856      };
45857    });
45858
45859    // Add `LazyWrapper` methods for `_.first` and `_.last`.
45860    arrayEach(['first', 'last'], function(methodName, index) {
45861      var takeName = 'take' + (index ? 'Right' : '');
45862
45863      LazyWrapper.prototype[methodName] = function() {
45864        return this[takeName](1).value()[0];
45865      };
45866    });
45867
45868    // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
45869    arrayEach(['initial', 'rest'], function(methodName, index) {
45870      var dropName = 'drop' + (index ? '' : 'Right');
45871
45872      LazyWrapper.prototype[methodName] = function() {
45873        return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
45874      };
45875    });
45876
45877    // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
45878    arrayEach(['pluck', 'where'], function(methodName, index) {
45879      var operationName = index ? 'filter' : 'map',
45880          createCallback = index ? baseMatches : property;
45881
45882      LazyWrapper.prototype[methodName] = function(value) {
45883        return this[operationName](createCallback(value));
45884      };
45885    });
45886
45887    LazyWrapper.prototype.compact = function() {
45888      return this.filter(identity);
45889    };
45890
45891    LazyWrapper.prototype.reject = function(predicate, thisArg) {
45892      predicate = getCallback(predicate, thisArg, 1);
45893      return this.filter(function(value) {
45894        return !predicate(value);
45895      });
45896    };
45897
45898    LazyWrapper.prototype.slice = function(start, end) {
45899      start = start == null ? 0 : (+start || 0);
45900
45901      var result = this;
45902      if (result.__filtered__ && (start > 0 || end < 0)) {
45903        return new LazyWrapper(result);
45904      }
45905      if (start < 0) {
45906        result = result.takeRight(-start);
45907      } else if (start) {
45908        result = result.drop(start);
45909      }
45910      if (end !== undefined) {
45911        end = (+end || 0);
45912        result = end < 0 ? result.dropRight(-end) : result.take(end - start);
45913      }
45914      return result;
45915    };
45916
45917    LazyWrapper.prototype.takeRightWhile = function(predicate, thisArg) {
45918      return this.reverse().takeWhile(predicate, thisArg).reverse();
45919    };
45920
45921    LazyWrapper.prototype.toArray = function() {
45922      return this.take(POSITIVE_INFINITY);
45923    };
45924
45925    // Add `LazyWrapper` methods to `lodash.prototype`.
45926    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
45927      var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
45928          retUnwrapped = /^(?:first|last)$/.test(methodName),
45929          lodashFunc = lodash[retUnwrapped ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName];
45930
45931      if (!lodashFunc) {
45932        return;
45933      }
45934      lodash.prototype[methodName] = function() {
45935        var args = retUnwrapped ? [1] : arguments,
45936            chainAll = this.__chain__,
45937            value = this.__wrapped__,
45938            isHybrid = !!this.__actions__.length,
45939            isLazy = value instanceof LazyWrapper,
45940            iteratee = args[0],
45941            useLazy = isLazy || isArray(value);
45942
45943        if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
45944          // Avoid lazy use if the iteratee has a "length" value other than `1`.
45945          isLazy = useLazy = false;
45946        }
45947        var interceptor = function(value) {
45948          return (retUnwrapped && chainAll)
45949            ? lodashFunc(value, 1)[0]
45950            : lodashFunc.apply(undefined, arrayPush([value], args));
45951        };
45952
45953        var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined },
45954            onlyLazy = isLazy && !isHybrid;
45955
45956        if (retUnwrapped && !chainAll) {
45957          if (onlyLazy) {
45958            value = value.clone();
45959            value.__actions__.push(action);
45960            return func.call(value);
45961          }
45962          return lodashFunc.call(undefined, this.value())[0];
45963        }
45964        if (!retUnwrapped && useLazy) {
45965          value = onlyLazy ? value : new LazyWrapper(this);
45966          var result = func.apply(value, args);
45967          result.__actions__.push(action);
45968          return new LodashWrapper(result, chainAll);
45969        }
45970        return this.thru(interceptor);
45971      };
45972    });
45973
45974    // Add `Array` and `String` methods to `lodash.prototype`.
45975    arrayEach(['join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
45976      var func = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
45977          chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
45978          retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
45979
45980      lodash.prototype[methodName] = function() {
45981        var args = arguments;
45982        if (retUnwrapped && !this.__chain__) {
45983          return func.apply(this.value(), args);
45984        }
45985        return this[chainName](function(value) {
45986          return func.apply(value, args);
45987        });
45988      };
45989    });
45990
45991    // Map minified function names to their real names.
45992    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
45993      var lodashFunc = lodash[methodName];
45994      if (lodashFunc) {
45995        var key = lodashFunc.name,
45996            names = realNames[key] || (realNames[key] = []);
45997
45998        names.push({ 'name': methodName, 'func': lodashFunc });
45999      }
46000    });
46001
46002    realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': undefined }];
46003
46004    // Add functions to the lazy wrapper.
46005    LazyWrapper.prototype.clone = lazyClone;
46006    LazyWrapper.prototype.reverse = lazyReverse;
46007    LazyWrapper.prototype.value = lazyValue;
46008
46009    // Add chaining functions to the `lodash` wrapper.
46010    lodash.prototype.chain = wrapperChain;
46011    lodash.prototype.commit = wrapperCommit;
46012    lodash.prototype.concat = wrapperConcat;
46013    lodash.prototype.plant = wrapperPlant;
46014    lodash.prototype.reverse = wrapperReverse;
46015    lodash.prototype.toString = wrapperToString;
46016    lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
46017
46018    // Add function aliases to the `lodash` wrapper.
46019    lodash.prototype.collect = lodash.prototype.map;
46020    lodash.prototype.head = lodash.prototype.first;
46021    lodash.prototype.select = lodash.prototype.filter;
46022    lodash.prototype.tail = lodash.prototype.rest;
46023
46024    return lodash;
46025  }
46026
46027  /*--------------------------------------------------------------------------*/
46028
46029  // Export lodash.
46030  var _ = runInContext();
46031
46032  // Some AMD build optimizers like r.js check for condition patterns like the following:
46033  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
46034    // Expose lodash to the global object when an AMD loader is present to avoid
46035    // errors in cases where lodash is loaded by a script tag and not intended
46036    // as an AMD module. See http://requirejs.org/docs/errors.html#mismatch for
46037    // more details.
46038    root._ = _;
46039
46040    // Define as an anonymous module so, through path mapping, it can be
46041    // referenced as the "underscore" module.
46042    define(function() {
46043      return _;
46044    });
46045  }
46046  // Check for `exports` after `define` in case a build optimizer adds an `exports` object.
46047  else if (freeExports && freeModule) {
46048    // Export for Node.js or RingoJS.
46049    if (moduleExports) {
46050      (freeModule.exports = _)._ = _;
46051    }
46052    // Export for Rhino with CommonJS support.
46053    else {
46054      freeExports._ = _;
46055    }
46056  }
46057  else {
46058    // Export for a browser or Rhino.
46059    root._ = _;
46060  }
46061}.call(this));
46062
46063}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
46064
46065},{}],134:[function(require,module,exports){
46066'use strict';
46067
46068module.exports = require('./lib')
46069
46070},{"./lib":139}],135:[function(require,module,exports){
46071'use strict';
46072
46073var asap = require('asap/raw');
46074
46075function noop() {}
46076
46077// States:
46078//
46079// 0 - pending
46080// 1 - fulfilled with _value
46081// 2 - rejected with _value
46082// 3 - adopted the state of another promise, _value
46083//
46084// once the state is no longer pending (0) it is immutable
46085
46086// All `_` prefixed properties will be reduced to `_{random number}`
46087// at build time to obfuscate them and discourage their use.
46088// We don't use symbols or Object.defineProperty to fully hide them
46089// because the performance isn't good enough.
46090
46091
46092// to avoid using try/catch inside critical functions, we
46093// extract them to here.
46094var LAST_ERROR = null;
46095var IS_ERROR = {};
46096function getThen(obj) {
46097  try {
46098    return obj.then;
46099  } catch (ex) {
46100    LAST_ERROR = ex;
46101    return IS_ERROR;
46102  }
46103}
46104
46105function tryCallOne(fn, a) {
46106  try {
46107    return fn(a);
46108  } catch (ex) {
46109    LAST_ERROR = ex;
46110    return IS_ERROR;
46111  }
46112}
46113function tryCallTwo(fn, a, b) {
46114  try {
46115    fn(a, b);
46116  } catch (ex) {
46117    LAST_ERROR = ex;
46118    return IS_ERROR;
46119  }
46120}
46121
46122module.exports = Promise;
46123
46124function Promise(fn) {
46125  if (typeof this !== 'object') {
46126    throw new TypeError('Promises must be constructed via new');
46127  }
46128  if (typeof fn !== 'function') {
46129    throw new TypeError('Promise constructor\'s argument is not a function');
46130  }
46131  this._40 = 0;
46132  this._65 = 0;
46133  this._55 = null;
46134  this._72 = null;
46135  if (fn === noop) return;
46136  doResolve(fn, this);
46137}
46138Promise._37 = null;
46139Promise._87 = null;
46140Promise._61 = noop;
46141
46142Promise.prototype.then = function(onFulfilled, onRejected) {
46143  if (this.constructor !== Promise) {
46144    return safeThen(this, onFulfilled, onRejected);
46145  }
46146  var res = new Promise(noop);
46147  handle(this, new Handler(onFulfilled, onRejected, res));
46148  return res;
46149};
46150
46151function safeThen(self, onFulfilled, onRejected) {
46152  return new self.constructor(function (resolve, reject) {
46153    var res = new Promise(noop);
46154    res.then(resolve, reject);
46155    handle(self, new Handler(onFulfilled, onRejected, res));
46156  });
46157}
46158function handle(self, deferred) {
46159  while (self._65 === 3) {
46160    self = self._55;
46161  }
46162  if (Promise._37) {
46163    Promise._37(self);
46164  }
46165  if (self._65 === 0) {
46166    if (self._40 === 0) {
46167      self._40 = 1;
46168      self._72 = deferred;
46169      return;
46170    }
46171    if (self._40 === 1) {
46172      self._40 = 2;
46173      self._72 = [self._72, deferred];
46174      return;
46175    }
46176    self._72.push(deferred);
46177    return;
46178  }
46179  handleResolved(self, deferred);
46180}
46181
46182function handleResolved(self, deferred) {
46183  asap(function() {
46184    var cb = self._65 === 1 ? deferred.onFulfilled : deferred.onRejected;
46185    if (cb === null) {
46186      if (self._65 === 1) {
46187        resolve(deferred.promise, self._55);
46188      } else {
46189        reject(deferred.promise, self._55);
46190      }
46191      return;
46192    }
46193    var ret = tryCallOne(cb, self._55);
46194    if (ret === IS_ERROR) {
46195      reject(deferred.promise, LAST_ERROR);
46196    } else {
46197      resolve(deferred.promise, ret);
46198    }
46199  });
46200}
46201function resolve(self, newValue) {
46202  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
46203  if (newValue === self) {
46204    return reject(
46205      self,
46206      new TypeError('A promise cannot be resolved with itself.')
46207    );
46208  }
46209  if (
46210    newValue &&
46211    (typeof newValue === 'object' || typeof newValue === 'function')
46212  ) {
46213    var then = getThen(newValue);
46214    if (then === IS_ERROR) {
46215      return reject(self, LAST_ERROR);
46216    }
46217    if (
46218      then === self.then &&
46219      newValue instanceof Promise
46220    ) {
46221      self._65 = 3;
46222      self._55 = newValue;
46223      finale(self);
46224      return;
46225    } else if (typeof then === 'function') {
46226      doResolve(then.bind(newValue), self);
46227      return;
46228    }
46229  }
46230  self._65 = 1;
46231  self._55 = newValue;
46232  finale(self);
46233}
46234
46235function reject(self, newValue) {
46236  self._65 = 2;
46237  self._55 = newValue;
46238  if (Promise._87) {
46239    Promise._87(self, newValue);
46240  }
46241  finale(self);
46242}
46243function finale(self) {
46244  if (self._40 === 1) {
46245    handle(self, self._72);
46246    self._72 = null;
46247  }
46248  if (self._40 === 2) {
46249    for (var i = 0; i < self._72.length; i++) {
46250      handle(self, self._72[i]);
46251    }
46252    self._72 = null;
46253  }
46254}
46255
46256function Handler(onFulfilled, onRejected, promise){
46257  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
46258  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
46259  this.promise = promise;
46260}
46261
46262/**
46263 * Take a potentially misbehaving resolver function and make sure
46264 * onFulfilled and onRejected are only called once.
46265 *
46266 * Makes no guarantees about asynchrony.
46267 */
46268function doResolve(fn, promise) {
46269  var done = false;
46270  var res = tryCallTwo(fn, function (value) {
46271    if (done) return;
46272    done = true;
46273    resolve(promise, value);
46274  }, function (reason) {
46275    if (done) return;
46276    done = true;
46277    reject(promise, reason);
46278  });
46279  if (!done && res === IS_ERROR) {
46280    done = true;
46281    reject(promise, LAST_ERROR);
46282  }
46283}
46284
46285},{"asap/raw":130}],136:[function(require,module,exports){
46286'use strict';
46287
46288var Promise = require('./core.js');
46289
46290module.exports = Promise;
46291Promise.prototype.done = function (onFulfilled, onRejected) {
46292  var self = arguments.length ? this.then.apply(this, arguments) : this;
46293  self.then(null, function (err) {
46294    setTimeout(function () {
46295      throw err;
46296    }, 0);
46297  });
46298};
46299
46300},{"./core.js":135}],137:[function(require,module,exports){
46301'use strict';
46302
46303//This file contains the ES6 extensions to the core Promises/A+ API
46304
46305var Promise = require('./core.js');
46306
46307module.exports = Promise;
46308
46309/* Static Functions */
46310
46311var TRUE = valuePromise(true);
46312var FALSE = valuePromise(false);
46313var NULL = valuePromise(null);
46314var UNDEFINED = valuePromise(undefined);
46315var ZERO = valuePromise(0);
46316var EMPTYSTRING = valuePromise('');
46317
46318function valuePromise(value) {
46319  var p = new Promise(Promise._61);
46320  p._65 = 1;
46321  p._55 = value;
46322  return p;
46323}
46324Promise.resolve = function (value) {
46325  if (value instanceof Promise) return value;
46326
46327  if (value === null) return NULL;
46328  if (value === undefined) return UNDEFINED;
46329  if (value === true) return TRUE;
46330  if (value === false) return FALSE;
46331  if (value === 0) return ZERO;
46332  if (value === '') return EMPTYSTRING;
46333
46334  if (typeof value === 'object' || typeof value === 'function') {
46335    try {
46336      var then = value.then;
46337      if (typeof then === 'function') {
46338        return new Promise(then.bind(value));
46339      }
46340    } catch (ex) {
46341      return new Promise(function (resolve, reject) {
46342        reject(ex);
46343      });
46344    }
46345  }
46346  return valuePromise(value);
46347};
46348
46349Promise.all = function (arr) {
46350  var args = Array.prototype.slice.call(arr);
46351
46352  return new Promise(function (resolve, reject) {
46353    if (args.length === 0) return resolve([]);
46354    var remaining = args.length;
46355    function res(i, val) {
46356      if (val && (typeof val === 'object' || typeof val === 'function')) {
46357        if (val instanceof Promise && val.then === Promise.prototype.then) {
46358          while (val._65 === 3) {
46359            val = val._55;
46360          }
46361          if (val._65 === 1) return res(i, val._55);
46362          if (val._65 === 2) reject(val._55);
46363          val.then(function (val) {
46364            res(i, val);
46365          }, reject);
46366          return;
46367        } else {
46368          var then = val.then;
46369          if (typeof then === 'function') {
46370            var p = new Promise(then.bind(val));
46371            p.then(function (val) {
46372              res(i, val);
46373            }, reject);
46374            return;
46375          }
46376        }
46377      }
46378      args[i] = val;
46379      if (--remaining === 0) {
46380        resolve(args);
46381      }
46382    }
46383    for (var i = 0; i < args.length; i++) {
46384      res(i, args[i]);
46385    }
46386  });
46387};
46388
46389Promise.reject = function (value) {
46390  return new Promise(function (resolve, reject) {
46391    reject(value);
46392  });
46393};
46394
46395Promise.race = function (values) {
46396  return new Promise(function (resolve, reject) {
46397    values.forEach(function(value){
46398      Promise.resolve(value).then(resolve, reject);
46399    });
46400  });
46401};
46402
46403/* Prototype Methods */
46404
46405Promise.prototype['catch'] = function (onRejected) {
46406  return this.then(null, onRejected);
46407};
46408
46409},{"./core.js":135}],138:[function(require,module,exports){
46410'use strict';
46411
46412var Promise = require('./core.js');
46413
46414module.exports = Promise;
46415Promise.prototype['finally'] = function (f) {
46416  return this.then(function (value) {
46417    return Promise.resolve(f()).then(function () {
46418      return value;
46419    });
46420  }, function (err) {
46421    return Promise.resolve(f()).then(function () {
46422      throw err;
46423    });
46424  });
46425};
46426
46427},{"./core.js":135}],139:[function(require,module,exports){
46428'use strict';
46429
46430module.exports = require('./core.js');
46431require('./done.js');
46432require('./finally.js');
46433require('./es6-extensions.js');
46434require('./node-extensions.js');
46435require('./synchronous.js');
46436
46437},{"./core.js":135,"./done.js":136,"./es6-extensions.js":137,"./finally.js":138,"./node-extensions.js":140,"./synchronous.js":141}],140:[function(require,module,exports){
46438'use strict';
46439
46440// This file contains then/promise specific extensions that are only useful
46441// for node.js interop
46442
46443var Promise = require('./core.js');
46444var asap = require('asap');
46445
46446module.exports = Promise;
46447
46448/* Static Functions */
46449
46450Promise.denodeify = function (fn, argumentCount) {
46451  if (
46452    typeof argumentCount === 'number' && argumentCount !== Infinity
46453  ) {
46454    return denodeifyWithCount(fn, argumentCount);
46455  } else {
46456    return denodeifyWithoutCount(fn);
46457  }
46458};
46459
46460var callbackFn = (
46461  'function (err, res) {' +
46462  'if (err) { rj(err); } else { rs(res); }' +
46463  '}'
46464);
46465function denodeifyWithCount(fn, argumentCount) {
46466  var args = [];
46467  for (var i = 0; i < argumentCount; i++) {
46468    args.push('a' + i);
46469  }
46470  var body = [
46471    'return function (' + args.join(',') + ') {',
46472    'var self = this;',
46473    'return new Promise(function (rs, rj) {',
46474    'var res = fn.call(',
46475    ['self'].concat(args).concat([callbackFn]).join(','),
46476    ');',
46477    'if (res &&',
46478    '(typeof res === "object" || typeof res === "function") &&',
46479    'typeof res.then === "function"',
46480    ') {rs(res);}',
46481    '});',
46482    '};'
46483  ].join('');
46484  return Function(['Promise', 'fn'], body)(Promise, fn);
46485}
46486function denodeifyWithoutCount(fn) {
46487  var fnLength = Math.max(fn.length - 1, 3);
46488  var args = [];
46489  for (var i = 0; i < fnLength; i++) {
46490    args.push('a' + i);
46491  }
46492  var body = [
46493    'return function (' + args.join(',') + ') {',
46494    'var self = this;',
46495    'var args;',
46496    'var argLength = arguments.length;',
46497    'if (arguments.length > ' + fnLength + ') {',
46498    'args = new Array(arguments.length + 1);',
46499    'for (var i = 0; i < arguments.length; i++) {',
46500    'args[i] = arguments[i];',
46501    '}',
46502    '}',
46503    'return new Promise(function (rs, rj) {',
46504    'var cb = ' + callbackFn + ';',
46505    'var res;',
46506    'switch (argLength) {',
46507    args.concat(['extra']).map(function (_, index) {
46508      return (
46509        'case ' + (index) + ':' +
46510        'res = fn.call(' + ['self'].concat(args.slice(0, index)).concat('cb').join(',') + ');' +
46511        'break;'
46512      );
46513    }).join(''),
46514    'default:',
46515    'args[argLength] = cb;',
46516    'res = fn.apply(self, args);',
46517    '}',
46518    
46519    'if (res &&',
46520    '(typeof res === "object" || typeof res === "function") &&',
46521    'typeof res.then === "function"',
46522    ') {rs(res);}',
46523    '});',
46524    '};'
46525  ].join('');
46526
46527  return Function(
46528    ['Promise', 'fn'],
46529    body
46530  )(Promise, fn);
46531}
46532
46533Promise.nodeify = function (fn) {
46534  return function () {
46535    var args = Array.prototype.slice.call(arguments);
46536    var callback =
46537      typeof args[args.length - 1] === 'function' ? args.pop() : null;
46538    var ctx = this;
46539    try {
46540      return fn.apply(this, arguments).nodeify(callback, ctx);
46541    } catch (ex) {
46542      if (callback === null || typeof callback == 'undefined') {
46543        return new Promise(function (resolve, reject) {
46544          reject(ex);
46545        });
46546      } else {
46547        asap(function () {
46548          callback.call(ctx, ex);
46549        })
46550      }
46551    }
46552  }
46553};
46554
46555Promise.prototype.nodeify = function (callback, ctx) {
46556  if (typeof callback != 'function') return this;
46557
46558  this.then(function (value) {
46559    asap(function () {
46560      callback.call(ctx, null, value);
46561    });
46562  }, function (err) {
46563    asap(function () {
46564      callback.call(ctx, err);
46565    });
46566  });
46567};
46568
46569},{"./core.js":135,"asap":129}],141:[function(require,module,exports){
46570'use strict';
46571
46572var Promise = require('./core.js');
46573
46574module.exports = Promise;
46575Promise.enableSynchronous = function () {
46576  Promise.prototype.isPending = function() {
46577    return this.getState() == 0;
46578  };
46579
46580  Promise.prototype.isFulfilled = function() {
46581    return this.getState() == 1;
46582  };
46583
46584  Promise.prototype.isRejected = function() {
46585    return this.getState() == 2;
46586  };
46587
46588  Promise.prototype.getValue = function () {
46589    if (this._65 === 3) {
46590      return this._55.getValue();
46591    }
46592
46593    if (!this.isFulfilled()) {
46594      throw new Error('Cannot get a value of an unfulfilled promise.');
46595    }
46596
46597    return this._55;
46598  };
46599
46600  Promise.prototype.getReason = function () {
46601    if (this._65 === 3) {
46602      return this._55.getReason();
46603    }
46604
46605    if (!this.isRejected()) {
46606      throw new Error('Cannot get a rejection reason of a non-rejected promise.');
46607    }
46608
46609    return this._55;
46610  };
46611
46612  Promise.prototype.getState = function () {
46613    if (this._65 === 3) {
46614      return this._55.getState();
46615    }
46616    if (this._65 === -1 || this._65 === -2) {
46617      return 0;
46618    }
46619
46620    return this._65;
46621  };
46622};
46623
46624Promise.disableSynchronous = function() {
46625  Promise.prototype.isPending = undefined;
46626  Promise.prototype.isFulfilled = undefined;
46627  Promise.prototype.isRejected = undefined;
46628  Promise.prototype.getValue = undefined;
46629  Promise.prototype.getReason = undefined;
46630  Promise.prototype.getState = undefined;
46631};
46632
46633},{"./core.js":135}],142:[function(require,module,exports){
46634/*!
46635 * EventEmitter v4.2.11 - git.io/ee
46636 * Unlicense - http://unlicense.org/
46637 * Oliver Caldwell - http://oli.me.uk/
46638 * @preserve
46639 */
46640
46641;(function () {
46642    'use strict';
46643
46644    /**
46645     * Class for managing events.
46646     * Can be extended to provide event functionality in other classes.
46647     *
46648     * @class EventEmitter Manages event registering and emitting.
46649     */
46650    function EventEmitter() {}
46651
46652    // Shortcuts to improve speed and size
46653    var proto = EventEmitter.prototype;
46654    var exports = this;
46655    var originalGlobalValue = exports.EventEmitter;
46656
46657    /**
46658     * Finds the index of the listener for the event in its storage array.
46659     *
46660     * @param {Function[]} listeners Array of listeners to search through.
46661     * @param {Function} listener Method to look for.
46662     * @return {Number} Index of the specified listener, -1 if not found
46663     * @api private
46664     */
46665    function indexOfListener(listeners, listener) {
46666        var i = listeners.length;
46667        while (i--) {
46668            if (listeners[i].listener === listener) {
46669                return i;
46670            }
46671        }
46672
46673        return -1;
46674    }
46675
46676    /**
46677     * Alias a method while keeping the context correct, to allow for over
46677writing of target method.
46678     *
46679     * @param {String} name The name of the target method.
46680     * @return {Function} The aliased method
46681     * @api private
46682     */
46683    function alias(name) {
46684        return function aliasClosure() {
46685            return this[name].apply(this, arguments);
46686        };
46687    }
46688
46689    /**
46690     * Returns the listener array for the specified event.
46691     * Will initialise the event object and listener arrays if required.
46692     * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
46693     * Each property in the object response is an array of listener functions.
46694     *
46695     * @param {String|RegExp} evt Name of the event to return the listeners from.
46696     * @return {Function[]|Object} All listener functions for the event.
46697     */
46698    proto.getListeners = function getListeners(evt) {
46699        var events = this._getEvents();
46700        var response;
46701        var key;
46702
46703        // Return a concatenated array of all matching events if
46704        // the selector is a regular expression.
46705        if (evt instanceof RegExp) {
46706            response = {};
46707            for (key in events) {
46708                if (events.hasOwnProperty(key) && evt.test(key)) {
46709                    response[key] = events[key];
46710                }
46711            }
46712        }
46713        else {
46714            response = events[evt] || (events[evt] = []);
46715        }
46716
46717        return response;
46718    };
46719
46720    /**
46721     * Takes a list of listener objects and flattens it into a list of listener functions.
46722     *
46723     * @param {Object[]} listeners Raw listener objects.
46724     * @return {Function[]} Just the listener functions.
46725     */
46726    proto.flattenListeners = function flattenListeners(listeners) {
46727        var flatListeners = [];
46728        var i;
46729
46730        for (i = 0; i < listeners.length; i += 1) {
46731            flatListeners.push(listeners[i].listener);
46732        }
46733
46734        return flatListeners;
46735    };
46736
46737    /**
46738     * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
46739     *
46740     * @param {String|RegExp} evt Name of the event to return the listeners from.
46741     * @return {Object} All listener functions for an event in an object.
46742     */
46743    proto.getListenersAsObject = function getListenersAsObject(evt) {
46744        var listeners = this.getListeners(evt);
46745        var response;
46746
46747        if (listeners instanceof Array) {
46748            response = {};
46749            response[evt] = listeners;
46750        }
46751
46752        return response || listeners;
46753    };
46754
46755    /**
46756     * Adds a listener function to the specified event.
46757     * The listener will not be added if it is a duplicate.
46758     * If the listener returns true then it will be removed after it is called.
46759     * If you pass a regular expression as the event name then the listener will be added to all events that match it.
46760     *
46761     * @param {String|RegExp} evt Name of the event to attach the listener to.
46762     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
46763     * @return {Object} Current instance of EventEmitter for chaining.
46764     */
46765    proto.addListener = function addListener(evt, listener) {
46766        var listeners = this.getListenersAsObject(evt);
46767        var listenerIsWrapped = typeof listener === 'object';
46768        var key;
46769
46770        for (key in listeners) {
46771            if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
46772                listeners[key].push(listenerIsWrapped ? listener : {
46773                    listener: listener,
46774                    once: false
46775                });
46776            }
46777        }
46778
46779        return this;
46780    };
46781
46782    /**
46783     * Alias of addListener
46784     */
46785    proto.on = alias('addListener');
46786
46787    /**
46788     * Semi-alias of addListener. It will add a listener that will be
46789     * automatically removed after its first execution.
46790     *
46791     * @param {String|RegExp} evt Name of the event to attach the listener to.
46792     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
46793     * @return {Object} Current instance of EventEmitter for chaining.
46794     */
46795    proto.addOnceListener = function addOnceListener(evt, listener) {
46796        return this.addListener(evt, {
46797            listener: listener,
46798            once: true
46799        });
46800    };
46801
46802    /**
46803     * Alias of addOnceListener.
46804     */
46805    proto.once = alias('addOnceListener');
46806
46807    /**
46808     * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
46809     * You need to tell it what event names should be matched by a regex.
46810     *
46811     * @param {String} evt Name of the event to create.
46812     * @return {Object} Current instance of EventEmitter for chaining.
46813     */
46814    proto.defineEvent = function defineEvent(evt) {
46815        this.getListeners(evt);
46816        return this;
46817    };
46818
46819    /**
46820     * Uses defineEvent to define multiple events.
46821     *
46822     * @param {String[]} evts An array of event names to define.
46823     * @return {Object} Current instance of EventEmitter for chaining.
46824     */
46825    proto.defineEvents = function defineEvents(evts) {
46826        for (var i = 0; i < evts.length; i += 1) {
46827            this.defineEvent(evts[i]);
46828        }
46829        return this;
46830    };
46831
46832    /**
46833     * Removes a listener function from the specified event.
46834     * When passed a regular expression as the event name, it will remove the listener from all events that match it.
46835     *
46836     * @param {String|RegExp} evt Name of the event to remove the listener from.
46837     * @param {Function} listener Method to remove from the event.
46838     * @return {Object} Current instance of EventEmitter for chaining.
46839     */
46840    proto.removeListener = function removeListener(evt, listener) {
46841        var listeners = this.getListenersAsObject(evt);
46842        var index;
46843        var key;
46844
46845        for (key in listeners) {
46846            if (listeners.hasOwnProperty(key)) {
46847                index = indexOfListener(listeners[key], listener);
46848
46849                if (index !== -1) {
46850                    listeners[key].splice(index, 1);
46851                }
46852            }
46853        }
46854
46855        return this;
46856    };
46857
46858    /**
46859     * Alias of removeListener
46860     */
46861    proto.off = alias('removeListener');
46862
46863    /**
46864     * Adds listeners in bulk using the manipulateListeners method.
46865     * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
46866     * You can also pass it a regular expression to add the array of listeners to all events that match it.
46867     * Yeah, this function does quite a bit. That's probably a bad thing.
46868     *
46869     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
46870     * @param {Function[]} [listeners] An optional array of listener functions to add.
46871     * @return {Object} Current instance of EventEmitter for chaining.
46872     */
46873    proto.addListeners = function addListeners(evt, listeners) {
46874        // Pass through to manipulateListeners
46875        return this.manipulateListeners(false, evt, listeners);
46876    };
46877
46878    /**
46879     * Removes listeners in bulk using the manipulateListeners method.
46880     * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
46881     * You can also pass it an event name and an array of listeners to be removed.
46882     * You can also pass it a regular expression to remove the listeners from all events that match it.
46883     *
46884     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
46885     * @param {Function[]} [listeners] An optional array of listener functions to remove.
46886     * @return {Object} Current instance of EventEmitter for chaining.
46887     */
46888    proto.removeListeners = function removeListeners(evt, listeners) {
46889        // Pass through to manipulateListeners
46890        return this.manipulateListeners(true, evt, listeners);
46891    };
46892
46893    /**
46894     * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
46895     * The first argument will determine if the listeners are removed (true) or added (false).
46896     * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
46897     * You can also pass it an event name and an array of listeners to be added/removed.
46898     * You can also pass it a regular expression to manipulate the listeners of all events that match it.
46899     *
46900     * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
46901     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
46902     * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
46903     * @return {Object} Current instance of EventEmitter for chaining.
46904     */
46905    proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
46906        var i;
46907        var value;
46908        var single = remove ? this.removeListener : this.addListener;
46909        var multiple = remove ? this.removeListeners : this.addListeners;
46910
46911        // If evt is an object then pass each of its properties to this method
46912        if (typeof evt === 'object' && !(evt instanceof RegExp)) {
46913            for (i in evt) {
46914                if (evt.hasOwnProperty(i) && (value = evt[i])) {
46915                    // Pass the single listener straight through to the singular method
46916                    if (typeof value === 'function') {
46917                        single.call(this, i, value);
46918                    }
46919                    else {
46920                        // Otherwise pass back to the multiple function
46921                        multiple.call(this, i, value);
46922                    }
46923                }
46924            }
46925        }
46926        else {
46927            // So evt must be a string
46928            // And listeners must be an array of listeners
46929            // Loop over it and pass each one to the multiple method
46930            i = listeners.length;
46931            while (i--) {
46932                single.call(this, evt, listeners[i]);
46933            }
46934        }
46935
46936        return this;
46937    };
46938
46939    /**
46940     * Removes all listeners from a specified event.
46941     * If you do not specify an event then all listeners will be removed.
46942     * That means every event will be emptied.
46943     * You can also pass a regex to remove all events that match it.
46944     *
46945     * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
46946     * @return {Object} Current instance of EventEmitter for chaining.
46947     */
46948    proto.removeEvent = function removeEvent(evt) {
46949        var type = typeof evt;
46950        var events = this._getEvents();
46951        var key;
46952
46953        // Remove different things depending on the state of evt
46954        if (type === 'string') {
46955            // Remove all listeners for the specified event
46956            delete events[evt];
46957        }
46958        else if (evt instanceof RegExp) {
46959            // Remove all events matching the regex.
46960            for (key in events) {
46961                if (events.hasOwnProperty(key) && evt.test(key)) {
46962                    delete events[key];
46963                }
46964            }
46965        }
46966        else {
46967            // Remove all listeners in all events
46968            delete this._events;
46969        }
46970
46971        return this;
46972    };
46973
46974    /**
46975     * Alias of removeEvent.
46976     *
46977     * Added to mirror the node API.
46978     */
46979    proto.removeAllListeners = alias('removeEvent');
46980
46981    /**
46982     * Emits an event of your choice.
46983     * When emitted, every listener attached to that event will be executed.
46984     * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
46985     * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
46986     * So they will not arrive within the array on the other side, they will be separate.
46987     * You can also pass a regular expression to emit to all events that match it.
46988     *
46989     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
46990     * @param {Array} [args] Optional array of arguments to be passed to each listener.
46991     * @return {Object} Current instance of EventEmitter for chaining.
46992     */
46993    proto.emitEvent = function emitEvent(evt, args) {
46994        var listenersMap = this.getListenersAsObject(evt);
46995        var listeners;
46996        var listener;
46997        var i;
46998        var key;
46999        var response;
47000
47001        for (key in listenersMap) {
47002            if (listenersMap.hasOwnProperty(key)) {
47003                listeners = listenersMap[key].slice(0);
47004                i = listeners.length;
47005
47006                while (i--) {
47007                    // If the listener returns true then it shall be removed from the event
47008                    // The function is executed either with a basic call or an apply if there is an args array
47009                    listener = listeners[i];
47010
47011                    if (listener.once === true) {
47012                        this.removeListener(evt, listener.listener);
47013                    }
47014
47015                    response = listener.listener.apply(this, args || []);
47016
47017                    if (response === this._getOnceReturnValue()) {
47018                        this.removeListener(evt, listener.listener);
47019                    }
47020                }
47021            }
47022        }
47023
47024        return this;
47025    };
47026
47027    /**
47028     * Alias of emitEvent
47029     */
47030    proto.trigger = alias('emitEvent');
47031
47032    /**
47033     * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
47034     * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
47035     *
47036     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
47037     * @param {...*} Optional additional arguments to be passed to each listener.
47038     * @return {Object} Current instance of EventEmitter for chaining.
47039     */
47040    proto.emit = function emit(evt) {
47041        var args = Array.prototype.slice.call(arguments, 1);
47042        return this.emitEvent(evt, args);
47043    };
47044
47045    /**
47046     * Sets the current value to check against when executing listeners. If a
47047     * listeners return value matches the one set here then it will be removed
47048     * after execution. This value defaults to true.
47049     *
47050     * @param {*} value The new value to check for when executing listeners.
47051     * @return {Object} Current instance of EventEmitter for chaining.
47052     */
47053    proto.setOnceReturnValue = function setOnceReturnValue(value) {
47054        this._onceReturnValue = value;
47055        return this;
47056    };
47057
47058    /**
47059     * Fetches the current value to check against when executing listeners. If
47060     * the listeners return value matches this one then it should be removed
47061     * automatically. It will return true by default.
47062     *
47063     * @return {*|Boolean} The current value to check for or the default, true.
47064     * @api private
47065     */
47066    proto._getOnceReturnValue = function _getOnceReturnValue() {
47067        if (this.hasOwnProperty('_onceReturnValue')) {
47068            return this._onceReturnValue;
47069        }
47070        else {
47071            return true;
47072        }
47073    };
47074
47075    /**
47076     * Fetches the events object and creates one if required.
47077     *
47078     * @return {Object} The events storage object.
47079     * @api private
47080     */
47081    p
47081roto._getEvents = function _getEvents() {
47082        return this._events || (this._events = {});
47083    };
47084
47085    /**
47086     * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
47087     *
47088     * @return {Function} Non conflicting EventEmitter class.
47089     */
47090    EventEmitter.noConflict = function noConflict() {
47091        exports.EventEmitter = originalGlobalValue;
47092        return EventEmitter;
47093    };
47094
47095    // Expose the class either via AMD, CommonJS or the global object
47096    if (typeof define === 'function' && define.amd) {
47097        define(function () {
47098            return EventEmitter;
47099        });
47100    }
47101    else if (typeof module === 'object' && module.exports){
47102        module.exports = EventEmitter;
47103    }
47104    else {
47105        exports.EventEmitter = EventEmitter;
47106    }
47107}.call(this));
47108
47109},{}]},{},[5])
47110
47111//# sourceMappingURL=app.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.