{"version":3,"file":"default/js/main.js","sources":["webpack:///webpack/bootstrap","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/components/backtotop.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/components/tooltips.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/config/slickConfigs.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/config/zoomConfigs.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/product/base.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/thirdparty/bootstrap.js","webpack:///./cartridges/app_lyonscg_mfra/cartridge/client/default/js/util/utils.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/cart/cart.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/clientSideValidation.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/collapsibleItem.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/cookie.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/countrySelector.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/focus.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/formValidation.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/keyboardAccessibility.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/components/miniCart.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/product/base.js","webpack:///./cartridges/app_storefront_base/cartridge/client/default/js/util.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/cart/cart.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/carousels.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/clientSideValidation.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/consentTracking.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/einstein.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/einsteinObserver.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/footer.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/geoLookup.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/menu.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/miniCart.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/quantityInput.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/search.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/components/spinner.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/config/slickConfigs.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/main.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/product/base.js","webpack:///./cartridges/org_actionsports/cartridge/client/default/js/util/utils.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/components/quantityInput.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/config/glide.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/pinchzoom/pinch-zoom.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/util/formZoom.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/util/modal.js","webpack:///./cartridges/org_vista/cartridge/client/default/js/util/utils.js","webpack:///./cartridges/plugin_wishlists/cartridge/client/default/js/components/miniCart.js","webpack:///./cartridges/site_bell/cartridge/client/default/js/config/glideConfigs.js","webpack:///./node_modules/@babel/polyfill/lib/index.js","webpack:///./node_modules/@babel/polyfill/lib/noConflict.js","webpack:///./node_modules/@babel/polyfill/node_modules/regenerator-runtime/runtime.js","webpack:///./node_modules/@glidejs/glide/dist/glide.js","webpack:///./node_modules/bootstrap/js/src/alert.js","webpack:///./node_modules/bootstrap/js/src/carousel.js","webpack:///./node_modules/bootstrap/js/src/collapse.js","webpack:///./node_modules/bootstrap/js/src/modal.js","webpack:///./node_modules/bootstrap/js/src/scrollspy.js","webpack:///./node_modules/bootstrap/js/src/tab.js","webpack:///./node_modules/bootstrap/js/src/tools/sanitizer.js","webpack:///./node_modules/bootstrap/js/src/tooltip.js","webpack:///./node_modules/bootstrap/js/src/util.js","webpack:///./node_modules/core-js/es6/index.js","webpack:///./node_modules/core-js/fn/array/flat-map.js","webpack:///./node_modules/core-js/fn/array/includes.js","webpack:///./node_modules/core-js/fn/object/entries.js","webpack:///./node_modules/core-js/fn/object/get-own-property-descriptors.js","webpack:///./node_modules/core-js/fn/object/values.js","webpack:///./node_modules/core-js/fn/promise/finally.js","webpack:///./node_modules/core-js/fn/string/pad-end.js","webpack:///./node_modules/core-js/fn/string/pad-start.js","webpack:///./node_modules/core-js/fn/string/trim-end.js","webpack:///./node_modules/core-js/fn/string/trim-start.js","webpack:///./node_modules/core-js/fn/symbol/async-iterator.js","webpack:///./node_modules/core-js/library/fn/global.js","webpack:///./node_modules/core-js/library/modules/_a-function.js","webpack:///./node_modules/core-js/library/modules/_an-object.js","webpack:///./node_modules/core-js/library/modules/_core.js","webpack:///./node_modules/core-js/library/modules/_ctx.js","webpack:///./node_modules/core-js/library/modules/_descriptors.js","webpack:///./node_modules/core-js/library/modules/_dom-create.js","webpack:///./node_modules/core-js/library/modules/_export.js","webpack:///./node_modules/core-js/library/modules/_fails.js","webpack:///./node_modules/core-js/library/modules/_global.js","webpack:///./node_modules/core-js/library/modules/_has.js","webpack:///./node_modules/core-js/library/modules/_hide.js","webpack:///./node_modules/core-js/library/modules/_ie8-dom-define.js","webpack:///./node_modules/core-js/library/modules/_is-object.js","webpack:///./node_modules/core-js/library/modules/_object-dp.js","webpack:///./node_modules/core-js/library/modules/_property-desc.js","webpack:///./node_modules/core-js/library/modules/_to-primitive.js","webpack:///./node_modules/core-js/library/modules/es7.global.js","webpack:///./node_modules/core-js/modules/_a-function.js","webpack:///./node_modules/core-js/modules/_a-number-value.js","webpack:///./node_modules/core-js/modules/_add-to-unscopables.js","webpack:///./node_modules/core-js/modules/_advance-string-index.js","webpack:///./node_modules/core-js/modules/_an-instance.js","webpack:///./node_modules/core-js/modules/_an-object.js","webpack:///./node_modules/core-js/modules/_array-copy-within.js","webpack:///./node_modules/core-js/modules/_array-fill.js","webpack:///./node_modules/core-js/modules/_array-includes.js","webpack:///./node_modules/core-js/modules/_array-methods.js","webpack:///./node_modules/core-js/modules/_array-reduce.js","webpack:///./node_modules/core-js/modules/_array-species-constructor.js","webpack:///./node_modules/core-js/modules/_array-species-create.js","webpack:///./node_modules/core-js/modules/_bind.js","webpack:///./node_modules/core-js/modules/_classof.js","webpack:///./node_modules/core-js/modules/_cof.js","webpack:///./node_modules/core-js/modules/_collection-strong.js","webpack:///./node_modules/core-js/modules/_collection-weak.js","webpack:///./node_modules/core-js/modules/_collection.js","webpack:///./node_modules/core-js/modules/_core.js","webpack:///./node_modules/core-js/modules/_create-property.js","webpack:///./node_modules/core-js/modules/_ctx.js","webpack:///./node_modules/core-js/modules/_date-to-iso-string.js","webpack:///./node_modules/core-js/modules/_date-to-primitive.js","webpack:///./node_modules/core-js/modules/_defined.js","webpack:///./node_modules/core-js/modules/_descriptors.js","webpack:///./node_modules/core-js/modules/_dom-create.js","webpack:///./node_modules/core-js/modules/_enum-bug-keys.js","webpack:///./node_modules/core-js/modules/_enum-keys.js","webpack:///./node_modules/core-js/modules/_export.js","webpack:///./node_modules/core-js/modules/_fails-is-regexp.js","webpack:///./node_modules/core-js/modules/_fails.js","webpack:///./node_modules/core-js/modules/_fix-re-wks.js","webpack:///./node_modules/core-js/modules/_flags.js","webpack:///./node_modules/core-js/modules/_flatten-into-array.js","webpack:///./node_modules/core-js/modules/_for-of.js","webpack:///./node_modules/core-js/modules/_function-to-string.js","webpack:///./node_modules/core-js/modules/_global.js","webpack:///./node_modules/core-js/modules/_has.js","webpack:///./node_modules/core-js/modules/_hide.js","webpack:///./node_modules/core-js/modules/_html.js","webpack:///./node_modules/core-js/modules/_ie8-dom-define.js","webpack:///./node_modules/core-js/modules/_inherit-if-required.js","webpack:///./node_modules/core-js/modules/_invoke.js","webpack:///./node_modules/core-js/modules/_iobject.js","webpack:///./node_modules/core-js/modules/_is-array-iter.js","webpack:///./node_modules/core-js/modules/_is-array.js","webpack:///./node_modules/core-js/modules/_is-integer.js","webpack:///./node_modules/core-js/modules/_is-object.js","webpack:///./node_modules/core-js/modules/_is-regexp.js","webpack:///./node_modules/core-js/modules/_iter-call.js","webpack:///./node_modules/core-js/modules/_iter-create.js","webpack:///./node_modules/core-js/modules/_iter-define.js","webpack:///./node_modules/core-js/modules/_iter-detect.js","webpack:///./node_modules/core-js/modules/_iter-step.js","webpack:///./node_modules/core-js/modules/_iterators.js","webpack:///./node_modules/core-js/modules/_library.js","webpack:///./node_modules/core-js/modules/_math-expm1.js","webpack:///./node_modules/core-js/modules/_math-fround.js","webpack:///./node_modules/core-js/modules/_math-log1p.js","webpack:///./node_modules/core-js/modules/_math-sign.js","webpack:///./node_modules/core-js/modules/_meta.js","webpack:///./node_modules/core-js/modules/_microtask.js","webpack:///./node_modules/core-js/modules/_new-promise-capability.js","webpack:///./node_modules/core-js/modules/_object-assign.js","webpack:///./node_modules/core-js/modules/_object-create.js","webpack:///./node_modules/core-js/modules/_object-dp.js","webpack:///./node_modules/core-js/modules/_object-dps.js","webpack:///./node_modules/core-js/modules/_object-gopd.js","webpack:///./node_modules/core-js/modules/_object-gopn-ext.js","webpack:///./node_modules/core-js/modules/_object-gopn.js","webpack:///./node_modules/core-js/modules/_object-gops.js","webpack:///./node_modules/core-js/modules/_object-gpo.js","webpack:///./node_modules/core-js/modules/_object-keys-internal.js","webpack:///./node_modules/core-js/modules/_object-keys.js","webpack:///./node_modules/core-js/modules/_object-pie.js","webpack:///./node_modules/core-js/modules/_object-sap.js","webpack:///./node_modules/core-js/modules/_object-to-array.js","webpack:///./node_modules/core-js/modules/_own-keys.js","webpack:///./node_modules/core-js/modules/_parse-float.js","webpack:///./node_modules/core-js/modules/_parse-int.js","webpack:///./node_modules/core-js/modules/_perform.js","webpack:///./node_modules/core-js/modules/_promise-resolve.js","webpack:///./node_modules/core-js/modules/_property-desc.js","webpack:///./node_modules/core-js/modules/_redefine-all.js","webpack:///./node_modules/core-js/modules/_redefine.js","webpack:///./node_modules/core-js/modules/_regexp-exec-abstract.js","webpack:///./node_modules/core-js/modules/_regexp-exec.js","webpack:///./node_modules/core-js/modules/_same-value.js","webpack:///./node_modules/core-js/modules/_set-proto.js","webpack:///./node_modules/core-js/modules/_set-species.js","webpack:///./node_modules/core-js/modules/_set-to-string-tag.js","webpack:///./node_modules/core-js/modules/_shared-key.js","webpack:///./node_modules/core-js/modules/_shared.js","webpack:///./node_modules/core-js/modules/_species-constructor.js","webpack:///./node_modules/core-js/modules/_strict-method.js","webpack:///./node_modules/core-js/modules/_string-at.js","webpack:///./node_modules/core-js/modules/_string-context.js","webpack:///./node_modules/core-js/modules/_string-html.js","webpack:///./node_modules/core-js/modules/_string-pad.js","webpack:///./node_modules/core-js/modules/_string-repeat.js","webpack:///./node_modules/core-js/modules/_string-trim.js","webpack:///./node_modules/core-js/modules/_string-ws.js","webpack:///./node_modules/core-js/modules/_task.js","webpack:///./node_modules/core-js/modules/_to-absolute-index.js","webpack:///./node_modules/core-js/modules/_to-index.js","webpack:///./node_modules/core-js/modules/_to-integer.js","webpack:///./node_modules/core-js/modules/_to-iobject.js","webpack:///./node_modules/core-js/modules/_to-length.js","webpack:///./node_modules/core-js/modules/_to-object.js","webpack:///./node_modules/core-js/modules/_to-primitive.js","webpack:///./node_modules/core-js/modules/_typed-array.js","webpack:///./node_modules/core-js/modules/_typed-buffer.js","webpack:///./node_modules/core-js/modules/_typed.js","webpack:///./node_modules/core-js/modules/_uid.js","webpack:///./node_modules/core-js/modules/_user-agent.js","webpack:///./node_modules/core-js/modules/_validate-collection.js","webpack:///./node_modules/core-js/modules/_wks-define.js","webpack:///./node_modules/core-js/modules/_wks-ext.js","webpack:///./node_modules/core-js/modules/_wks.js","webpack:///./node_modules/core-js/modules/core.get-iterator-method.js","webpack:///./node_modules/core-js/modules/es6.array.copy-within.js","webpack:///./node_modules/core-js/modules/es6.array.every.js","webpack:///./node_modules/core-js/modules/es6.array.fill.js","webpack:///./node_modules/core-js/modules/es6.array.filter.js","webpack:///./node_modules/core-js/modules/es6.array.find-index.js","webpack:///./node_modules/core-js/modules/es6.array.find.js","webpack:///./node_modules/core-js/modules/es6.array.for-each.js","webpack:///./node_modules/core-js/modules/es6.array.from.js","webpack:///./node_modules/core-js/modules/es6.array.index-of.js","webpack:///./node_modules/core-js/modules/es6.array.is-array.js","webpack:///./node_modules/core-js/modules/es6.array.iterator.js","webpack:///./node_modules/core-js/modules/es6.array.join.js","webpack:///./node_modules/core-js/modules/es6.array.last-index-of.js","webpack:///./node_modules/core-js/modules/es6.array.map.js","webpack:///./node_modules/core-js/modules/es6.array.of.js","webpack:///./node_modules/core-js/modules/es6.array.reduce-right.js","webpack:///./node_modules/core-js/modules/es6.array.reduce.js","webpack:///./node_modules/core-js/modules/es6.array.slice.js","webpack:///./node_modules/core-js/modules/es6.array.some.js","webpack:///./node_modules/core-js/modules/es6.array.sort.js","webpack:///./node_modules/core-js/modules/es6.array.species.js","webpack:///./node_modules/core-js/modules/es6.date.now.js","webpack:///./node_modules/core-js/modules/es6.date.to-iso-string.js","webpack:///./node_modules/core-js/modules/es6.date.to-json.js","webpack:///./node_modules/core-js/modules/es6.date.to-primitive.js","webpack:///./node_modules/core-js/modules/es6.date.to-string.js","webpack:///./node_modules/core-js/modules/es6.function.bind.js","webpack:///./node_modules/core-js/modules/es6.function.has-instance.js","webpack:///./node_modules/core-js/modules/es6.function.name.js","webpack:///./node_modules/core-js/modules/es6.map.js","webpack:///./node_modules/core-js/modules/es6.math.acosh.js","webpack:///./node_modules/core-js/modules/es6.math.asinh.js","webpack:///./node_modules/core-js/modules/es6.math.atanh.js","webpack:///./node_modules/core-js/modules/es6.math.cbrt.js","webpack:///./node_modules/core-js/modules/es6.math.clz32.js","webpack:///./node_modules/core-js/modules/es6.math.cosh.js","webpack:///./node_modules/core-js/modules/es6.math.expm1.js","webpack:///./node_modules/core-js/modules/es6.math.fround.js","webpack:///./node_modules/core-js/modules/es6.math.hypot.js","webpack:///./node_modules/core-js/modules/es6.math.imul.js","webpack:///./node_modules/core-js/modules/es6.math.log10.js","webpack:///./node_modules/core-js/modules/es6.math.log1p.js","webpack:///./node_modules/core-js/modules/es6.math.log2.js","webpack:///./node_modules/core-js/modules/es6.math.sign.js","webpack:///./node_modules/core-js/modules/es6.math.sinh.js","webpack:///./node_modules/core-js/modules/es6.math.tanh.js","webpack:///./node_modules/core-js/modules/es6.math.trunc.js","webpack:///./node_modules/core-js/modules/es6.number.constructor.js","webpack:///./node_modules/core-js/modules/es6.number.epsilon.js","webpack:///./node_modules/core-js/modules/es6.number.is-finite.js","webpack:///./node_modules/core-js/modules/es6.number.is-integer.js","webpack:///./node_modules/core-js/modules/es6.number.is-nan.js","webpack:///./node_modules/core-js/modules/es6.number.is-safe-integer.js","webpack:///./node_modules/core-js/modules/es6.number.max-safe-integer.js","webpack:///./node_modules/core-js/modules/es6.number.min-safe-integer.js","webpack:///./node_modules/core-js/modules/es6.number.parse-float.js","webpack:///./node_modules/core-js/modules/es6.number.parse-int.js","webpack:///./node_modules/core-js/modules/es6.number.to-fixed.js","webpack:///./node_modules/core-js/modules/es6.number.to-precision.js","webpack:///./node_modules/core-js/modules/es6.object.assign.js","webpack:///./node_modules/core-js/modules/es6.object.create.js","webpack:///./node_modules/core-js/modules/es6.object.define-properties.js","webpack:///./node_modules/core-js/modules/es6.object.define-property.js","webpack:///./node_modules/core-js/modules/es6.object.freeze.js","webpack:///./node_modules/core-js/modules/es6.object.get-own-property-descriptor.js","webpack:///./node_modules/core-js/modules/es6.object.get-own-property-names.js","webpack:///./node_modules/core-js/modules/es6.object.get-prototype-of.js","webpack:///./node_modules/core-js/modules/es6.object.is-extensible.js","webpack:///./node_modules/core-js/modules/es6.object.is-frozen.js","webpack:///./node_modules/core-js/modules/es6.object.is-sealed.js","webpack:///./node_modules/core-js/modules/es6.object.is.js","webpack:///./node_modules/core-js/modules/es6.object.keys.js","webpack:///./node_modules/core-js/modules/es6.object.prevent-extensions.js","webpack:///./node_modules/core-js/modules/es6.object.seal.js","webpack:///./node_modules/core-js/modules/es6.object.set-prototype-of.js","webpack:///./node_modules/core-js/modules/es6.object.to-string.js","webpack:///./node_modules/core-js/modules/es6.parse-float.js","webpack:///./node_modules/core-js/modules/es6.parse-int.js","webpack:///./node_modules/core-js/modules/es6.promise.js","webpack:///./node_modules/core-js/modules/es6.reflect.apply.js","webpack:///./node_modules/core-js/modules/es6.reflect.construct.js","webpack:///./node_modules/core-js/modules/es6.reflect.define-property.js","webpack:///./node_modules/core-js/modules/es6.reflect.delete-property.js","webpack:///./node_modules/core-js/modules/es6.reflect.enumerate.js","webpack:///./node_modules/core-js/modules/es6.reflect.get-own-property-descriptor.js","webpack:///./node_modules/core-js/modules/es6.reflect.get-prototype-of.js","webpack:///./node_modules/core-js/modules/es6.reflect.get.js","webpack:///./node_modules/core-js/modules/es6.reflect.has.js","webpack:///./node_modules/core-js/modules/es6.reflect.is-extensible.js","webpack:///./node_modules/core-js/modules/es6.reflect.own-keys.js","webpack:///./node_modules/core-js/modules/es6.reflect.prevent-extensions.js","webpack:///./node_modules/core-js/modules/es6.reflect.set-prototype-of.js","webpack:///./node_modules/core-js/modules/es6.reflect.set.js","webpack:///./node_modules/core-js/modules/es6.regexp.constructor.js","webpack:///./node_modules/core-js/modules/es6.regexp.exec.js","webpack:///./node_modules/core-js/modules/es6.regexp.flags.js","webpack:///./node_modules/core-js/modules/es6.regexp.match.js","webpack:///./node_modules/core-js/modules/es6.regexp.replace.js","webpack:///./node_modules/core-js/modules/es6.regexp.search.js","webpack:///./node_modules/core-js/modules/es6.regexp.split.js","webpack:///./node_modules/core-js/modules/es6.regexp.to-string.js","webpack:///./node_modules/core-js/modules/es6.set.js","webpack:///./node_modules/core-js/modules/es6.string.anchor.js","webpack:///./node_modules/core-js/modules/es6.string.big.js","webpack:///./node_modules/core-js/modules/es6.string.blink.js","webpack:///./node_modules/core-js/modules/es6.string.bold.js","webpack:///./node_modules/core-js/modules/es6.string.code-point-at.js","webpack:///./node_modules/core-js/modules/es6.string.ends-with.js","webpack:///./node_modules/core-js/modules/es6.string.fixed.js","webpack:///./node_modules/core-js/modules/es6.string.fontcolor.js","webpack:///./node_modules/core-js/modules/es6.string.fontsize.js","webpack:///./node_modules/core-js/modules/es6.string.from-code-point.js","webpack:///./node_modules/core-js/modules/es6.string.includes.js","webpack:///./node_modules/core-js/modules/es6.string.italics.js","webpack:///./node_modules/core-js/modules/es6.string.iterator.js","webpack:///./node_modules/core-js/modules/es6.string.link.js","webpack:///./node_modules/core-js/modules/es6.string.raw.js","webpack:///./node_modules/core-js/modules/es6.string.repeat.js","webpack:///./node_modules/core-js/modules/es6.string.small.js","webpack:///./node_modules/core-js/modules/es6.string.starts-with.js","webpack:///./node_modules/core-js/modules/es6.string.strike.js","webpack:///./node_modules/core-js/modules/es6.string.sub.js","webpack:///./node_modules/core-js/modules/es6.string.sup.js","webpack:///./node_modules/core-js/modules/es6.string.trim.js","webpack:///./node_modules/core-js/modules/es6.symbol.js","webpack:///./node_modules/core-js/modules/es6.typed.array-buffer.js","webpack:///./node_modules/core-js/modules/es6.typed.data-view.js","webpack:///./node_modules/core-js/modules/es6.typed.float32-array.js","webpack:///./node_modules/core-js/modules/es6.typed.float64-array.js","webpack:///./node_modules/core-js/modules/es6.typed.int16-array.js","webpack:///./node_modules/core-js/modules/es6.typed.int32-array.js","webpack:///./node_modules/core-js/modules/es6.typed.int8-array.js","webpack:///./node_modules/core-js/modules/es6.typed.uint16-array.js","webpack:///./node_modules/core-js/modules/es6.typed.uint32-array.js","webpack:///./node_modules/core-js/modules/es6.typed.uint8-array.js","webpack:///./node_modules/core-js/modules/es6.typed.uint8-clamped-array.js","webpack:///./node_modules/core-js/modules/es6.weak-map.js","webpack:///./node_modules/core-js/modules/es6.weak-set.js","webpack:///./node_modules/core-js/modules/es7.array.flat-map.js","webpack:///./node_modules/core-js/modules/es7.array.includes.js","webpack:///./node_modules/core-js/modules/es7.object.entries.js","webpack:///./node_modules/core-js/modules/es7.object.get-own-property-descriptors.js","webpack:///./node_modules/core-js/modules/es7.object.values.js","webpack:///./node_modules/core-js/modules/es7.promise.finally.js","webpack:///./node_modules/core-js/modules/es7.string.pad-end.js","webpack:///./node_modules/core-js/modules/es7.string.pad-start.js","webpack:///./node_modules/core-js/modules/es7.string.trim-left.js","webpack:///./node_modules/core-js/modules/es7.string.trim-right.js","webpack:///./node_modules/core-js/modules/es7.symbol.async-iterator.js","webpack:///./node_modules/core-js/modules/web.dom.iterable.js","webpack:///./node_modules/core-js/modules/web.immediate.js","webpack:///./node_modules/core-js/modules/web.timers.js","webpack:///./node_modules/core-js/web/index.js","webpack:///./node_modules/ev-emitter/ev-emitter.js","webpack:///./node_modules/imagesloaded/imagesloaded.js","webpack:///./node_modules/jquery/dist/jquery.js","webpack:///./node_modules/lazysizes/lazysizes.js","webpack:///./node_modules/lodash/_Symbol.js","webpack:///./node_modules/lodash/_baseGetTag.js","webpack:///./node_modules/lodash/_baseTrim.js","webpack:///./node_modules/lodash/_freeGlobal.js","webpack:///./node_modules/lodash/_getRawTag.js","webpack:///./node_modules/lodash/_objectToString.js","webpack:///./node_modules/lodash/_root.js","webpack:///./node_modules/lodash/_trimmedEndIndex.js","webpack:///./node_modules/lodash/debounce.js","webpack:///./node_modules/lodash/isObject.js","webpack:///./node_modules/lodash/isObjectLike.js","webpack:///./node_modules/lodash/isSymbol.js","webpack:///./node_modules/lodash/lodash.js","webpack:///./node_modules/lodash/now.js","webpack:///./node_modules/lodash/toNumber.js","webpack:///./node_modules/pinch-zoom-js/src/pinch-zoom.js","webpack:///./node_modules/popper.js/dist/esm/popper.js","webpack:///./node_modules/slick-carousel/slick/slick.js","webpack:///(webpack)/buildin/global.js","webpack:///(webpack)/buildin/module.js"],"sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./cartridges/org_actionsports/cartridge/client/default/js/main.js\");\n","'use strict';\n\nvar offsetTrigger = 300;\nvar scrollSpeed = 4000; // in px per sec\nvar hideBelow = 0; // min breakpoint in px\nvar hideAbove = Infinity; // max breakpoint in px (default is Infinity, a.k.a. no maximum)\nvar $backToTopButton = $('.back-to-top');\n\nvar backtotop = {\n isVisible: true,\n isScrolling: false,\n\n init: function () {\n /**\n * initialize back to top button and event handlers.\n **/\n\n // When the user scrolls down N px from the top of the document, show the button\n window.onscroll = function () {\n backtotop.testPosition();\n };\n\n // When the user clicks on the button, scroll back to the top of the document\n $backToTopButton.on('click', function () {\n backtotop.scroll();\n });\n\n // Initially hide the button on page load, before we test to sees if it should show.\n backtotop.hide();\n\n // Go ahead and see if we're already scrolled down far enough\n // to display the button after page loads.\n backtotop.testPosition();\n },\n show: function () {\n if (backtotop.isScrolling) {\n return;\n }\n if (!backtotop.isVisible) {\n $backToTopButton.fadeIn('fast').removeClass('active');\n backtotop.isVisible = true;\n }\n },\n hide: function () {\n if (backtotop.isScrolling) {\n return;\n }\n if (backtotop.isVisible) {\n $backToTopButton.fadeOut('fast');\n backtotop.isVisible = false;\n }\n },\n testPosition: function () {\n if (backtotop.isScrolling) {\n return;\n }\n var width = $(window).innerWidth();\n if (width >= hideBelow && width < hideAbove && ($(window).scrollTop() > offsetTrigger)) {\n backtotop.show();\n } else {\n backtotop.hide();\n }\n },\n reset: function () {\n $backToTopButton.removeClass('active');\n $backToTopButton.removeAttr('style');\n backtotop.hide();\n backtotop.isVisible = false;\n backtotop.isScrolling = false;\n },\n scroll: function () {\n if (backtotop.isScrolling) {\n return;\n }\n backtotop.show(); // just in case\n $backToTopButton.addClass('active');\n $backToTopButton.blur();\n backtotop.isScrolling = true;\n var distanceToTop = $backToTopButton.offset().top;\n var animationDuration = distanceToTop / (scrollSpeed / 1000);\n $('html, body')\n .stop(true, true)\n .animate({ scrollTop: 0 }, animationDuration, function () {\n backtotop.isScrolling = false;\n backtotop.reset();\n backtotop.testPosition();\n }\n );\n }\n};\n\nmodule.exports = function () {\n backtotop.init();\n};\n","'use strict';\n\n$(document).ready(function () {\n $(function () {\n $('[data-toggle=\"tooltip\"]').tooltip();\n });\n});\n","'use strict';\n\n/**\n * Reusable slick carousel configurations\n * @example - $('.product-carousel').slick(slickConfigs.pdp)\n */\n\nmodule.exports = {\n hero: {\n autoplay: true,\n autoplaySpeed: 5000,\n easing: 'swing',\n infinite: true,\n speed: 800,\n dots: true,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1\n },\n\n productTiles: {\n infinite: true,\n speed: 300,\n dots: false,\n arrows: true,\n slidesToShow: 4,\n slidesToScroll: 1,\n responsive: [\n {\n breakpoint: 991,\n settings: {\n slidesToShow: 3,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: 768,\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n pdp: {\n infinite: true,\n speed: 400,\n dots: false,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1\n }\n};\n","'use strict';\n\n/**\n * Default Zoom config\n */\nmodule.exports = {\n url: false,\n on: 'mouseover',\n duration: 120,\n target: false,\n touch: true,\n magnify: 1,\n callback: false,\n onZoomIn: false,\n onZoomOut: false\n};\n","'use strict';\n\nvar base = require('base/product/base');\nvar slickConfigs = require('../config/slickConfigs');\nvar zoomConfigs = require('../config/zoomConfigs');\nvar imagesloaded = require('imagesloaded');\nvar utils = require('../util/utils');\n\n/**\n * Disable PDP Zoom\n */\nfunction disableZoom() {\n $('.slide-link').trigger('zoom.destroy');\n}\n\n/**\n * Init PDP Zoom\n */\nfunction initZoom() {\n disableZoom();\n\n var isDesktop = utils.mediaBreakpointUp('lg');\n var $activeSlide = $('.product-carousel .slick-active');\n var $image = $activeSlide.find('.slide-link.zoom-hires');\n var url = $image.attr('href');\n\n if ($image.length > 0 && url && url !== 'null' && isDesktop) {\n // Start spinner while zoom image loads\n $activeSlide.spinner().start();\n\n var config = {\n url: url,\n callback: function () {\n // Stop spinner when zoom image loaded\n $activeSlide.spinner().stop();\n }\n };\n config = $.extend({}, zoomConfigs, config);\n\n $image.zoom(config);\n }\n}\n\n/**\n * Init the product carousel using a predefined slick configuration\n */\nfunction carouselInit() {\n var $carousel = $('.product-carousel');\n\n if ($carousel.length) {\n imagesloaded($carousel).on('done', function () {\n if ($carousel.hasClass('slick-initialized')) {\n $carousel.off('init', initZoom);\n $carousel.off('afterChange', initZoom);\n $carousel.slick('unslick');\n }\n $carousel.on('init', initZoom);\n $carousel.on('afterChange', initZoom);\n $carousel.not('.slick-initialized').slick(slickConfigs.pdp);\n });\n }\n}\n\n/**\n * Deconstruct (unslick) the carousel, removing classes and handlers added on slick initialize.\n */\nfunction carouselUnslick() {\n var $carousel = $('.product-carousel');\n\n try {\n if ($carousel.length && $carousel.hasClass('slick-initialized')) {\n $carousel.off('init', initZoom);\n $carousel.off('afterChange', initZoom);\n $carousel.slick('unslick');\n }\n } catch (e) {\n // eslint-disable-line no-empty\n }\n}\n\n/**\n * Init the product carousel using a predefined slick configuration for bonus item\n */\nfunction carouselInitBonus() {\n var $carouselBonus = $('.bonus-product-item');\n if ($carouselBonus.length) {\n $carouselBonus.each(function () {\n var $carouselBonusDiv = $(this).find('.product-carousel');\n if ($carouselBonusDiv.length) {\n imagesloaded($carouselBonusDiv).on('done', function () {\n $carouselBonusDiv.on('init', initZoom);\n $carouselBonusDiv.on('afterChange', initZoom);\n $carouselBonusDiv.not('.slick-initialized').slick(slickConfigs.pdp);\n });\n }\n });\n }\n}\n\n/**\n * Deconstruct (unslick) the carousel, removing classes and handlers added on slick initialize.\n */\nfunction carouselUnslickBonus() {\n var $carouselBonus = $('.bonus-product-item');\n if ($carouselBonus.length) {\n $carouselBonus.each(function () {\n var $carouselBonusDiv = $(this).find('.product-carousel');\n if ($carouselBonusDiv.length && $carouselBonusDiv.hasClass('slick-initialized')) {\n $carouselBonusDiv.off('init', initZoom);\n $carouselBonusDiv.off('afterChange', initZoom);\n $carouselBonusDiv.slick('unslick');\n }\n });\n }\n}\n/**\n * @param {json} product - Product json\n */\nfunction updateMainImages(product) {\n var images = product.images;\n var productID = product.id;\n var imagesZoom = product.imagesZoom['hi-res'];\n var htmlString = '';\n\n images.large.forEach(function (image, idx) {\n var zoomImage = imagesZoom[idx] ? imagesZoom[idx] : image;\n var zoomClass = imagesZoom[idx] ? 'zoom-hires' : 'zoom-disabled';\n var htmlSlide = '
'\n + ''\n + '\"''\n + ''\n + '
';\n\n htmlString += htmlSlide;\n });\n var $varName = $('.product-detail .set-items');\n var $carouselImage;\n var $tempDiv;\n\n if ($varName.length) {\n $tempDiv = $('div.set-items').find('div[data-pid=\"' + productID + '\"]');\n $carouselImage = $tempDiv.find('.product-carousel');\n } else {\n $carouselImage = $('.product-carousel');\n }\n $carouselImage.html(htmlString);\n}\n\nvar exportBase = $.extend({}, base, {\n carouselInit: carouselInit,\n carouselInitBonus: carouselInitBonus,\n carouselUnslick: carouselUnslick,\n carouselUnslickBonus: carouselUnslickBonus,\n updateMainImages: updateMainImages\n});\n\nmodule.exports = exportBase;\n","require('bootstrap/js/src/util.js');\nrequire('bootstrap/js/src/alert.js');\n// require('bootstrap/js/src/button.js');\nrequire('bootstrap/js/src/carousel.js');\nrequire('bootstrap/js/src/collapse.js');\n// require('bootstrap/js/src/dropdown.js');\nrequire('bootstrap/js/src/modal.js');\nrequire('bootstrap/js/src/scrollspy.js');\nrequire('bootstrap/js/src/tab.js');\nrequire('bootstrap/js/src/tooltip.js');\n// require('bootstrap/js/src/popover.js');\n","'use strict';\n\n/**\n * isString - native function replacing lodash\n * @param {*} str - passed to function to determine type\n * @description - replaces lodash version\n * @returns {boolean} is it a string?\n */\nfunction isString(str) {\n if (str != null && typeof str.valueOf() === 'string') {\n return true;\n }\n return false;\n}\n\nvar utils = {\n /**\n * @desc Media breakpoints that are used throughout the Javascript\n */\n breakpoints: { // TODO: LFED Update with project breakpoints\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200\n },\n\n /**\n * @function\n * @description Returns either an object with all of the available breakpoints or a specific viewport based on the given size\n * @param {string=} size The viewport to return\n * @param {Object=} breakpoints A custom breakpoints object\n * @returns {Object|string} - breakpoints or specific viewport\n */\n getViewports: function (size, breakpoints) {\n var bps = breakpoints || this.breakpoints;\n\n if (size) {\n var viewport = bps[size];\n\n if (viewport) {\n return viewport;\n }\n\n window.console.error('Unexpected viewport size given in util.getViewports');\n throw new Error('Unexpected viewport size given in util.getViewports');\n } else {\n return breakpoints;\n }\n },\n\n /**\n * @function\n * @description Returns the current viewport name (ex: 'medium') or 'max' if the current window is larger than any defined viewport width\n * @returns {string} - current viewport name\n */\n getCurrentViewport: function () {\n var w = window.innerWidth;\n var viewports = utils.getViewports();\n var viewport = 'max';\n // Traverse the object from small up to desktop, and return the first match\n viewports.forEach((value, name) => {\n if (w <= value) {\n viewport = name;\n }\n });\n return viewport;\n },\n\n /**\n * @function\n * @description appends the parameter with the given name and value to the given url and returns the changed url\n * @param {string} url the url to which the parameter will be added\n * @param {string} name the name of the parameter\n * @param {string} value the value of the parameter\n * @returns {string} - URL with parameter\n */\n appendParamToURL: function (url, name, value) {\n // quit if the param already exists\n if (url.indexOf(name + '=' + value) !== -1) {\n return url;\n }\n var separator = url.indexOf('?') !== -1 ? '&' : '?';\n return url + separator + name + '=' + encodeURIComponent(value);\n },\n\n /**\n * @function\n * @description remove the parameter and its value from the given url and returns the changed url\n * @param {string} url the url from which the parameter will be removed\n * @param {string} name the name of parameter that will be removed from url\n * @param {string} value the value of the parameter that will be remove from url (optional)\n * @returns {string} - URL without parameter\n */\n removeParamFromURL: function (url, name, value) {\n var paramValue = value || '';\n if (url.indexOf('?') === -1 || url.indexOf(name + '=' + paramValue) === -1) {\n return url;\n }\n var hash;\n var params;\n var domain = url.split('?')[0];\n var paramUrl = url.split('?')[1];\n var newParams = [];\n // if there is a hash at the end, store the hash\n if (paramUrl.indexOf('#') > -1) {\n hash = paramUrl.split('#')[1] || '';\n paramUrl = paramUrl.split('#')[0];\n }\n params = paramUrl.split('&');\n for (var i = 0; i < params.length; i++) {\n // put back param to newParams array if it is not the one to be removed\n var paramAndValue = params[i].split('=');\n\n if (paramAndValue[0] !== name || (paramAndValue[0] === name && paramAndValue[1] !== paramValue && paramValue !== '')) {\n newParams.push(params[i]);\n }\n }\n return domain + '?' + newParams.join('&') + (hash ? '#' + hash : '');\n },\n\n /**\n * appends params to a url\n * @param {string} url - Original url\n * @param {Object} params - Parameters to append\n * @returns {string} result url with appended parameters\n */\n appendToUrl: function (url, params) {\n var newUrl = url;\n newUrl += (newUrl.indexOf('?') !== -1 ? '&' : '?') + Object.keys(params).map(function (key) {\n return key + '=' + encodeURIComponent(params[key]);\n }).join('&');\n\n return newUrl;\n },\n\n /**\n * @function\n * @description extract the query string from URL\n * @param {string} url the url to extra query string from\n * @returns {string|Object} - Query String from URL\n **/\n getQueryString: function (url) {\n var qs;\n if (!isString(url)) {\n return null;\n }\n var a = document.createElement('a');\n a.href = url;\n if (a.search) {\n qs = a.search.substr(1); // remove the leading ?\n }\n return qs;\n },\n\n /**\n * @function\n * @description Checks to see if the given element is in the current viewport\n * @param {string} el - Element to check\n * @param {string} offsetToTop - Offset to give the top value\n * @returns {boolean} - Whether or not the element is in the viewport\n */\n elementInViewport: function (el, offsetToTop) {\n var top = el.offsetTop;\n var left = el.offsetLeft;\n var width = el.offsetWidth;\n var height = el.offsetHeight;\n\n while (el.offsetParent) {\n var offsetParent = el.offsetParent;\n top += offsetParent.offsetTop;\n left += offsetParent.offsetLeft;\n }\n\n if (typeof offsetToTop !== 'undefined') {\n top -= offsetToTop;\n }\n\n if (window.pageXOffset !== null) {\n return (\n top < (window.pageYOffset + window.innerHeight) &&\n left < (window.pageXOffset + window.innerWidth) &&\n (top + height) > window.pageYOffset &&\n (left + width) > window.pageXOffset\n );\n }\n\n if (document.compatMode === 'CSS1Compat') {\n return (\n top < (window.document.documentElement.scrollTop + window.document.documentElement.clientHeight) &&\n left < (window.document.documentElement.scrollLeft + window.document.documentElement.clientWidth) &&\n (top + height) > window.document.documentElement.scrollTop &&\n (left + width) > window.document.documentElement.scrollLeft\n );\n }\n\n return false;\n },\n\n /**\n * @function\n * @description Appends the parameter 'format=ajax' to a given path\n * @param {string} path the relative path\n * @returns {string} - URL with format param of ajax\n */\n ajaxUrl: function (path) {\n return this.appendParamToURL(path, 'format', 'ajax');\n },\n\n /**\n * @function\n * @description Converts the given relative URL to an absolute URL\n * @param {string} url - URL to convert\n * @returns {string} - Absolute URL\n */\n toAbsoluteUrl: function (url) {\n var absURL = url;\n if (url.indexOf('http') !== 0 && url.charAt(0) !== '/') {\n absURL = '/' + url;\n }\n return absURL;\n },\n\n /**\n * @function\n * @description Loads css dynamically from given urls\n * @param {Array} urls Array of urls from which css will be dynamically loaded.\n */\n loadDynamicCss: function (urls) {\n var i = urls.length;\n var len = urls.length;\n for (i = 0; i < len; i++) {\n this.loadedCssFiles.push(this.loadCssFile(urls[i]));\n }\n },\n\n /**\n * @function\n * @description Loads css file dynamically from given url\n * @param {string} url The url from which css file will be dynamically loaded.\n * @returns {jQuery} - CSS Link Element\n */\n loadCssFile: function (url) {\n return $('').appendTo($('head')).attr({\n type: 'text/css',\n rel: 'stylesheet'\n }).attr('href', url); // for i.e. <9, href must be added after link has been appended to head\n },\n // array to keep track of the dynamically loaded CSS files\n loadedCssFiles: [],\n\n /**\n * @function\n * @description Removes all css files which were dynamically loaded\n */\n clearDynamicCss: function () {\n var i = this.loadedCssFiles.length;\n while (i-- > 0) {\n $(this.loadedCssFiles[i]).remove();\n }\n this.loadedCssFiles = [];\n },\n\n /**\n * @function\n * @description Extracts all parameters from a given query string into an object\n * @param {string} qs The query string from which the parameters will be extracted\n * @returns {Object} - Object with params from the query string\n */\n getQueryStringParams: function (qs) {\n if (!qs || qs.length === 0) { return {}; }\n var params = {};\n var unescapedQS = decodeURIComponent(qs);\n // Use the String::replace method to iterate over each\n // name-value pair in the string.\n unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'),\n function ($0, $1, $2, $3) {\n params[$1] = $3;\n }\n );\n return params;\n },\n\n /**\n * @function\n * @description Fills in the given form with the given address information\n * @param {Object} address - Address fields object\n * @param {Object} $form - Form jQuery object\n */\n fillAddressFields: function (address, $form) {\n var fields = Object.keys(address);\n for (var i = 0; i < fields.length; i++) {\n var field = fields[i];\n if (field !== 'ID' && field !== 'UUID' && field !== 'key') {\n // if the key in address object ends with 'Code', remove that suffix\n // keys that ends with 'Code' are postalCode, stateCode and countryCode\n $form.find('[name$=\"' + field.replace('Code', '') + '\"]').val(address[field]);\n // update the state fields\n if (field === 'countryCode') {\n $form.find('[name$=\"country\"]').trigger('change');\n // retrigger state selection after country has changed\n // this results in duplication of the state code, but is a necessary evil\n // for now because sometimes countryCode comes after stateCode\n $form.find('[name$=\"state\"]').val(address.stateCode);\n }\n }\n }\n },\n\n /**\n * @function\n * @description Binds the onclick-event to a delete button on a given container,\n * which opens a confirmation box with a given message\n * @param {string} container - The name of element to which the function will be bind\n * @param {string} message - The message the will be shown upon a click\n */\n setDeleteConfirmation: function (container, message) {\n $(container).on('click', '.delete', function () {\n return window.confirm(message); // eslint-disable-line\n });\n },\n\n /**\n * @function\n * @description Scrolls a browser window to a given x point\n * @param {string} xLocation - The x coordinate\n */\n scrollBrowser: function (xLocation) {\n $('html, body').animate({\n scrollTop: xLocation\n }, 500);\n },\n\n /**\n * @function\n * @desc Determines if the device that is being used is mobile\n * @returns {boolean} - Wether or not the browser is currently mobile\n */\n isMobile: function () {\n var mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];\n var idx = 0;\n var isMobile = false;\n var userAgent = (navigator.userAgent).toLowerCase();\n\n while (mobileAgentHash[idx] && !isMobile) {\n isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);\n idx++;\n }\n return isMobile;\n },\n\n /**\n * @function\n * @description Executes a callback function when the user has stopped resizing the screen.\n * @param {function} callback - Callback function for the resize event\n * @return {function} - Callback function for the resize event\n */\n smartResize: function (callback) {\n var timeout;\n\n $(window).on('resize', function () {\n clearTimeout(timeout);\n timeout = setTimeout(callback, 100);\n }).resize();\n\n return callback;\n },\n\n /**\n * @function\n * @desc Generates a min-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {Object} breakpoints - Override of the util breakpoints (optional)\n * @returns {boolean} - Wether or not the given media query matches\n */\n mediaBreakpointUp: function (size, breakpoints) {\n var breakpoint = this.getViewports(size, breakpoints);\n var mediaQuery = window.matchMedia('screen and (min-width: ' + breakpoint + 'px)');\n return mediaQuery.matches;\n },\n\n /**\n * @function\n * @desc Generates a min-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {Object} breakpoints - Override of the util breakpoints object (optional)\n * @returns {boolean} - Wether or not the given media query matches\n */\n mediaBreakpointDown: function (size, breakpoints) {\n var bps = typeof breakpoints !== 'undefined' ? breakpoints : this.breakpoints;\n var nextSize = this.getNextObjectKey(bps, size);\n\n if (typeof nextSize === 'string') {\n var breakpoint = this.getViewports(nextSize, breakpoints) - 1;\n var mediaQuery = window.matchMedia('screen and (max-width: ' + breakpoint + 'px)');\n return mediaQuery.matches;\n }\n\n return true;\n },\n\n /**\n * @function\n * @desc Generates a min-width and max-width matchMedia media queries based on the given params\n * @param {string} sizeMin - Min breakpoint to use for the media query\n * @param {string} sizeMax - Max breakpoint to use for the media query\n * @param {Object} breakpoints - Override of the util breakpoints object (optional)\n * @returns {boolean} - Wether or not the given media query matches\n */\n mediaBreakpointBetween: function (sizeMin, sizeMax, breakpoints) {\n var min = this.mediaBreakpointUp(sizeMin, breakpoints);\n var max = this.mediaBreakpointDown(sizeMax, breakpoints);\n\n return min && max;\n },\n\n /**\n * @function\n * @desc Generates a min-width and max-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {Object} breakpoints - Override of the util breakpoints object (optional)\n * @returns {boolean} - Wether or not the given media query matches\n */\n mediaBreakpointOnly: function (size, breakpoints) {\n return this.mediaBreakpointBetween(size, size, breakpoints);\n },\n\n /**\n * @function\n * @desc Retrieves the next key in the object or null if it doesn't exist\n * @param {Object} obj - Object to get the next key from\n * @param {string} key - Key to base the next key index on\n * @returns {string}|{null} - The next key of the given object or null if one doesn't exist\n */\n getNextObjectKey: function (obj, key) {\n var keys = Object.keys(obj);\n var nextIndex = keys.indexOf(key) + 1;\n\n if (keys.length > nextIndex) {\n return keys[nextIndex];\n }\n\n return null;\n },\n\n /**\n * @function\n * @desc Retrieves the util breakpoints object\n * @returns {Object} - All of the breakpoints\n */\n getBreakpoints: function () {\n return this.breakpoints;\n }\n};\n\nmodule.exports = utils;\n","'use strict';\n\nvar base = require('../product/base');\nvar focusHelper = require('../components/focus');\n\n/**\n * appends params to a url\n * @param {string} url - Original url\n * @param {Object} params - Parameters to append\n * @returns {string} result url with appended parameters\n */\nfunction appendToUrl(url, params) {\n var newUrl = url;\n newUrl += (newUrl.indexOf('?') !== -1 ? '&' : '?') + Object.keys(params).map(function (key) {\n return key + '=' + encodeURIComponent(params[key]);\n }).join('&');\n\n return newUrl;\n}\n\n/**\n * Checks whether the basket is valid. if invalid displays error message and disables\n * checkout button\n * @param {Object} data - AJAX response from the server\n */\nfunction validateBasket(data) {\n if (data.valid.error) {\n if (data.valid.message) {\n var errorHtml = '
' +\n '' + data.valid.message + '
';\n\n $('.cart-error').append(errorHtml);\n } else {\n $('.cart').empty().append('
' +\n '
' +\n '

' + data.resources.emptyCartMsg + '

' +\n '
' +\n '
'\n );\n $('.number-of-items').empty().append(data.resources.numberOfItems);\n $('.minicart-quantity').empty().append(data.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.resources.minicartCountOfItems,\n title: data.resources.minicartCountOfItems\n });\n $('.minicart .popover').empty();\n $('.minicart .popover').removeClass('show');\n }\n\n $('.checkout-btn').addClass('disabled');\n } else {\n $('.checkout-btn').removeClass('disabled');\n }\n}\n\n/**\n * re-renders the order totals and the number of items in the cart\n * @param {Object} data - AJAX response from the server\n */\nfunction updateCartTotals(data) {\n $('.number-of-items').empty().append(data.resources.numberOfItems);\n $('.shipping-cost').empty().append(data.totals.totalShippingCost);\n $('.tax-total').empty().append(data.totals.totalTax);\n $('.grand-total').empty().append(data.totals.grandTotal);\n $('.sub-total').empty().append(data.totals.subTotal);\n $('.minicart-quantity').empty().append(data.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.resources.minicartCountOfItems,\n title: data.resources.minicartCountOfItems\n });\n if (data.totals.orderLevelDiscountTotal.value > 0) {\n $('.order-discount').removeClass('hide-order-discount');\n $('.order-discount-total').empty()\n .append('- ' + data.totals.orderLevelDiscountTotal.formatted);\n } else {\n $('.order-discount').addClass('hide-order-discount');\n }\n\n if (data.totals.shippingLevelDiscountTotal.value > 0) {\n $('.shipping-discount').removeClass('hide-shipping-discount');\n $('.shipping-discount-total').empty().append('- ' +\n data.totals.shippingLevelDiscountTotal.formatted);\n } else {\n $('.shipping-discount').addClass('hide-shipping-discount');\n }\n\n data.items.forEach(function (item) {\n if (data.totals.orderLevelDiscountTotal.value > 0) {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n }\n if (item.renderedPromotions) {\n $('.item-' + item.UUID).empty().append(item.renderedPromotions);\n } else {\n $('.item-' + item.UUID).empty();\n }\n $('.uuid-' + item.UUID + ' .unit-price').empty().append(item.renderedPrice);\n $('.line-item-price-' + item.UUID + ' .unit-price').empty().append(item.renderedPrice);\n $('.item-total-' + item.UUID).empty().append(item.priceTotal.renderedPrice);\n });\n}\n\n/**\n * re-renders the order totals and the number of items in the cart\n * @param {Object} message - Error message to display\n */\nfunction createErrorNotification(message) {\n var errorHtml = '
' +\n '' + message + '
';\n\n $('.cart-error').append(errorHtml);\n}\n\n/**\n * re-renders the approaching discount messages\n * @param {Object} approachingDiscounts - updated approaching discounts for the cart\n */\nfunction updateApproachingDiscounts(approachingDiscounts) {\n var html = '';\n $('.approaching-discounts').empty();\n if (approachingDiscounts.length > 0) {\n approachingDiscounts.forEach(function (item) {\n html += '
'\n + item.discountMsg + '
';\n });\n }\n $('.approaching-discounts').append(html);\n}\n\n/**\n * Updates the availability of a product line item\n * @param {Object} data - AJAX response from the server\n * @param {string} uuid - The uuid of the product line item to update\n */\nfunction updateAvailability(data, uuid) {\n var lineItem;\n var messages = '';\n\n for (var i = 0; i < data.items.length; i++) {\n if (data.items[i].UUID === uuid) {\n lineItem = data.items[i];\n break;\n }\n }\n\n if (lineItem != null) {\n $('.availability-' + lineItem.UUID).empty();\n\n if (lineItem.availability) {\n if (lineItem.availability.messages) {\n lineItem.availability.messages.forEach(function (message) {\n messages += '

' + message + '

';\n });\n }\n\n if (lineItem.availability.inStockDate) {\n messages += '

'\n + lineItem.availability.inStockDate\n + '

';\n }\n }\n\n $('.availability-' + lineItem.UUID).html(messages);\n }\n}\n\n/**\n * Finds an element in the array that matches search parameter\n * @param {array} array - array of items to search\n * @param {function} match - function that takes an element and returns a boolean indicating if the match is made\n * @returns {Object|null} - returns an element of the array that matched the query.\n */\nfunction findItem(array, match) { // eslint-disable-line no-unused-vars\n for (var i = 0, l = array.length; i < l; i++) {\n if (match.call(this, array[i])) {\n return array[i];\n }\n }\n return null;\n}\n\n/**\n * Updates details of a product line item\n * @param {Object} data - AJAX response from the server\n * @param {string} uuid - The uuid of the product line item to update\n */\nfunction updateProductDetails(data, uuid) {\n $('.card.product-info.uuid-' + uuid).replaceWith(data.renderedTemplate);\n}\n\n/**\n * Generates the modal window on the first call.\n *\n */\nfunction getModalHtmlElement() {\n if ($('#editProductModal').length !== 0) {\n $('#editProductModal').remove();\n }\n var htmlString = ''\n + '
'\n + ''\n + '
'\n + ''\n + '
'\n + '
'\n + ' '\n + '
'\n + '
'\n + '
'\n + '
'\n + '
'\n + '
';\n $('body').append(htmlString);\n}\n\n/**\n * Parses the html for a modal window\n * @param {string} html - representing the body and footer of the modal window\n *\n * @return {Object} - Object with properties body and footer.\n */\nfunction parseHtml(html) {\n var $html = $('
').append($.parseHTML(html));\n\n var body = $html.find('.product-quickview');\n var footer = $html.find('.modal-footer').children();\n\n return { body: body, footer: footer };\n}\n\n/**\n * replaces the content in the modal window for product variation to be edited.\n * @param {string} editProductUrl - url to be used to retrieve a new product model\n */\nfunction fillModalElement(editProductUrl) {\n $('.modal-body').spinner().start();\n $.ajax({\n url: editProductUrl,\n method: 'GET',\n dataType: 'json',\n success: function (data) {\n var parsedHtml = parseHtml(data.renderedTemplate);\n\n $('#editProductModal .modal-body').empty();\n $('#editProductModal .modal-body').html(parsedHtml.body);\n $('#editProductModal .modal-footer').html(parsedHtml.footer);\n $('#editProductModal .modal-header .close .sr-only').text(data.closeButtonText);\n $('#editProductModal .enter-message').text(data.enterDialogMessage);\n $('#editProductModal').modal('show');\n $('body').trigger('editproductmodal:ready');\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * replace content of modal\n * @param {string} actionUrl - url to be used to remove product\n * @param {string} productID - pid\n * @param {string} productName - product name\n * @param {string} uuid - uuid\n */\nfunction confirmDelete(actionUrl, productID, productName, uuid) {\n var $deleteConfirmBtn = $('.cart-delete-confirmation-btn');\n var $productToRemoveSpan = $('.product-to-remove');\n\n $deleteConfirmBtn.data('pid', productID);\n $deleteConfirmBtn.data('action', actionUrl);\n $deleteConfirmBtn.data('uuid', uuid);\n\n $productToRemoveSpan.empty().append(productName);\n}\n\nmodule.exports = function () {\n $('body').on('click', '.remove-product', function (e) {\n e.preventDefault();\n\n var actionUrl = $(this).data('action');\n var productID = $(this).data('pid');\n var productName = $(this).data('name');\n var uuid = $(this).data('uuid');\n confirmDelete(actionUrl, productID, productName, uuid);\n });\n\n $('body').on('afterRemoveFromCart', function (e, data) {\n e.preventDefault();\n confirmDelete(data.actionUrl, data.productID, data.productName, data.uuid);\n });\n\n $('.optional-promo').click(function (e) {\n e.preventDefault();\n $('.promo-code-form').toggle();\n });\n\n $('body').on('click', '.cart-delete-confirmation-btn', function (e) {\n e.preventDefault();\n\n var productID = $(this).data('pid');\n var url = $(this).data('action');\n var uuid = $(this).data('uuid');\n var urlParams = {\n pid: productID,\n uuid: uuid\n };\n\n url = appendToUrl(url, urlParams);\n\n $('body > .modal-backdrop').remove();\n\n $.spinner().start();\n\n $('body').trigger('cart:beforeUpdate');\n\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n success: function (data) {\n if (data.basket.items.length === 0) {\n $('.cart').empty().append('
' +\n '
' +\n '

' + data.basket.resources.emptyCartMsg + '

' +\n '
' +\n '
'\n );\n $('.number-of-items').empty().append(data.basket.resources.numberOfItems);\n $('.minicart-quantity').empty().append(data.basket.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.basket.resources.minicartCountOfItems,\n title: data.basket.resources.minicartCountOfItems\n });\n $('.minicart .popover').empty();\n $('.minicart .popover').removeClass('show');\n $('body').removeClass('modal-open');\n $('html').removeClass('veiled');\n } else {\n if (data.toBeDeletedUUIDs && data.toBeDeletedUUIDs.length > 0) {\n for (var i = 0; i < data.toBeDeletedUUIDs.length; i++) {\n $('.uuid-' + data.toBeDeletedUUIDs[i]).remove();\n }\n }\n $('.uuid-' + uuid).remove();\n if (!data.basket.hasBonusProduct) {\n $('.bonus-product').remove();\n }\n $('.coupons-and-promos').empty().append(data.basket.totals.discountsHtml);\n updateCartTotals(data.basket);\n updateApproachingDiscounts(data.basket.approachingDiscounts);\n $('body').trigger('setShippingMethodSelection', data.basket);\n validateBasket(data.basket);\n }\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n });\n\n $('body').on('change', '.quantity-form > .quantity', function () {\n var preSelectQty = $(this).data('pre-select-qty');\n var quantity = $(this).val();\n var productID = $(this).data('pid');\n var url = $(this).data('action');\n var uuid = $(this).data('uuid');\n\n var urlParams = {\n pid: productID,\n quantity: quantity,\n uuid: uuid\n };\n url = appendToUrl(url, urlParams);\n\n $(this).parents('.card').spinner().start();\n\n $('body').trigger('cart:beforeUpdate');\n\n $.ajax({\n url: url,\n type: 'get',\n context: this,\n dataType: 'json',\n success: function (data) {\n $('.quantity[data-uuid=\"' + uuid + '\"]').val(quantity);\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n updateAvailability(data, uuid);\n validateBasket(data);\n $(this).data('pre-select-qty', quantity);\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n if ($(this).parents('.product-info').hasClass('bonus-product-line-item') && $('.cart-page').length) {\n location.reload();\n }\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $(this).val(parseInt(preSelectQty, 10));\n $.spinner().stop();\n }\n }\n });\n });\n\n $('.shippingMethods').change(function () {\n var url = $(this).attr('data-actionUrl');\n var urlParams = {\n methodID: $(this).find(':selected').attr('data-shipping-id')\n };\n // url = appendToUrl(url, urlParams);\n\n $('.totals').spinner().start();\n $('body').trigger('cart:beforeShippingMethodSelected');\n $.ajax({\n url: url,\n type: 'post',\n dataType: 'json',\n data: urlParams,\n success: function (data) {\n if (data.error) {\n window.location.href = data.redirectUrl;\n } else {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n }\n\n $('body').trigger('cart:shippingMethodSelected', data);\n $.spinner().stop();\n },\n error: function (err) {\n if (err.redirectUrl) {\n window.location.href = err.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n });\n\n $('.promo-code-form').submit(function (e) {\n e.preventDefault();\n $.spinner().start();\n $('.coupon-missing-error').hide();\n $('.coupon-error-message').empty();\n if (!$('.coupon-code-field').val()) {\n $('.promo-code-form .form-control').addClass('is-invalid');\n $('.promo-code-form .form-control').attr('aria-describedby', 'missingCouponCode');\n $('.coupon-missing-error').show();\n $.spinner().stop();\n return false;\n }\n var $form = $('.promo-code-form');\n $('.promo-code-form .form-control').removeClass('is-invalid');\n $('.coupon-error-message').empty();\n $('body').trigger('promotion:beforeUpdate');\n\n $.ajax({\n url: $form.attr('action'),\n type: 'GET',\n dataType: 'json',\n data: $form.serialize(),\n success: function (data) {\n if (data.error) {\n $('.promo-code-form .form-control').addClass('is-invalid');\n $('.promo-code-form .form-control').attr('aria-describedby', 'invalidCouponCode');\n $('.coupon-error-message').empty().append(data.errorMessage);\n $('body').trigger('promotion:error', data);\n } else {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n $('body').trigger('promotion:success', data);\n }\n $('.coupon-code-field').val('');\n $.spinner().stop();\n },\n error: function (err) {\n $('body').trigger('promotion:error', err);\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.errorMessage);\n $.spinner().stop();\n }\n }\n });\n return false;\n });\n\n $('body').on('click', '.remove-coupon', function (e) {\n e.preventDefault();\n\n var couponCode = $(this).data('code');\n var uuid = $(this).data('uuid');\n var $deleteConfirmBtn = $('.delete-coupon-confirmation-btn');\n var $productToRemoveSpan = $('.coupon-to-remove');\n\n $deleteConfirmBtn.data('uuid', uuid);\n $deleteConfirmBtn.data('code', couponCode);\n\n $productToRemoveSpan.empty().append(couponCode);\n });\n\n $('body').on('click', '.delete-coupon-confirmation-btn', function (e) {\n e.preventDefault();\n\n var url = $(this).data('action');\n var uuid = $(this).data('uuid');\n var couponCode = $(this).data('code');\n var urlParams = {\n code: couponCode,\n uuid: uuid\n };\n\n url = appendToUrl(url, urlParams);\n\n $('body > .modal-backdrop').remove();\n\n $.spinner().start();\n $('body').trigger('promotion:beforeUpdate');\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n success: function (data) {\n $('.coupon-uuid-' + uuid).remove();\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n $.spinner().stop();\n $('body').trigger('promotion:success', data);\n },\n error: function (err) {\n $('body').trigger('promotion:error', err);\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n });\n $('body').on('click', '.cart-page .bonus-product-button', function () {\n $.spinner().start();\n $(this).addClass('launched-modal');\n $.ajax({\n url: $(this).data('url'),\n method: 'GET',\n dataType: 'json',\n success: function (data) {\n base.methods.editBonusProducts(data);\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n });\n\n $('body').on('hidden.bs.modal', '#chooseBonusProductModal', function () {\n $('#chooseBonusProductModal').remove();\n $('.modal-backdrop').remove();\n $('body').removeClass('modal-open');\n\n if ($('.cart-page').length) {\n $('.launched-modal .btn-outline-primary').trigger('focus');\n $('.launched-modal').removeClass('launched-modal');\n } else {\n $('.product-detail .add-to-cart').focus();\n }\n });\n\n $('body').on('click', '.cart-page .product-edit .edit, .cart-page .bundle-edit .edit', function (e) {\n e.preventDefault();\n\n var editProductUrl = $(this).attr('href');\n getModalHtmlElement();\n fillModalElement(editProductUrl);\n });\n\n $('body').on('shown.bs.modal', '#editProductModal', function () {\n $('#editProductModal').siblings().attr('aria-hidden', 'true');\n $('#editProductModal .close').focus();\n });\n\n $('body').on('hidden.bs.modal', '#editProductModal', function () {\n $('#editProductModal').siblings().attr('aria-hidden', 'false');\n });\n\n $('body').on('keydown', '#editProductModal', function (e) {\n var focusParams = {\n event: e,\n containerSelector: '#editProductModal',\n firstElementSelector: '.close',\n lastElementSelector: '.update-cart-product-global',\n nextToLastElementSelector: '.modal-footer .quantity-select'\n };\n focusHelper.setTabNextFocus(focusParams);\n });\n\n $('body').on('product:updateAddToCart', function (e, response) {\n // update global add to cart (single products, bundles)\n var dialog = $(response.$productContainer)\n .closest('.quick-view-dialog');\n\n $('.update-cart-product-global', dialog).attr('disabled',\n !$('.global-availability', dialog).data('ready-to-order')\n || !$('.global-availability', dialog).data('available')\n );\n });\n\n $('body').on('product:updateAvailability', function (e, response) {\n // bundle individual products\n $('.product-availability', response.$productContainer)\n .data('ready-to-order', response.product.readyToOrder)\n .data('available', response.product.available)\n .find('.availability-msg')\n .empty()\n .html(response.message);\n\n\n var dialog = $(response.$productContainer)\n .closest('.quick-view-dialog');\n\n if ($('.product-availability', dialog).length) {\n // bundle all products\n var allAvailable = $('.product-availability', dialog).toArray()\n .every(function (item) { return $(item).data('available'); });\n\n var allReady = $('.product-availability', dialog).toArray()\n .every(function (item) { return $(item).data('ready-to-order'); });\n\n $('.global-availability', dialog)\n .data('ready-to-order', allReady)\n .data('available', allAvailable);\n\n $('.global-availability .availability-msg', dialog).empty()\n .html(allReady ? response.message : response.resources.info_selectforstock);\n } else {\n // single product\n $('.global-availability', dialog)\n .data('ready-to-order', response.product.readyToOrder)\n .data('available', response.product.available)\n .find('.availability-msg')\n .empty()\n .html(response.message);\n }\n });\n\n $('body').on('product:afterAttributeSelect', function (e, response) {\n if ($('.modal.show .product-quickview .bundle-items').length) {\n $('.modal.show').find(response.container).data('pid', response.data.product.id);\n $('.modal.show').find(response.container).find('.product-id').text(response.data.product.id);\n } else {\n $('.modal.show .product-quickview').data('pid', response.data.product.id);\n }\n });\n\n $('body').on('change', '.quantity-select', function () {\n var selectedQuantity = $(this).val();\n $('.modal.show .update-cart-url').data('selected-quantity', selectedQuantity);\n });\n\n $('body').on('change', '.options-select', function () {\n var selectedOptionValueId = $(this).children('option:selected').data('value-id');\n $('.modal.show .update-cart-url').data('selected-option', selectedOptionValueId);\n });\n\n $('body').on('click', '.update-cart-product-global', function (e) {\n e.preventDefault();\n\n var updateProductUrl = $(this).closest('.cart-and-ipay').find('.update-cart-url').val();\n var selectedQuantity = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('selected-quantity');\n var selectedOptionValueId = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('selected-option');\n var uuid = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('uuid');\n\n var form = {\n uuid: uuid,\n pid: base.getPidValue($(this)),\n quantity: selectedQuantity,\n selectedOptionValueId: selectedOptionValueId\n };\n\n $(this).parents('.card').spinner().start();\n\n $('body').trigger('cart:beforeUpdate');\n\n if (updateProductUrl) {\n $.ajax({\n url: updateProductUrl,\n type: 'post',\n context: this,\n data: form,\n dataType: 'json',\n success: function (data) {\n $('#editProductModal').modal('hide');\n\n $('.coupons-and-promos').empty().append(data.cartModel.totals.discountsHtml);\n updateCartTotals(data.cartModel);\n updateApproachingDiscounts(data.cartModel.approachingDiscounts);\n updateAvailability(data.cartModel, uuid);\n updateProductDetails(data, uuid);\n\n if (data.uuidToBeDeleted) {\n $('.uuid-' + data.uuidToBeDeleted).remove();\n }\n\n validateBasket(data.cartModel);\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n }\n });\n\n base.selectAttribute();\n base.colorAttribute();\n base.removeBonusProduct();\n base.selectBonusProduct();\n base.enableBonusProductSelection();\n base.showMoreBonusProducts();\n base.addBonusProductsToCart();\n base.focusChooseBonusProductModal();\n base.trapChooseBonusProductModalFocus();\n base.onClosingChooseBonusProductModal();\n};\n","'use strict';\n\n/**\n * Validate whole form. Requires `this` to be set to form object\n * @param {jQuery.event} event - Event to be canceled if form is invalid.\n * @returns {boolean} - Flag to indicate if form is valid\n */\nfunction validateForm(event) {\n var valid = true;\n if (this.checkValidity && !this.checkValidity()) {\n // safari\n valid = false;\n if (event) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n }\n $(this).find('input, select').each(function () {\n if (!this.validity.valid) {\n $(this).trigger('invalid', this.validity);\n }\n });\n }\n return valid;\n}\n\n/**\n * Remove all validation. Should be called every time before revalidating form\n * @param {element} form - Form to be cleared\n * @returns {void}\n */\nfunction clearForm(form) {\n $(form).find('.form-control.is-invalid').removeClass('is-invalid');\n}\n\nmodule.exports = {\n invalid: function () {\n $('form input, form select').on('invalid', function (e) {\n e.preventDefault();\n this.setCustomValidity('');\n if (!this.validity.valid) {\n var validationMessage = this.validationMessage;\n $(this).addClass('is-invalid');\n if (this.validity.patternMismatch && $(this).data('pattern-mismatch')) {\n validationMessage = $(this).data('pattern-mismatch');\n }\n if ((this.validity.rangeOverflow || this.validity.rangeUnderflow)\n && $(this).data('range-error')) {\n validationMessage = $(this).data('range-error');\n }\n if ((this.validity.tooLong || this.validity.tooShort)\n && $(this).data('range-error')) {\n validationMessage = $(this).data('range-error');\n }\n if (this.validity.valueMissing && $(this).data('missing-error')) {\n validationMessage = $(this).data('missing-error');\n }\n $(this).parents('.form-group').find('.invalid-feedback')\n .text(validationMessage);\n }\n });\n },\n\n submit: function () {\n $('form').on('submit', function (e) {\n return validateForm.call(this, e);\n });\n },\n\n buttonClick: function () {\n $('form button[type=\"submit\"], form input[type=\"submit\"]').on('click', function () {\n // clear all errors when trying to submit the form\n clearForm($(this).parents('form'));\n });\n },\n\n functions: {\n validateForm: function (form, event) {\n validateForm.call($(form), event || null);\n },\n clearForm: clearForm\n }\n};\n","'use strict';\nmodule.exports = function () {\n var sizes = ['xs', 'sm', 'md', 'lg', 'xl'];\n\n sizes.forEach(function (size) {\n var selector = '.collapsible-' + size + ' .title';\n $('body').on('click', selector, function (e) {\n e.preventDefault();\n $(this).parents('.collapsible-' + size).toggleClass('active');\n\n if ($(this).parents('.collapsible-' + size).hasClass('active')) {\n $(this).attr('aria-expanded', true);\n } else {\n $(this).attr('aria-expanded', false);\n }\n });\n });\n};\n","'use strict';\n\n/**\n * Get cookie value by cookie name from browser\n * @param {string} cookieName - name of the cookie\n * @returns {string} cookie value of the found cookie name\n */\nfunction getCookie(cookieName) {\n var name = cookieName + '=';\n var decodedCookie = decodeURIComponent(document.cookie);\n var cookieArray = decodedCookie.split(';');\n for (var i = 0; i < cookieArray.length; i++) {\n var cookieItem = cookieArray[i];\n while (cookieItem.charAt(0) === ' ') {\n cookieItem = cookieItem.substring(1);\n }\n if (cookieItem.indexOf(name) === 0) {\n return cookieItem.substring(name.length, cookieItem.length);\n }\n }\n return '';\n}\n\nmodule.exports = function () {\n if ($('.valid-cookie-warning').length > 0) {\n var previousSessionID = window.localStorage.getItem('previousSid');\n var currentSessionID = getCookie('sid');\n if (!previousSessionID && currentSessionID) {\n // When a user first time visit the home page,\n // set the previousSessionID to currentSessionID\n // and Show the cookie alert\n previousSessionID = currentSessionID;\n window.localStorage.setItem('previousSid', previousSessionID);\n $('.cookie-warning-messaging').show();\n } else if (previousSessionID && previousSessionID === currentSessionID) {\n // Hide the cookie alert if user is in the same session\n $('.cookie-warning-messaging').hide();\n } else {\n // Clear the previousSessionID from localStorage\n // when user session is changed or expired\n window.localStorage.removeItem('previousSid');\n }\n }\n};\n","'use strict';\n\nvar keyboardAccessibility = require('./keyboardAccessibility');\n\nmodule.exports = function () {\n $('.country-selector a').click(function (e) {\n e.preventDefault();\n var action = $('.page').data('action');\n var localeCode = $(this).data('locale');\n var localeCurrencyCode = $(this).data('currencycode');\n var queryString = $('.page').data('querystring');\n var url = $('.country-selector').data('url');\n\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n data: {\n code: localeCode,\n queryString: queryString,\n CurrencyCode: localeCurrencyCode,\n action: action\n },\n success: function (response) {\n $.spinner().stop();\n if (response && response.redirectUrl) {\n window.location.href = response.redirectUrl;\n }\n },\n error: function () {\n $.spinner().stop();\n }\n });\n });\n\n keyboardAccessibility('.navbar-header .country-selector',\n {\n 40: function ($countryOptions) { // down\n if ($(this).is(':focus')) {\n $countryOptions.first().focus();\n } else {\n $(':focus').next().focus();\n }\n },\n 38: function ($countryOptions) { // up\n if ($countryOptions.first().is(':focus') || $(this).is(':focus')) {\n $(this).focus();\n $(this).removeClass('show');\n } else {\n $(':focus').prev().focus();\n }\n },\n 27: function () { // escape\n $(this).focus();\n $(this).removeClass('show').children('.dropdown-menu').removeClass('show');\n },\n 9: function () { // tab\n $(this).removeClass('show').children('.dropdown-menu').removeClass('show');\n }\n },\n function () {\n if (!($(this).hasClass('show'))) {\n $(this).addClass('show');\n }\n return $(this).find('.dropdown-country-selector').children('a');\n }\n );\n\n $('.navbar-header .country-selector').on('focusin', function () {\n $(this).addClass('show').children('.dropdown-menu').addClass('show');\n });\n};\n","'use strict';\n\nmodule.exports = {\n setTabNextFocus: function (focusParams) {\n var KEYCODE_TAB = 9;\n var isTabPressed = (focusParams.event.key === 'Tab' || focusParams.event.keyCode === KEYCODE_TAB);\n\n if (!isTabPressed) {\n return;\n }\n\n var firstFocusableEl = $(focusParams.containerSelector + ' ' + focusParams.firstElementSelector);\n var lastFocusableEl = $(focusParams.containerSelector + ' ' + focusParams.lastElementSelector);\n\n if ($(focusParams.containerSelector + ' ' + focusParams.lastElementSelector).is(':disabled')) {\n lastFocusableEl = $(focusParams.containerSelector + ' ' + focusParams.nextToLastElementSelector);\n if ($('.product-quickview.product-set').length > 0) {\n var linkElements = $(focusParams.containerSelector + ' a#fa-link.share-icons');\n lastFocusableEl = linkElements[linkElements.length - 1];\n }\n }\n\n if (focusParams.event.shiftKey) /* shift + tab */ {\n if ($(':focus').is(firstFocusableEl)) {\n lastFocusableEl.focus();\n focusParams.event.preventDefault();\n }\n } else /* tab */ {\n if ($(':focus').is(lastFocusableEl)) { // eslint-disable-line\n firstFocusableEl.focus();\n focusParams.event.preventDefault();\n }\n }\n }\n};\n","'use strict';\n\n/**\n * Remove all validation. Should be called every time before revalidating form\n * @param {element} form - Form to be cleared\n * @returns {void}\n */\nfunction clearFormErrors(form) {\n $(form).find('.form-control.is-invalid').removeClass('is-invalid');\n}\n\nmodule.exports = function (formElement, payload) {\n // clear form validation first\n clearFormErrors(formElement);\n $('.alert', formElement).remove();\n\n if (typeof payload === 'object' && payload.fields) {\n Object.keys(payload.fields).forEach(function (key) {\n if (payload.fields[key]) {\n var feedbackElement = $(formElement).find('[name=\"' + key + '\"]')\n .parent()\n .children('.invalid-feedback');\n\n if (feedbackElement.length > 0) {\n if (Array.isArray(payload[key])) {\n feedbackElement.html(payload.fields[key].join('
'));\n } else {\n feedbackElement.html(payload.fields[key]);\n }\n feedbackElement.siblings('.form-control').addClass('is-invalid');\n }\n }\n });\n }\n if (payload && payload.error) {\n var form = $(formElement).prop('tagName') === 'FORM'\n ? $(formElement)\n : $(formElement).parents('form');\n\n form.prepend('
'\n + payload.error.join('
') + '
');\n }\n};\n","'use strict';\n\nmodule.exports = function (selector, keyFunctions, preFunction) {\n $(selector).on('keydown', function (e) {\n var key = e.which;\n var supportedKeyCodes = [37, 38, 39, 40, 27];\n if (supportedKeyCodes.indexOf(key) >= 0) {\n e.preventDefault();\n }\n var returnedScope = preFunction.call(this);\n if (keyFunctions[key]) {\n keyFunctions[key].call(this, returnedScope);\n }\n });\n};\n","'use strict';\n\nvar cart = require('../cart/cart');\n\nvar updateMiniCart = true;\n\nmodule.exports = function () {\n cart();\n\n $('.minicart').on('count:update', function (event, count) {\n if (count && $.isNumeric(count.quantityTotal)) {\n $('.minicart .minicart-quantity').text(count.quantityTotal);\n $('.minicart .minicart-link').attr({\n 'aria-label': count.minicartCountOfItems,\n title: count.minicartCountOfItems\n });\n }\n });\n\n $('.minicart').on('mouseenter focusin touchstart', function () {\n if ($('.search:visible').length === 0) {\n return;\n }\n var url = $('.minicart').data('action-url');\n var count = parseInt($('.minicart .minicart-quantity').text(), 10);\n\n if (count !== 0 && $('.minicart .popover.show').length === 0) {\n if (!updateMiniCart) {\n $('.minicart .popover').addClass('show');\n return;\n }\n\n $('.minicart .popover').addClass('show');\n $('.minicart .popover').spinner().start();\n $.get(url, function (data) {\n $('.minicart .popover').empty();\n $('.minicart .popover').append(data);\n updateMiniCart = false;\n $.spinner().stop();\n });\n }\n });\n $('body').on('touchstart click', function (e) {\n if ($('.minicart').has(e.target).length <= 0) {\n $('.minicart .popover').removeClass('show');\n }\n });\n $('.minicart').on('mouseleave focusout', function (event) {\n if ((event.type === 'focusout' && $('.minicart').has(event.target).length > 0)\n || (event.type === 'mouseleave' && $(event.target).is('.minicart .quantity'))\n || $('body').hasClass('modal-open')) {\n event.stopPropagation();\n return;\n }\n $('.minicart .popover').removeClass('show');\n });\n $('body').on('change', '.minicart .quantity', function () {\n if ($(this).parents('.bonus-product-line-item').length && $('.cart-page').length) {\n location.reload();\n }\n });\n $('body').on('product:afterAddToCart', function () {\n updateMiniCart = true;\n });\n $('body').on('cart:update', function () {\n updateMiniCart = true;\n });\n};\n","'use strict';\nvar focusHelper = require('../components/focus');\n\n/**\n * Retrieves the relevant pid value\n * @param {jquery} $el - DOM container for a given add to cart button\n * @return {string} - value to be used when adding product to cart\n */\nfunction getPidValue($el) {\n var pid;\n\n if ($('#quickViewModal').hasClass('show') && !$('.product-set').length) {\n pid = $($el).closest('.modal-content').find('.product-quickview').data('pid');\n } else if ($('.product-set-detail').length || $('.product-set').length) {\n pid = $($el).closest('.product-detail').find('.product-id').text();\n } else {\n pid = $('.product-detail:not(\".bundle-item\")').data('pid');\n }\n\n return pid;\n}\n\n/**\n * Retrieve contextual quantity selector\n * @param {jquery} $el - DOM container for the relevant quantity\n * @return {jquery} - quantity selector DOM container\n */\nfunction getQuantitySelector($el) {\n var quantitySelected;\n if ($el && $('.set-items').length) {\n quantitySelected = $($el).closest('.product-detail').find('.quantity-select');\n } else if ($el && $('.product-bundle').length) {\n var quantitySelectedModal = $($el).closest('.modal-footer').find('.quantity-select');\n var quantitySelectedPDP = $($el).closest('.bundle-footer').find('.quantity-select');\n if (quantitySelectedModal.val() === undefined) {\n quantitySelected = quantitySelectedPDP;\n } else {\n quantitySelected = quantitySelectedModal;\n }\n } else {\n quantitySelected = $('.quantity-select');\n }\n return quantitySelected;\n}\n\n/**\n * Retrieves the value associated with the Quantity pull-down menu\n * @param {jquery} $el - DOM container for the relevant quantity\n * @return {string} - value found in the quantity input\n */\nfunction getQuantitySelected($el) {\n return getQuantitySelector($el).val();\n}\n\n/**\n * Process the attribute values for an attribute that has image swatches\n *\n * @param {Object} attr - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {Object[]} attr.values - Array of attribute value objects\n * @param {string} attr.values.value - Attribute coded value\n * @param {string} attr.values.url - URL to de/select an attribute value of the product\n * @param {boolean} attr.values.isSelectable - Flag as to whether an attribute value can be\n * selected. If there is no variant that corresponds to a specific combination of attribute\n * values, an attribute may be disabled in the Product Detail Page\n * @param {jQuery} $productContainer - DOM container for a given product\n * @param {Object} msgs - object containing resource messages\n */\nfunction processSwatchValues(attr, $productContainer, msgs) {\n attr.values.forEach(function (attrValue) {\n var $attrValue = $productContainer.find('[data-attr=\"' + attr.id + '\"] [data-attr-value=\"' +\n attrValue.value + '\"]');\n var $swatchButton = $attrValue.parent();\n\n if (attrValue.selected) {\n $attrValue.addClass('selected');\n $attrValue.siblings('.selected-assistive-text').text(msgs.assistiveSelectedText);\n } else {\n $attrValue.removeClass('selected');\n $attrValue.siblings('.selected-assistive-text').empty();\n }\n\n if (attrValue.url) {\n $swatchButton.attr('data-url', attrValue.url);\n } else {\n $swatchButton.removeAttr('data-url');\n }\n\n // Disable if not selectable\n $attrValue.removeClass('selectable unselectable');\n\n $attrValue.addClass(attrValue.selectable ? 'selectable' : 'unselectable');\n });\n}\n\n/**\n * Process attribute values associated with an attribute that does not have image swatches\n *\n * @param {Object} attr - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {Object[]} attr.values - Array of attribute value objects\n * @param {string} attr.values.value - Attribute coded value\n * @param {string} attr.values.url - URL to de/select an attribute value of the product\n * @param {boolean} attr.values.isSelectable - Flag as to whether an attribute value can be\n * selected. If there is no variant that corresponds to a specific combination of attribute\n * values, an attribute may be disabled in the Product Detail Page\n * @param {jQuery} $productContainer - DOM container for a given product\n */\nfunction processNonSwatchValues(attr, $productContainer) {\n var $attr = '[data-attr=\"' + attr.id + '\"]';\n var $defaultOption = $productContainer.find($attr + ' .select-' + attr.id + ' option:first');\n $defaultOption.attr('value', attr.resetUrl);\n\n attr.values.forEach(function (attrValue) {\n var $attrValue = $productContainer\n .find($attr + ' [data-attr-value=\"' + attrValue.value + '\"]');\n $attrValue.attr('value', attrValue.url)\n .removeAttr('disabled');\n\n if (!attrValue.selectable) {\n $attrValue.attr('disabled', true);\n }\n });\n}\n\n/**\n * Routes the handling of attribute processing depending on whether the attribute has image\n * swatches or not\n *\n * @param {Object} attrs - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {jQuery} $productContainer - DOM element for a given product\n * @param {Object} msgs - object containing resource messages\n */\nfunction updateAttrs(attrs, $productContainer, msgs) {\n // Currently, the only attribute type that has image swatches is Color.\n var attrsWithSwatches = ['color'];\n\n attrs.forEach(function (attr) {\n if (attrsWithSwatches.indexOf(attr.id) > -1) {\n processSwatchValues(attr, $productContainer, msgs);\n } else {\n processNonSwatchValues(attr, $productContainer);\n }\n });\n}\n\n/**\n * Updates the availability status in the Product Detail Page\n *\n * @param {Object} response - Ajax response object after an\n * attribute value has been [de]selected\n * @param {jQuery} $productContainer - DOM element for a given product\n */\nfunction updateAvailability(response, $productContainer) {\n var availabilityValue = '';\n var availabilityMessages = response.product.availability.messages;\n if (!response.product.readyToOrder) {\n availabilityValue = '
  • ' + response.resources.info_selectforstock + '
  • ';\n } else {\n availabilityMessages.forEach(function (message) {\n availabilityValue += '
  • ' + message + '
  • ';\n });\n }\n\n $($productContainer).trigger('product:updateAvailability', {\n product: response.product,\n $productContainer: $productContainer,\n message: availabilityValue,\n resources: response.resources\n });\n}\n\n/**\n * Generates html for product attributes section\n *\n * @param {array} attributes - list of attributes\n * @return {string} - Compiled HTML\n */\nfunction getAttributesHtml(attributes) {\n if (!attributes) {\n return '';\n }\n\n var html = '';\n\n attributes.forEach(function (attributeGroup) {\n if (attributeGroup.ID === 'mainAttributes') {\n attributeGroup.attributes.forEach(function (attribute) {\n html += '
    ' + attribute.label + ': '\n + attribute.value + '
    ';\n });\n }\n });\n\n return html;\n}\n\n/**\n * @typedef UpdatedOptionValue\n * @type Object\n * @property {string} id - Option value ID for look up\n * @property {string} url - Updated option value selection URL\n */\n\n/**\n * @typedef OptionSelectionResponse\n * @type Object\n * @property {string} priceHtml - Updated price HTML code\n * @property {Object} options - Updated Options\n * @property {string} options.id - Option ID\n * @property {UpdatedOptionValue[]} options.values - Option values\n */\n\n/**\n * Updates DOM using post-option selection Ajax response\n *\n * @param {OptionSelectionResponse} optionsHtml - Ajax response optionsHtml from selecting a product option\n * @param {jQuery} $productContainer - DOM element for current product\n */\nfunction updateOptions(optionsHtml, $productContainer) {\n\t// Update options\n $productContainer.find('.product-options').empty().html(optionsHtml);\n}\n\n/**\n * Dynamically creates Bootstrap carousel from response containing images\n * @param {Object[]} imgs - Array of large product images,along with related information\n * @param {jQuery} $productContainer - DOM element for a given product\n */\nfunction createCarousel(imgs, $productContainer) {\n var carousel = $productContainer.find('.carousel');\n $(carousel).carousel('dispose');\n var carouselId = $(carousel).attr('id');\n $(carousel).empty().append('
      ' + $(carousel).data('prev') + '' + $(carousel).data('next') + '');\n for (var i = 0; i < imgs.length; i++) {\n $('
      \"'
      ').appendTo($(carousel).find('.carousel-inner'));\n $('
    1. ').appendTo($(carousel).find('.carousel-indicators'));\n }\n $($(carousel).find('.carousel-item')).first().addClass('active');\n $($(carousel).find('.carousel-indicators > li')).first().addClass('active');\n if (imgs.length === 1) {\n $($(carousel).find('.carousel-indicators, a[class^=\"carousel-control-\"]')).detach();\n }\n $(carousel).carousel();\n $($(carousel).find('.carousel-indicators')).attr('aria-hidden', true);\n}\n\n/**\n * Parses JSON from Ajax call made whenever an attribute value is [de]selected\n * @param {Object} response - response from Ajax call\n * @param {Object} response.product - Product object\n * @param {string} response.product.id - Product ID\n * @param {Object[]} response.product.variationAttributes - Product attributes\n * @param {Object[]} response.product.images - Product images\n * @param {boolean} response.product.hasRequiredAttrsSelected - Flag as to whether all required\n * attributes have been selected. Used partially to\n * determine whether the Add to Cart button can be enabled\n * @param {jQuery} $productContainer - DOM element for a given product.\n */\nfunction handleVariantResponse(response, $productContainer) {\n var isChoiceOfBonusProducts =\n $productContainer.parents('.choose-bonus-product-dialog').length > 0;\n var isVaraint;\n if (response.product.variationAttributes) {\n updateAttrs(response.product.variationAttributes, $productContainer, response.resources);\n isVaraint = response.product.productType === 'variant';\n if (isChoiceOfBonusProducts && isVaraint) {\n $productContainer.parent('.bonus-product-item')\n .data('pid', response.product.id);\n\n $productContainer.parent('.bonus-product-item')\n .data('ready-to-order', response.product.readyToOrder);\n }\n }\n\n // Update primary images\n var primaryImageUrls = response.product.images.large;\n createCarousel(primaryImageUrls, $productContainer);\n\n // Update pricing\n if (!isChoiceOfBonusProducts) {\n var $priceSelector = $('.prices .price', $productContainer).length\n ? $('.prices .price', $productContainer)\n : $('.prices .price');\n $priceSelector.replaceWith(response.product.price.html);\n }\n\n // Update promotions\n $productContainer.find('.promotions').empty().html(response.product.promotionsHtml);\n\n updateAvailability(response, $productContainer);\n\n if (isChoiceOfBonusProducts) {\n var $selectButton = $productContainer.find('.select-bonus-product');\n $selectButton.trigger('bonusproduct:updateSelectButton', {\n product: response.product, $productContainer: $productContainer\n });\n } else {\n // Enable \"Add to Cart\" button if all required attributes have been selected\n $('button.add-to-cart, button.add-to-cart-global, button.update-cart-product-global').trigger('product:updateAddToCart', {\n product: response.product, $productContainer: $productContainer, resources: response.resources\n }).trigger('product:statusUpdate', response.product);\n }\n\n // Update attributes\n $productContainer.find('.main-attributes').empty()\n .html(getAttributesHtml(response.product.attributes));\n}\n\n/**\n * @typespec UpdatedQuantity\n * @type Object\n * @property {boolean} selected - Whether the quantity has been selected\n * @property {string} value - The number of products to purchase\n * @property {string} url - Compiled URL that specifies variation attributes, product ID, options,\n * etc.\n */\n\n/**\n * Updates the quantity DOM elements post Ajax call\n * @param {UpdatedQuantity[]} quantities -\n * @param {jQuery} $productContainer - DOM container for a given product\n */\nfunction updateQuantities(quantities, $productContainer) {\n if ($productContainer.parent('.bonus-product-item').length <= 0) {\n var optionsHtml = quantities.map(function (quantity) {\n var selected = quantity.selected ? ' selected ' : '';\n return '';\n }).join('');\n getQuantitySelector($productContainer).empty().html(optionsHtml);\n }\n}\n\n/**\n * updates the product view when a product attribute is selected or deselected or when\n * changing quantity\n * @param {string} selectedValueUrl - the Url for the selected variation value\n * @param {jQuery} $productContainer - DOM element for current product\n */\nfunction attributeSelect(selectedValueUrl, $productContainer) {\n if (selectedValueUrl) {\n $('body').trigger('product:beforeAttributeSelect',\n { url: selectedValueUrl, container: $productContainer });\n\n $.ajax({\n url: selectedValueUrl,\n method: 'GET',\n success: function (data) {\n handleVariantResponse(data, $productContainer);\n updateOptions(data.product.optionsHtml, $productContainer);\n updateQuantities(data.product.quantities, $productContainer);\n $('body').trigger('product:afterAttributeSelect',\n { data: data, container: $productContainer });\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n }\n}\n\n/**\n * Retrieves url to use when adding a product to the cart\n *\n * @return {string} - The provided URL to use when adding a product to the cart\n */\nfunction getAddToCartUrl() {\n return $('.add-to-cart-url').val();\n}\n\n/**\n * Parses the html for a modal window\n * @param {string} html - representing the body and footer of the modal window\n *\n * @return {Object} - Object with properties body and footer.\n */\nfunction parseHtml(html) {\n var $html = $('
      ').append($.parseHTML(html));\n\n var body = $html.find('.choice-of-bonus-product');\n var footer = $html.find('.modal-footer').children();\n\n return { body: body, footer: footer };\n}\n\n/**\n * Retrieves url to use when adding a product to the cart\n *\n * @param {Object} data - data object used to fill in dynamic portions of the html\n */\nfunction chooseBonusProducts(data) {\n $('.modal-body').spinner().start();\n\n if ($('#chooseBonusProductModal').length !== 0) {\n $('#chooseBonusProductModal').remove();\n }\n var bonusUrl;\n if (data.bonusChoiceRuleBased) {\n bonusUrl = data.showProductsUrlRuleBased;\n } else {\n bonusUrl = data.showProductsUrlListBased;\n }\n\n var htmlString = ''\n + '
      '\n + ''\n + '
      '\n + ''\n + '
      '\n + '
      '\n + ' ' + data.labels.selectprods + ''\n + ' '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      ';\n $('body').append(htmlString);\n $('.modal-body').spinner().start();\n\n $.ajax({\n url: bonusUrl,\n method: 'GET',\n dataType: 'json',\n success: function (response) {\n var parsedHtml = parseHtml(response.renderedTemplate);\n $('#chooseBonusProductModal .modal-body').empty();\n $('#chooseBonusProductModal .enter-message').text(response.enterDialogMessage);\n $('#chooseBonusProductModal .modal-header .close .sr-only').text(response.closeButtonText);\n $('#chooseBonusProductModal .modal-body').html(parsedHtml.body);\n $('#chooseBonusProductModal .modal-footer').html(parsedHtml.footer);\n $('#chooseBonusProductModal').modal('show');\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * Updates the Mini-Cart quantity value after the customer has pressed the \"Add to Cart\" button\n * @param {string} response - ajax response from clicking the add to cart button\n */\nfunction handlePostCartAdd(response) {\n $('.minicart').trigger('count:update', response);\n var messageType = response.error ? 'alert-danger' : 'alert-success';\n // show add to cart toast\n if (response.newBonusDiscountLineItem\n && Object.keys(response.newBonusDiscountLineItem).length !== 0) {\n chooseBonusProducts(response.newBonusDiscountLineItem);\n } else {\n if ($('.add-to-cart-messages').length === 0) {\n $('body').append(\n '
      '\n );\n }\n\n $('.add-to-cart-messages').append(\n '
      '\n + response.message\n + '
      '\n );\n\n setTimeout(function () {\n $('.add-to-basket-alert').remove();\n }, 5000);\n }\n}\n\n/**\n * Retrieves the bundle product item ID's for the Controller to replace bundle master product\n * items with their selected variants\n *\n * @return {string[]} - List of selected bundle product item ID's\n */\nfunction getChildProducts() {\n var childProducts = [];\n $('.bundle-item').each(function () {\n childProducts.push({\n pid: $(this).find('.product-id').text(),\n quantity: parseInt($(this).find('label.quantity').data('quantity'), 10)\n });\n });\n\n return childProducts.length ? JSON.stringify(childProducts) : [];\n}\n\n/**\n * Retrieve product options\n *\n * @param {jQuery} $productContainer - DOM element for current product\n * @return {string} - Product options and their selected values\n */\nfunction getOptions($productContainer) {\n var options = $productContainer\n .find('.product-option')\n .map(function () {\n var $elOption = $(this).find('.options-select');\n var urlValue = $elOption.val();\n var selectedValueId = $elOption.find('option[value=\"' + urlValue + '\"]')\n .data('value-id');\n return {\n optionId: $(this).data('option-id'),\n selectedValueId: selectedValueId\n };\n }).toArray();\n\n return JSON.stringify(options);\n}\n\n/**\n * Makes a call to the server to report the event of adding an item to the cart\n *\n * @param {string | boolean} url - a string representing the end point to hit so that the event can be recorded, or false\n */\nfunction miniCartReportingUrl(url) {\n if (url) {\n $.ajax({\n url: url,\n method: 'GET',\n success: function () {\n // reporting urls hit on the server\n },\n error: function () {\n // no reporting urls hit on the server\n }\n });\n }\n}\n\nmodule.exports = {\n attributeSelect: attributeSelect,\n methods: {\n editBonusProducts: function (data) {\n chooseBonusProducts(data);\n }\n },\n\n focusChooseBonusProductModal: function () {\n $('body').on('shown.bs.modal', '#chooseBonusProductModal', function () {\n $('#chooseBonusProductModal').siblings().attr('aria-hidden', 'true');\n $('#chooseBonusProductModal .close').focus();\n });\n },\n\n onClosingChooseBonusProductModal: function () {\n $('body').on('hidden.bs.modal', '#chooseBonusProductModal', function () {\n $('#chooseBonusProductModal').siblings().attr('aria-hidden', 'false');\n });\n },\n\n trapChooseBonusProductModalFocus: function () {\n $('body').on('keydown', '#chooseBonusProductModal', function (e) {\n var focusParams = {\n event: e,\n containerSelector: '#chooseBonusProductModal',\n firstElementSelector: '.close',\n lastElementSelector: '.add-bonus-products'\n };\n focusHelper.setTabNextFocus(focusParams);\n });\n },\n\n colorAttribute: function () {\n $(document).on('click', '[data-attr=\"color\"] button', function (e) {\n e.preventDefault();\n\n if ($(this).attr('disabled')) {\n return;\n }\n var $productContainer = $(this).closest('.set-item');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.product-detail');\n }\n\n attributeSelect($(this).attr('data-url'), $productContainer);\n });\n },\n\n selectAttribute: function () {\n $(document).on('change', 'select[class*=\"select-\"], .options-select', function (e) {\n e.preventDefault();\n\n var $productContainer = $(this).closest('.set-item');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.product-detail');\n }\n attributeSelect(e.currentTarget.value, $productContainer);\n });\n },\n\n availability: function () {\n $(document).on('change', '.quantity-select', function (e) {\n e.preventDefault();\n\n var $productContainer = $(this).closest('.product-detail');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.modal-content').find('.product-quickview');\n }\n\n if ($('.bundle-items', $productContainer).length === 0) {\n attributeSelect($(e.currentTarget).find('option:selected').data('url'),\n $productContainer);\n }\n });\n },\n\n addToCart: function () {\n $(document).on('click', 'button.add-to-cart, button.add-to-cart-global', function () {\n var addToCartUrl;\n var pid;\n var pidsObj;\n var setPids;\n\n $('body').trigger('product:beforeAddToCart', this);\n\n if ($('.set-items').length && $(this).hasClass('add-to-cart-global')) {\n setPids = [];\n\n $('.product-detail').each(function () {\n if (!$(this).hasClass('product-set-detail')) {\n setPids.push({\n pid: $(this).find('.product-id').text(),\n qty: $(this).find('.quantity-select').val(),\n options: getOptions($(this))\n });\n }\n });\n pidsObj = JSON.stringify(setPids);\n }\n\n pid = getPidValue($(this));\n\n var $productContainer = $(this).closest('.product-detail');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.quick-view-dialog').find('.product-detail');\n }\n\n addToCartUrl = getAddToCartUrl();\n\n var form = {\n pid: pid,\n pidsObj: pidsObj,\n childProducts: getChildProducts(),\n quantity: getQuantitySelected($(this))\n };\n\n if (!$('.bundle-item').length) {\n form.options = getOptions($productContainer);\n }\n\n $(this).trigger('updateAddToCartFormData', form);\n if (addToCartUrl) {\n $.ajax({\n url: addToCartUrl,\n method: 'POST',\n data: form,\n success: function (data) {\n handlePostCartAdd(data);\n $('body').trigger('product:afterAddToCart', data);\n $.spinner().stop();\n miniCartReportingUrl(data.reportingURL);\n },\n error: function () {\n $.spinner().stop();\n }\n });\n }\n });\n },\n selectBonusProduct: function () {\n $(document).on('click', '.select-bonus-product', function () {\n var $choiceOfBonusProduct = $(this).parents('.choice-of-bonus-product');\n var pid = $(this).data('pid');\n var maxPids = $('.choose-bonus-product-dialog').data('total-qty');\n var submittedQty = parseInt($choiceOfBonusProduct.find('.bonus-quantity-select').val(), 10);\n var totalQty = 0;\n $.each($('#chooseBonusProductModal .selected-bonus-products .selected-pid'), function () {\n totalQty += $(this).data('qty');\n });\n totalQty += submittedQty;\n var optionID = $choiceOfBonusProduct.find('.product-option').data('option-id');\n var valueId = $choiceOfBonusProduct.find('.options-select option:selected').data('valueId');\n if (totalQty <= maxPids) {\n var selectedBonusProductHtml = ''\n + '
      '\n + '
      '\n + $choiceOfBonusProduct.find('.product-name').html()\n + '
      '\n + '
      '\n + '
      '\n ;\n $('#chooseBonusProductModal .selected-bonus-products').append(selectedBonusProductHtml);\n $('.pre-cart-products').html(totalQty);\n $('.selected-bonus-products .bonus-summary').removeClass('alert-danger');\n } else {\n $('.selected-bonus-products .bonus-summary').addClass('alert-danger');\n }\n });\n },\n removeBonusProduct: function () {\n $(document).on('click', '.selected-pid', function () {\n $(this).remove();\n var $selected = $('#chooseBonusProductModal .selected-bonus-products .selected-pid');\n var count = 0;\n if ($selected.length) {\n $selected.each(function () {\n count += parseInt($(this).data('qty'), 10);\n });\n }\n\n $('.pre-cart-products').html(count);\n $('.selected-bonus-products .bonus-summary').removeClass('alert-danger');\n });\n },\n enableBonusProductSelection: function () {\n $('body').on('bonusproduct:updateSelectButton', function (e, response) {\n $('button.select-bonus-product', response.$productContainer).attr('disabled',\n (!response.product.readyToOrder || !response.product.available));\n var pid = response.product.id;\n $('button.select-bonus-product', response.$productContainer).data('pid', pid);\n });\n },\n showMoreBonusProducts: function () {\n $(document).on('click', '.show-more-bonus-products', function () {\n var url = $(this).data('url');\n $('.modal-content').spinner().start();\n $.ajax({\n url: url,\n method: 'GET',\n success: function (html) {\n var parsedHtml = parseHtml(html);\n $('.modal-body').append(parsedHtml.body);\n $('.show-more-bonus-products:first').remove();\n $('.modal-content').spinner().stop();\n },\n error: function () {\n $('.modal-content').spinner().stop();\n }\n });\n });\n },\n addBonusProductsToCart: function () {\n $(document).on('click', '.add-bonus-products', function () {\n var $readyToOrderBonusProducts = $('.choose-bonus-product-dialog .selected-pid');\n var queryString = '?pids=';\n var url = $('.choose-bonus-product-dialog').data('addtocarturl');\n var pidsObject = {\n bonusProducts: []\n };\n\n $.each($readyToOrderBonusProducts, function () {\n var qtyOption =\n parseInt($(this)\n .data('qty'), 10);\n\n var option = null;\n if (qtyOption > 0) {\n if ($(this).data('optionid') && $(this).data('option-selected-value')) {\n option = {};\n option.optionId = $(this).data('optionid');\n option.productId = $(this).data('pid');\n option.selectedValueId = $(this).data('option-selected-value');\n }\n pidsObject.bonusProducts.push({\n pid: $(this).data('pid'),\n qty: qtyOption,\n options: [option]\n });\n pidsObject.totalQty = parseInt($('.pre-cart-products').html(), 10);\n }\n });\n queryString += JSON.stringify(pidsObject);\n queryString = queryString + '&uuid=' + $('.choose-bonus-product-dialog').data('uuid');\n queryString = queryString + '&pliuuid=' + $('.choose-bonus-product-dialog').data('pliuuid');\n $.spinner().start();\n $.ajax({\n url: url + queryString,\n method: 'POST',\n success: function (data) {\n $.spinner().stop();\n if (data.error) {\n $('#chooseBonusProductModal').modal('hide');\n if ($('.add-to-cart-messages').length === 0) {\n $('body').append('
      ');\n }\n $('.add-to-cart-messages').append(\n '
      '\n + data.errorMessage + '
      '\n );\n setTimeout(function () {\n $('.add-to-basket-alert').remove();\n }, 3000);\n } else {\n $('.configure-bonus-product-attributes').html(data);\n $('.bonus-products-step2').removeClass('hidden-xl-down');\n $('#chooseBonusProductModal').modal('hide');\n\n if ($('.add-to-cart-messages').length === 0) {\n $('body').append('
      ');\n }\n $('.minicart-quantity').html(data.totalQty);\n $('.add-to-cart-messages').append(\n '
      '\n + data.msgSuccess + '
      '\n );\n setTimeout(function () {\n $('.add-to-basket-alert').remove();\n if ($('.cart-page').length) {\n location.reload();\n }\n }, 1500);\n }\n },\n error: function () {\n $.spinner().stop();\n }\n });\n });\n },\n\n getPidValue: getPidValue,\n getQuantitySelected: getQuantitySelected,\n miniCartReportingUrl: miniCartReportingUrl\n};\n","'use strict';\n\nmodule.exports = function (include) {\n if (typeof include === 'function') {\n include();\n } else if (typeof include === 'object') {\n Object.keys(include).forEach(function (key) {\n if (typeof include[key] === 'function') {\n include[key]();\n }\n });\n }\n};\n","'use strict';\n\nvar base = require('../product/base');\nvar focusHelper = require('base/components/focus');\nvar qtyInput = require('../components/quantityInput');\n\n/**\n * Show restricted products message.\n */\nfunction showRestrictedProductMessage() {\n $('.restricted-products').show();\n}\n/**\n * Hide restricted product message\n */\nfunction hideRestrictedProductsMessage() {\n $('.restricted-products').hide();\n}\n\n/**\n * Update restricted product message for each line item in cart.\n *\n * @param {Object} data - server response that should have restricted product UUID's array and restricted product\n * message for line item.\n */\nfunction updateProductRestrictions(data) {\n $('.card').each(function () {\n $(this).find('.restricted-message').remove();\n });\n hideRestrictedProductsMessage();\n for (var i = 0; i < data.restrictedProducts.length; i++) {\n var restrictedProductUUID = data.restrictedProducts[i];\n var pli = $('.uuid-' + restrictedProductUUID);\n var pliParentAttr = pli.find('.product-edit');\n var $restrictedProduct = $('
      ' + data.restrictedProductMessage + '
      ');\n $(pliParentAttr).prepend($restrictedProduct);\n }\n\n if ($('.success-ship-msg').length) {\n $('.success-ship-msg').remove();\n }\n\n if (data.restrictedProducts.length > 0) {\n showRestrictedProductMessage();\n $('.checkout-btn').addClass('disabled');\n $('.restricted-products-cart-message .content-asset').removeClass('d-none');\n }\n\n if (data.restrictedProducts.length === 0) {\n $('.restricted-products-cart-message .content-asset').addClass('d-none');\n $('.restricted-products-cart-message').append('
      ' + data.noRestrictedProductsMessage + '
      ');\n $('.checkout-btn').removeClass('disabled');\n }\n}\n\n/**\n * re-renders the order totals and the number of items in the cart\n * @param {Object} message - Error message to display\n */\nfunction createErrorNotification(message) {\n var errorHtml = '
      ' +\n '' + message + '
      ';\n\n $('.cart-error').empty();\n $('.cart-error').append(errorHtml);\n}\n\n/**\n * Initialize shipping restriction functionality\n */\nfunction shippingStatesInit() {\n $('.checkout-btn').addClass('disabled');\n\n $('.shipping-restrictions').on('click', function (e) {\n e.preventDefault();\n e.stopPropagation();\n var shippingRestrictionsUrl = $(this).attr('href');\n var stateCode = $('.shipping-states').find('select[name$=_stateCode],input[name$=_stateCode]').val();\n\n if (stateCode === '') {\n return;\n }\n\n $('.card').spinner().start();\n $('body').trigger('cart:beforeUpdate');\n\n if (stateCode) {\n $(this).parents('.card').spinner().start();\n $.ajax({\n url: shippingRestrictionsUrl + '?stateCode=' + stateCode,\n type: 'get',\n dataType: 'json',\n success: function (data) {\n updateProductRestrictions(data);\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n }\n });\n}\n\n/**\n * appends params to a url\n * @param {string} url - Original url\n * @param {Object} params - Parameters to append\n * @returns {string} result url with appended parameters\n */\nfunction appendToUrl(url, params) {\n var newUrl = url;\n newUrl += (newUrl.indexOf('?') !== -1 ? '&' : '?') + Object.keys(params).map(function (key) {\n return key + '=' + encodeURIComponent(params[key]);\n }).join('&');\n\n return newUrl;\n}\n\n/**\n * Checks whether the basket is valid. if invalid displays error message and disables\n * checkout button\n * @param {Object} data - AJAX response from the server\n */\nfunction validateBasket(data) {\n if (data.valid.error) {\n if (data.valid.message) {\n var errorHtml = '
      ' +\n '' + data.valid.message + '
      ';\n\n $('.cart-error').append(errorHtml);\n } else {\n $('.cart').empty().append('
      ' +\n '
      ' +\n '

      ' + data.resources.emptyCartMsg + '

      ' +\n '
      ' +\n '
      '\n );\n $('.number-of-items').empty().append(data.resources.numberOfItems);\n $('.minicart-quantity').empty().append(data.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.resources.minicartCountOfItems,\n title: data.resources.minicartCountOfItems\n });\n $('.minicart .popover').empty();\n $('.minicart .popover').removeClass('show');\n }\n\n $('.checkout-btn').addClass('disabled');\n } else {\n $('.shipping-restrictions').click();\n }\n}\n\n/**\n * re-renders the order totals and the number of items in the cart\n * @param {Object} data - AJAX response from the server\n */\nfunction updateCartTotals(data) {\n $('.number-of-items').empty().append(data.resources.numberOfItems);\n $('.shipping-cost').empty().append(data.totals.totalShippingCost);\n $('.tax-total').empty().append(data.totals.totalTax);\n $('.grand-total').empty().append(data.totals.grandTotal);\n $('.sub-total').empty().append(data.totals.subTotal);\n $('.minicart-quantity').empty().append(data.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.resources.minicartCountOfItems,\n title: data.resources.minicartCountOfItems\n });\n if (data.totals.orderLevelDiscountTotal.value > 0) {\n $('.order-discount').removeClass('hide-order-discount');\n $('.order-discount-total').empty()\n .append('- ' + data.totals.orderLevelDiscountTotal.formatted);\n } else {\n $('.order-discount').addClass('hide-order-discount');\n }\n\n if (data.totals.shippingLevelDiscountTotal.value > 0) {\n $('.shipping-discount').removeClass('hide-shipping-discount');\n $('.shipping-discount-total').empty().append('- ' +\n data.totals.shippingLevelDiscountTotal.formatted);\n } else {\n $('.shipping-discount').addClass('hide-shipping-discount');\n }\n\n data.items.forEach(function (item) {\n if (data.totals.orderLevelDiscountTotal.value > 0) {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n }\n if (item.renderedPromotions) {\n $('.item-' + item.UUID).empty().append(item.renderedPromotions);\n } else {\n $('.item-' + item.UUID).empty();\n }\n $('.uuid-' + item.UUID + ' .unit-price').empty().append(item.renderedPrice);\n $('.line-item-price-' + item.UUID + ' .unit-price').empty().append(item.renderedPrice);\n $('.item-total-' + item.UUID).empty().append(item.priceTotal.renderedPrice);\n });\n}\n\n/**\n * re-renders the approaching discount messages\n * @param {Object} approachingDiscounts - updated approaching discounts for the cart\n */\nfunction updateApproachingDiscounts(approachingDiscounts) {\n var html = '';\n $('.approaching-discounts').empty();\n if (approachingDiscounts.length > 0) {\n approachingDiscounts.forEach(function (item) {\n html += '
      '\n + item.discountMsg + '
      ';\n });\n }\n $('.approaching-discounts').append(html);\n}\n\n/**\n * Updates the availability of a product line item\n * @param {Object} data - AJAX response from the server\n * @param {string} uuid - The uuid of the product line item to update\n */\nfunction updateAvailability(data, uuid) {\n var lineItem;\n var messages = '';\n\n for (var i = 0; i < data.items.length; i++) {\n if (data.items[i].UUID === uuid) {\n lineItem = data.items[i];\n break;\n }\n }\n\n if (lineItem != null) {\n $('.availability-' + lineItem.UUID).empty();\n\n if (lineItem.availability) {\n if (lineItem.availability.messages) {\n lineItem.availability.messages.forEach(function (message) {\n messages += '

      ' + message + '

      ';\n });\n }\n\n if (lineItem.availability.inStockDate) {\n messages += '

      '\n + lineItem.availability.inStockDate\n + '

      ';\n }\n }\n\n $('.availability-' + lineItem.UUID).html(messages);\n }\n}\n\n/**\n * Finds an element in the array that matches search parameter\n * @param {array} array - array of items to search\n * @param {function} match - function that takes an element and returns a boolean indicating if the match is made\n * @returns {Object|null} - returns an element of the array that matched the query.\n */\nfunction findItem(array, match) { // eslint-disable-line no-unused-vars\n for (var i = 0, l = array.length; i < l; i++) {\n if (match.call(this, array[i])) {\n return array[i];\n }\n }\n return null;\n}\n\n/**\n * Updates details of a product line item\n * @param {Object} data - AJAX response from the server\n * @param {string} uuid - The uuid of the product line item to update\n */\nfunction updateProductDetails(data, uuid) {\n $('.card.product-info.uuid-' + uuid).replaceWith(data.renderedTemplate);\n}\n\n/**\n * Generates the modal window on the first call.\n *\n */\nfunction getModalHtmlElement() {\n if ($('#editProductModal').length !== 0) {\n $('#editProductModal').remove();\n }\n var htmlString = ''\n + '
      '\n + ''\n + '
      '\n + ''\n + '
      '\n + '
      '\n + ' '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      ';\n $('body').append(htmlString);\n}\n\n/**\n * Parses the html for a modal window\n * @param {string} html - representing the body and footer of the modal window\n *\n * @return {Object} - Object with properties body and footer.\n */\nfunction parseHtml(html) {\n var $html = $('
      ').append($.parseHTML(html));\n\n var body = $html.find('.product-quickview');\n var footer = $html.find('.modal-footer').children();\n\n return { body: body, footer: footer };\n}\n\n/**\n * replaces the content in the modal window for product variation to be edited.\n * @param {string} editProductUrl - url to be used to retrieve a new product model\n */\nfunction fillModalElement(editProductUrl) {\n $('.modal-body').spinner().start();\n $.ajax({\n url: editProductUrl,\n method: 'GET',\n dataType: 'json',\n success: function (data) {\n var parsedHtml = parseHtml(data.renderedTemplate);\n $('#editProductModal .modal-body').empty();\n $('#editProductModal .modal-body').html(parsedHtml.body);\n $('#editProductModal .modal-footer').html(parsedHtml.footer);\n $('#editProductModal .modal-header .close .sr-only').text(data.closeButtonText);\n $('#editProductModal .enter-message').text(data.enterDialogMessage);\n $('#editProductModal .customShopInput').text(data.customShopFirearmDetails);\n $('#editProductModal').modal('show');\n $('body').trigger('editproductmodal:ready');\n $.spinner().stop();\n qtyInput.qvInit();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * replace content of modal\n * @param {string} actionUrl - url to be used to remove product\n * @param {string} productID - pid\n * @param {string} productName - product name\n * @param {string} uuid - uuid\n */\nfunction confirmDelete(actionUrl, productID, productName, uuid) {\n var $deleteConfirmBtn = $('.cart-delete-confirmation-btn');\n var $productToRemoveSpan = $('.product-to-remove');\n\n $deleteConfirmBtn.data('pid', productID);\n $deleteConfirmBtn.data('action', actionUrl);\n $deleteConfirmBtn.data('uuid', uuid);\n\n $productToRemoveSpan.empty().append(productName);\n}\n\n/**\n * Retrieve product options\n * @param {jQuery} el - current DOM element\n */\nfunction removeSavedItem(el) {\n $.ajax({\n url: el,\n method: 'GET',\n success: function (response) {\n if (!response.error) {\n $('.saved-products-list').html(response);\n $.spinner().stop();\n }\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * replace content of modal\n * @param {jQuery} el - current DOM element\n */\nfunction removeLineItem(el) {\n var productID = $(el).data('pid');\n var url = $(el).data('action');\n var uuid = $(el).data('uuid');\n var urlParams = {\n pid: productID,\n uuid: uuid\n };\n\n url = appendToUrl(url, urlParams);\n\n $('body > .modal-backdrop').remove();\n\n $.spinner().start();\n $('body').trigger('cart:beforeUpdate');\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n success: function (data) {\n if (data.basket.items.length === 0) {\n $('.cart').empty().append('
      ' +\n '
      ' +\n '

      ' + data.basket.resources.emptyCartMsg + '

      ' +\n '
      ' +\n '
      '\n );\n $('.number-of-items').empty().append(data.basket.resources.numberOfItems);\n $('.minicart-quantity').empty().append(data.basket.numItems);\n $('.minicart-link').attr({\n 'aria-label': data.basket.resources.minicartCountOfItems,\n title: data.basket.resources.minicartCountOfItems\n });\n $('.minicart .popover').empty();\n $('.minicart .popover').removeClass('show');\n $('body').removeClass('modal-open');\n $('html').removeClass('veiled');\n } else {\n if (data.toBeDeletedUUIDs && data.toBeDeletedUUIDs.length > 0) {\n for (var i = 0; i < data.toBeDeletedUUIDs.length; i++) {\n $('.uuid-' + data.toBeDeletedUUIDs[i]).remove();\n }\n }\n $('.uuid-' + uuid).remove();\n if (!data.basket.hasBonusProduct) {\n $('.bonus-product').remove();\n }\n $('.coupons-and-promos').empty().append(data.basket.totals.discountsHtml);\n updateCartTotals(data.basket);\n updateApproachingDiscounts(data.basket.approachingDiscounts);\n $('body').trigger('setShippingMethodSelection', data.basket);\n validateBasket(data.basket);\n }\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n\n if (data.shouldRefresh) {\n location.reload();\n }\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n}\n\n/**\n * Initialize shipping restriction functionality\n */\nfunction reinitPromo() {\n $('.promo-code-form').submit(function (e) {\n e.preventDefault();\n $.spinner().start();\n $('.coupon-missing-error').hide();\n $('.coupon-error-message').empty();\n if (!$('.coupon-code-field').val()) {\n $('.promo-code-form .form-control').addClass('is-invalid');\n $('.promo-code-form .form-control').attr('aria-describedby', 'missingCouponCode');\n $('.coupon-missing-error').show();\n $.spinner().stop();\n return false;\n }\n var $form = $('.promo-code-form');\n $('.promo-code-form .form-control').removeClass('is-invalid');\n $('.coupon-error-message').empty();\n $('body').trigger('promotion:beforeUpdate');\n\n $.ajax({\n url: $form.attr('action'),\n type: 'GET',\n dataType: 'json',\n data: $form.serialize(),\n success: function (data) {\n if (data.error) {\n $('.promo-code-form .form-control').addClass('is-invalid');\n $('.promo-code-form .form-control').attr('aria-describedby', 'invalidCouponCode');\n $('.coupon-error-message').empty().append(data.errorMessage);\n $('body').trigger('promotion:error', data);\n } else {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n $('body').trigger('promotion:success', data);\n }\n $('.coupon-code-field').val('');\n $.spinner().stop();\n },\n error: function (err) {\n $('body').trigger('promotion:error', err);\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.errorMessage);\n $.spinner().stop();\n }\n }\n });\n return false;\n });\n}\n\n/**\n * Retrieve product options\n * @param {jQuery} el - current DOM element\n */\nfunction updateProductLineItems(el) {\n var cartShowUrl = $(el).data('action-cart');\n var removeSavedItemUrl = $(el).data('action-remove');\n $.ajax({\n url: cartShowUrl,\n method: 'GET',\n success: function (data) {\n $('.container.cart.cart-page').html(data);\n removeSavedItem(removeSavedItemUrl);\n reinitPromo();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * Retrieve product options\n *\n * @param {jQuery} $productContainer - DOM element for current product\n * @return {string} - Product options and their selected values\n */\nfunction getOptions($productContainer) {\n var options = $productContainer\n .find('.product-option')\n .map(function () {\n var $elOption = $(this).find('.options-select');\n var urlValue = $elOption.val();\n var selectedValueId = $elOption.find('option[value=\"' + urlValue + '\"]')\n .data('value-id');\n return {\n optionId: $(this).data('option-id'),\n selectedValueId: selectedValueId\n };\n }).toArray();\n\n return JSON.stringify(options);\n}\n\n/**\n * Retrieves the bundle product item ID's for the Controller to replace bundle master product\n * items with their selected variants\n *\n * @return {string[]} - List of selected bundle product item ID's\n */\nfunction getChildProducts() {\n var childProducts = [];\n $('.bundle-item').each(function () {\n childProducts.push({\n pid: $(this).find('.product-id').text(),\n quantity: parseInt($(this).find('label.quantity').data('quantity'), 10)\n });\n });\n\n return childProducts.length ? JSON.stringify(childProducts) : [];\n}\n\n/**\n * Updates the Mini-Cart quantity value after the customer has pressed the \"Add to Cart\" button\n * @param {string} response - ajax response from clicking the add to cart button\n */\nfunction handleSavedProductStatus(response) {\n $('.minicart').trigger('count:update', response);\n var messageType = response.error ? 'alert-danger' : 'alert-success';\n if ($('.add-to-cart-messages').length === 0) {\n $('body').append(\n '
      '\n );\n }\n\n $('.add-to-cart-messages').append(\n '
      '\n + response.message\n + '
      '\n );\n\n setTimeout(function () {\n $('.add-to-basket-alert').remove();\n }, 5000);\n}\n\nmodule.exports = function () {\n qtyInput.cartInit();\n qtyInput.bonusInit();\n\n $('body').on('click', '.remove-product', function (e) {\n e.preventDefault();\n\n var actionUrl = $(this).data('action');\n var productID = $(this).data('pid');\n var productName = $(this).data('name');\n var uuid = $(this).data('uuid');\n confirmDelete(actionUrl, productID, productName, uuid);\n });\n\n $('body').on('afterRemoveFromCart', function (e, data) {\n e.preventDefault();\n confirmDelete(data.actionUrl, data.productID, data.productName, data.uuid);\n });\n\n $('.optional-promo').click(function (e) {\n e.preventDefault();\n $('.promo-code-form').toggle();\n });\n\n $('body').on('click', '.cart-delete-confirmation-btn', function (e) {\n e.preventDefault();\n removeLineItem($(this));\n });\n\n $('body').on('change', '.quantity-form input[type=\"text\"]', function () {\n var $qtyInput = $(this).closest('.quantity-form').find('input[type=\"text\"]');\n var preSelectQty = $qtyInput.data('pre-select-qty');\n var quantity = $qtyInput.val();\n var productID = $qtyInput.data('pid');\n var url = $qtyInput.data('action');\n var uuid = $qtyInput.data('uuid');\n\n var urlParams = {\n pid: productID,\n quantity: quantity,\n uuid: uuid\n };\n url = appendToUrl(url, urlParams);\n\n $(this).parents('.card').spinner().start();\n $('body').trigger('cart:beforeUpdate');\n\n $('body').trigger('cart:beforeUpdate');\n\n $.ajax({\n url: url,\n type: 'get',\n context: this,\n dataType: 'json',\n success: function (data) {\n $('.quantity[data-uuid=\"' + uuid + '\"]').val(quantity);\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n updateAvailability(data, uuid);\n validateBasket(data);\n $(this).data('pre-select-qty', quantity);\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n if (($(this).parents('.product-info').hasClass('bonus-product-line-item') && $('.cart-page').length) || data.shouldRefresh) {\n location.reload();\n }\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $(this).val(parseInt(preSelectQty, 10));\n $.spinner().stop();\n }\n }\n });\n });\n\n $('body').on('change', '.quantity-form-SFL input[type=\"text\"]', function (e) {\n e.preventDefault();\n var $qtyInput = $(this).closest('.quantity-form-SFL').find('input[type=\"text\"]');\n var preSelectQty = $qtyInput.data('pre-select-qty');\n var quantity = $qtyInput.val();\n var productID = $qtyInput.data('pid');\n var url = $qtyInput.data('action');\n var uuid = $qtyInput.data('uuid');\n\n var urlParams = {\n pid: productID,\n quantity: quantity,\n uuid: uuid\n };\n url = appendToUrl(url, urlParams);\n\n $(this).parents('.card').spinner().start();\n $('body').trigger('cart:beforeUpdate');\n\n $('body').trigger('cart:beforeUpdate');\n\n $.ajax({\n url: url,\n type: 'get',\n context: this,\n dataType: 'json',\n success: function (data) {\n $('.quantity[data-uuid=\"' + uuid + '\"]').val(quantity);\n $(this).data('pre-select-qty', quantity);\n data.savedItems.forEach(function (savedItem) {\n $('.line-item-total-price-amount.item-total-' + savedItem.UUID).empty().append(savedItem.priceTotal.price);\n });\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n if (($(this).parents('.product-info').hasClass('bonus-product-line-item') && $('.cart-page').length) || data.shouldRefresh) {\n location.reload();\n }\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.msg);\n $(this).val(parseInt(preSelectQty, 10));\n $.spinner().stop();\n }\n }\n });\n });\n\n $('.shippingMethods').change(function () {\n var url = $(this).attr('data-actionUrl');\n var urlParams = {\n methodID: $(this).find(':selected').attr('data-shipping-id')\n };\n // url = appendToUrl(url, urlParams);\n\n $('.totals').spinner().start();\n $('body').trigger('cart:beforeShippingMethodSelected');\n $.ajax({\n url: url,\n type: 'post',\n dataType: 'json',\n data: urlParams,\n success: function (data) {\n if (data.error) {\n window.location.href = data.redirectUrl;\n } else {\n $('.coupons-and-promos').empty().append(data.totals.discountsHtml);\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n }\n $('body').trigger('cart:shippingMethodSelected', data);\n $.spinner().stop();\n },\n error: function (err) {\n if (err.redirectUrl) {\n window.location.href = err.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n });\n\n $('body').on('click', '.remove-coupon', function (e) {\n e.preventDefault();\n\n var couponCode = $(this).data('code');\n var uuid = $(this).data('uuid');\n var $deleteConfirmBtn = $('.delete-coupon-confirmation-btn');\n var $productToRemoveSpan = $('.coupon-to-remove');\n\n $deleteConfirmBtn.data('uuid', uuid);\n $deleteConfirmBtn.data('code', couponCode);\n\n $productToRemoveSpan.empty().append(couponCode);\n });\n\n $('body').on('click', '.delete-coupon-confirmation-btn', function (e) {\n e.preventDefault();\n\n var url = $(this).data('action');\n var uuid = $(this).data('uuid');\n var couponCode = $(this).data('code');\n var urlParams = {\n code: couponCode,\n uuid: uuid\n };\n\n url = appendToUrl(url, urlParams);\n\n $('body > .modal-backdrop').remove();\n $('body').trigger('promotion:beforeUpdate');\n\n $.spinner().start();\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n success: function (data) {\n $('.coupon-uuid-' + uuid).remove();\n updateCartTotals(data);\n updateApproachingDiscounts(data.approachingDiscounts);\n validateBasket(data);\n $.spinner().stop();\n $('body').trigger('promotion:success', data);\n },\n error: function (err) {\n $('body').trigger('promotion:error', err);\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $.spinner().stop();\n }\n }\n });\n });\n $('body').on('click', '.cart-page .bonus-product-button', function () {\n $.spinner().start();\n $(this).addClass('launched-modal');\n $.ajax({\n url: $(this).data('url'),\n method: 'GET',\n dataType: 'json',\n success: function (data) {\n base.methods.editBonusProducts(data);\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n });\n\n $('body').on('hidden.bs.modal', '#chooseBonusProductModal', function () {\n $('#chooseBonusProductModal').remove();\n $('.modal-backdrop').remove();\n $('body').removeClass('modal-open');\n\n if ($('.cart-page').length) {\n $('.launched-modal .btn-outline-primary').trigger('focus');\n $('.launched-modal').removeClass('launched-modal');\n } else {\n $('.product-detail .add-to-cart').focus();\n }\n });\n\n $('body').on('click', '.cart-page .product-edit .edit, .cart-page .bundle-edit .edit, .cart-page .product-line-item-options .edit', function (e) {\n e.preventDefault();\n\n var editProductUrl = $(this).attr('href');\n getModalHtmlElement();\n fillModalElement(editProductUrl);\n });\n\n $('body').on('shown.bs.modal', '#editProductModal', function () {\n $('#editProductModal').siblings().attr('aria-hidden', 'true');\n $('#editProductModal .close').focus();\n });\n\n $('body').on('hidden.bs.modal', '#editProductModal', function () {\n $('#editProductModal').siblings().attr('aria-hidden', 'false');\n });\n\n $('body').on('keydown', '#editProductModal', function (e) {\n var focusParams = {\n event: e,\n containerSelector: '#editProductModal',\n firstElementSelector: '.close',\n lastElementSelector: '.update-cart-product-global',\n nextToLastElementSelector: '.modal-footer .quantity-select'\n };\n focusHelper.setTabNextFocus(focusParams);\n });\n\n $('body').on('product:updateAddToCart', function (e, response) {\n // update global add to cart (single products, bundles)\n var dialog = $(response.$productContainer)\n .closest('.quick-view-dialog');\n\n $('.update-cart-product-global', dialog).attr('disabled',\n !$('.global-availability', dialog).data('ready-to-order')\n || !$('.global-availability', dialog).data('available')\n );\n });\n\n $('body').on('product:updateAvailability', function (e, response) {\n // bundle individual products\n $('.product-availability', response.$productContainer)\n .data('ready-to-order', response.product.readyToOrder)\n .data('available', response.product.available)\n .find('.availability-msg')\n .empty()\n .html(response.message);\n\n\n var dialog = $(response.$productContainer)\n .closest('.quick-view-dialog');\n\n if ($('.product-availability', dialog).length) {\n // bundle all products\n var allAvailable = $('.product-availability', dialog).toArray()\n .every(function (item) { return $(item).data('available'); });\n\n var allReady = $('.product-availability', dialog).toArray()\n .every(function (item) { return $(item).data('ready-to-order'); });\n\n $('.global-availability', dialog)\n .data('ready-to-order', allReady)\n .data('available', allAvailable);\n\n $('.global-availability .availability-msg', dialog).empty()\n .html(allReady ? response.message : response.resources.info_selectforstock);\n } else {\n // single product\n $('.global-availability', dialog)\n .data('ready-to-order', response.product.readyToOrder)\n .data('available', response.product.available)\n .find('.availability-msg')\n .empty()\n .html(response.message);\n }\n });\n\n $('body').on('product:afterAttributeSelect', function (e, response) {\n if ($('.modal.show .product-quickview .bundle-items').length) {\n $('.modal.show').find(response.container).data('pid', response.data.product.id);\n $('.modal.show').find(response.container).find('.product-id').text(response.data.product.id);\n } else {\n $('.modal.show .product-quickview').data('pid', response.data.product.id);\n }\n });\n\n $('body').on('change', '.quantity-select', function () {\n var selectedQuantity = $(this).val();\n $('.modal.show .update-cart-url').data('selected-quantity', selectedQuantity);\n });\n\n $('body').on('change', '.options-select', function () {\n var selectedOptionValueId = $(this).children('option:selected').data('value-id');\n $('.modal.show .update-cart-url').data('selected-option', selectedOptionValueId);\n });\n\n $('body').on('click', '.update-cart-product-global', function (e) {\n e.preventDefault();\n var updateProductUrl = $(this).closest('.cart-and-ipay').find('.update-cart-url').val();\n var selectedQuantity = $(this).closest('.row-qty-addtocart').find('.quantity-form input[type=\"text\"]').val();\n var selectedOptionValueId = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('selected-option');\n var uuid = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('uuid');\n\n var form = {\n uuid: uuid,\n pid: base.getPidValue($(this)),\n quantity: selectedQuantity,\n selectedOptionValueId: selectedOptionValueId\n };\n\n $(this).parents('.card').spinner().start();\n $('body').trigger('cart:beforeUpdate');\n if (updateProductUrl) {\n $.ajax({\n url: updateProductUrl,\n type: 'post',\n context: this,\n data: form,\n dataType: 'json',\n success: function (data) {\n $('#editProductModal').modal('hide');\n $('.coupons-and-promos').empty().append(data.cartModel.totals.discountsHtml);\n updateCartTotals(data.cartModel);\n updateApproachingDiscounts(data.cartModel.approachingDiscounts);\n updateAvailability(data.cartModel, uuid);\n updateProductDetails(data, uuid);\n\n if (data.uuidToBeDeleted) {\n $('.uuid-' + data.uuidToBeDeleted).remove();\n }\n\n validateBasket(data.cartModel);\n\n $('body').trigger('cart:update', data);\n\n $.spinner().stop();\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.errorMessage);\n $('#editProductModal').modal('hide');\n $.spinner().stop();\n }\n }\n });\n }\n });\n\n $('body').on('click', '.update-cart-product-SFL', function (e) {\n e.preventDefault();\n var updateProductUrl = $(this).closest('.cart-and-ipay').find('.update-cart-url').val();\n var selectedQuantity = $(this).closest('.row-qty-addtocart').find('.quantity-form input[type=\"text\"]').val();\n var selectedOptionValueId = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('selected-option');\n var uuid = $(this).closest('.cart-and-ipay').find('.update-cart-url').data('uuid');\n var pid = $(this).closest('.modal-content').find('.product-quickview').data('pid');\n\n var form = {\n uuid: uuid,\n pid: pid,\n quantity: selectedQuantity,\n selectedOptionValueId: selectedOptionValueId\n };\n\n $(this).parents('.card').spinner().start();\n $('body').trigger('cart:beforeUpdate');\n if (updateProductUrl) {\n $.ajax({\n url: updateProductUrl,\n type: 'post',\n context: this,\n data: form,\n success: function (data) {\n $('#editProductModal').modal('hide');\n $('.saved-products-list').html(data);\n $('body').trigger('cart:update', data);\n $.spinner().stop();\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n } else {\n createErrorNotification(err.responseJSON.msg);\n $('#editProductModal').modal('hide');\n $.spinner().stop();\n }\n }\n });\n }\n });\n\n $('body').on('product:beforeAttributeSelect', function () {\n // Unslick the existing images to prepare them for direct js manipulation\n base.carouselUnslick();\n base.carouselUnslickBonus();\n });\n\n $('body').on('product:afterAttributeSelect', function () {\n base.carouselInit();\n base.carouselInitQuickview();\n });\n\n $('body').on('shown.bs.modal', '#editProductModal, #quickViewModal, #chooseBonusProductModal', function () {\n base.carouselInitBonus();\n base.carouselInitQuickview();\n });\n\n $('body').on('setShippingMethodSelection', function (e, basket) {\n var shippingMethodSelected = $('#shippingMethods option:selected');\n shippingMethodSelected.attr('data-shipping-id', basket.shipments[0].shippingMethodId);\n var shippingMethodName = basket.shipments[0].shippingMethods[0].displayName;\n if (basket.shipments[0].shippingMethods[0].estimatedArrivalTime && basket.shipments[0].shippingMethods[0].estimatedArrivalTime.length > 0) {\n shippingMethodName += ' ( ' + basket.shipments[0].shippingMethods[0].estimatedArrivalTime + ' )';\n }\n shippingMethodSelected.text(shippingMethodName);\n });\n\n $('body').on('click', '.add-to-save-for-later', function (e) {\n e.preventDefault();\n var url = $(this).attr('href');\n var quantity = $(this).closest('.product-info').find('.quantity-form input[type=\"text\"]').val();\n url = url + '&quantity=' + quantity;\n var element = $(this);\n $.spinner().stop();\n $.ajax({\n url: url,\n method: 'GET',\n success: function (response) {\n if (!response.error) {\n if (response.LoginRequired) {\n createErrorNotification(response.message);\n } else {\n $('.saved-products-list').html(response);\n var status = {};\n status.message = $('.status-message').data('message');\n status.error = $('.status-message').data('error');\n handleSavedProductStatus(status);\n removeLineItem(element);\n }\n }\n },\n error: function () {\n $.spinner().stop();\n }\n });\n });\n\n $(document).on('click', 'a.saved-list-ATC', function (e) {\n e.preventDefault();\n var addToCartUrl;\n var pid;\n var pidsObj;\n var setPids;\n\n $('body').trigger('product:beforeAddToCart', this);\n\n if ($('.set-items').length && $(this).hasClass('add-to-cart-global')) {\n setPids = [];\n\n $('.product-detail').each(function () {\n if (!$(this).hasClass('product-set-detail')) {\n setPids.push({\n pid: $(this).find('.product-id').text(),\n qty: $(this).find('.quantity-select').val(),\n options: getOptions($(this))\n });\n }\n });\n pidsObj = JSON.stringify(setPids);\n }\n\n pid = $(this).data('pid');\n\n var $productContainer = $(this).closest('.product-detail');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.quick-view-dialog').find('.product-detail');\n }\n var customInput = $productContainer.find('.customShopInput').val();\n var quantity = $(this).parents('.product-info').find('.quantity-select').val();\n addToCartUrl = $('.add-to-cart-url').val();\n var form = {\n pid: pid,\n pidsObj: pidsObj,\n childProducts: getChildProducts(),\n quantity: quantity,\n customInput: customInput\n };\n\n if (!$('.bundle-item').length) {\n form.options = getOptions($productContainer);\n }\n\n $(this).trigger('updateAddToCartFormData', form);\n $.spinner().start();\n var element = $(this);\n if (addToCartUrl) {\n $.ajax({\n url: addToCartUrl,\n method: 'POST',\n data: form,\n success: function (data) {\n $('body').trigger('product:afterAddToCart', data);\n base.handlePostCartAdd(data);\n if (data.error) {\n $.spinner().stop();\n return;\n }\n $('.coupons-and-promos').empty().append(data.cart.totals.discountsHtml);\n updateCartTotals(data.cart);\n updateApproachingDiscounts(data.cart.approachingDiscounts);\n updateProductLineItems(element);\n $('body').trigger('setShippingMethodSelection', data.cart);\n validateBasket(data.cart);\n },\n error: function () {\n $.spinner().stop();\n }\n });\n }\n });\n\n $('body').on('click', '.saved-list-remove', function (e) {\n e.preventDefault();\n var url = $(this).attr('href');\n $.spinner().start();\n removeSavedItem(url);\n });\n\n shippingStatesInit();\n reinitPromo();\n\n base.selectAttribute();\n base.colorAttribute();\n base.removeBonusProduct();\n base.selectBonusProduct();\n base.enableBonusProductSelection();\n base.showMoreBonusProducts();\n base.addBonusProductsToCart();\n base.focusChooseBonusProductModal();\n base.trapChooseBonusProductModalFocus();\n base.onClosingChooseBonusProductModal();\n};\n","'use strict';\nvar slickConfigs = require('../config/slickConfigs');\nvar util = require('../util/utils');\nvar imagesPerRow = $('#category-tiles-per-row').data('category-items') || 6;\nvar glide = require('org/config/glide');\nvar glideConfigs = require('site/config/glideConfigs');\n\n\n/**\n * Init globally reusable carousels\n */\n\nmodule.exports = {\n heroCarousels: function () {\n // prevents collision with content asset js\n // if ($('.hero-caro').length && !$('#hero-carousel').length) {\n // $('.hero-caro').on('init reInit', function (event, slick) {\n // slickConfigs.reinitPagination(event, slick, 1);\n // }).slick(slickConfigs.hero);\n // util.smartResize(function () {\n // slickConfigs.reinitPagination(event, $('.hero-caro').slick('getSlick'), 1);\n // });\n // }\n },\n blogProductTileCarousels: function () {\n // const $this = this;\n if ($('.product-tile-caro, .promotedProducts').length) {\n glide.sameClassMultiCarousel('.product-tile-caro', glideConfigs.recommendation);\n // many product tile sliders can exist on a page; loop through them to init individually\n // $('.product-tile-caro, .promotedProducts').not('.slick-initialized').each(function () {\n // var $slider = $(this);\n // var defaultConfig = slickConfigs.productTiles;\n // if ($slider.hasClass('recommendation')) {\n // defaultConfig.slide = '.grid-tile';\n // }\n\n // // each slider target only that slider's controls\n // var $sliderControls = $slider.next('.product-carousel-arrows');\n\n // // update slider config to use unique controls and prevent multiple sliders on same page from affecting each other\n // slickConfigs.productTiles.appendDots = $sliderControls.find('.slick-dots-custom');\n // slickConfigs.productTiles.prevArrow = $sliderControls.find('.slick-arrows-custom-prev');\n // slickConfigs.productTiles.nextArrow = $sliderControls.find('.slick-arrows-custom-next');\n\n // $slider.on('init reInit', function (event, slick) {\n // slickConfigs.reinitPagination(event, slick, 4);\n // }).slick(defaultConfig);\n\n // // watch DOM for PI recommendations content\n // if ($slider.hasClass('recommendation')) {\n // $this.observeEinsteinCarousels();\n // }\n\n // util.smartResize(function () {\n // slickConfigs.reinitPagination(event, $slider.slick('getSlick'), 4);\n // });\n // });\n }\n\n if ($('.product-tile-caro-mobile-hero').length) {\n $('.product-tile-caro-mobile-hero').not('.slick-initialized').each(function (idx) {\n var $slider = $(this);\n var sliderID = 'mobileTileSlider' + idx;\n var defaultConfig = slickConfigs.productTilesHeroMobile;\n $slider.attr('id', sliderID);\n var $sliderControls = $(`#${sliderID}`).next('.product-carousel-arrows');\n slickConfigs.productTilesHeroMobile.appendDots = $sliderControls.find('.slick-dots-custom');\n slickConfigs.productTilesHeroMobile.prevArrow = $sliderControls.find('.slick-arrows-custom-prev');\n slickConfigs.productTilesHeroMobile.nextArrow = $sliderControls.find('.slick-arrows-custom-next');\n\n $slider.on('init reInit', function (event, slick) {\n slickConfigs.reinitPagination(event, slick, 4);\n }).slick(slickConfigs.productTilesHeroMobile);\n\n util.smartResize(function () {\n slickConfigs.reinitPagination(event, $slider.slick('getSlick'), 4);\n });\n\n slickConfigs.productTilesHeroMobile = defaultConfig;\n });\n }\n },\n observeEinsteinCarousels: function () {\n // declare constants\n const einsteinSelector = '[id^=\"cq_recomm_slot\"]';\n const einsteinElements = document.querySelectorAll(einsteinSelector);\n const $this = this;\n // callback\n const mutationCallback = (mutationList, observer) => {\n if (mutationList.length) {\n observer.disconnect();\n $this.blogProductTileCarousels();\n }\n };\n // if we have einstein elements\n if (einsteinElements.length > 0) {\n einsteinElements.forEach((el) => {\n var observer = new MutationObserver(mutationCallback);\n observer.observe(el, { childList: true });\n });\n }\n },\n categorySubFolder: function () {\n $('.category-subfolder-tile-caro').on('init reInit', function (event, slick) {\n slickConfigs.reinitPagination(event, slick, imagesPerRow);\n }).slick(slickConfigs.categorySubFolderTiles);\n util.smartResize(function () {\n slickConfigs.reinitPagination(event, $('.category-subfolder-tile-caro').slick('getSlick'), imagesPerRow);\n });\n },\n productRelatedContent: function () {\n $('.product-related-content-tile-caro').slick(slickConfigs.productRelatedContent);\n },\n caroMobileOnly: function () {\n var $caroMobileOnly = $('.caro-mobile-only');\n var unslick = function () {\n if ($caroMobileOnly.hasClass('slick-initialized')) {\n $caroMobileOnly.slick('unslick');\n }\n };\n if ($caroMobileOnly.length) {\n util.smartResize(function () {\n if (util.mediaBreakpointDown('md')) {\n $caroMobileOnly.not('.slick-initialized').slick(slickConfigs.caroMobileOnly);\n } else {\n unslick();\n }\n });\n }\n }\n};\n","'use strict';\n\nvar baseClientSideValidation = require('base/components/clientSideValidation');\n\nvar exportClientSideValidation = $.extend(baseClientSideValidation, {\n invalid: function () {\n $('form input, form select, form textarea').on('invalid', function (e) {\n e.preventDefault();\n this.setCustomValidity('');\n if (!this.validity.valid) {\n var validationMessage = this.validationMessage;\n $(this).addClass('is-invalid');\n $(this).closest('.form-group').addClass('is-invalid');\n $(this).siblings('label').addClass('is-invalid');\n if (this.validity.patternMismatch && $(this).data('pattern-mismatch')) {\n validationMessage = $(this).data('pattern-mismatch');\n }\n if ((this.validity.rangeOverflow || this.validity.rangeUnderflow)\n && $(this).data('range-error')) {\n validationMessage = $(this).data('range-error');\n }\n if ((this.validity.tooLong || this.validity.tooShort)\n && $(this).data('range-error')) {\n validationMessage = $(this).data('range-error');\n }\n if (this.validity.valueMissing && $(this).data('missing-error')) {\n validationMessage = $(this).data('missing-error');\n }\n $(this).parents('.form-group').find('.invalid-feedback')\n .text(validationMessage);\n }\n });\n },\n clearErrorsOnChange: function () {\n $(':input, .form-control').on('change input', function () {\n if ($(this).hasClass('is-invalid')) {\n if ($(this).hasClass('addressType')) {\n $(document).find('input.addressType').removeClass('is-invalid');\n }\n $(this).removeClass('is-invalid');\n $(this).closest('.form-group').removeClass('is-invalid');\n $(this).closest('label').removeClass('is-invalid');\n $(this).closest('.invalid-feedback').remove();\n }\n });\n }\n});\n\nmodule.exports = exportClientSideValidation;\n","'use strict';\n\n/**\n * Renders a modal window that will track the users consenting to accepting site tracking policy\n */\nfunction showConsentModal() {\n if (!$('.tracking-consent').data('caonline')) {\n return;\n }\n var urlContent = $('.tracking-consent').data('url');\n var urlAccept = $('.tracking-consent').data('accept');\n var htmlString = '
      ';\n\n $('body').append(htmlString);\n\n $.ajax({\n url: urlContent,\n type: 'get',\n dataType: 'html',\n success: function (response) {\n $('#consent-tracking').html(response);\n $('#consent-tracking').find('.accept-cookie-btn').attr('data-url', urlAccept);\n\n $('.accept-cookie-btn').on('click', function (e) {\n e.preventDefault();\n var url = $(this).data('url');\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n success: function () {\n $('#consent-tracking').remove();\n $.spinner().stop();\n },\n error: function () {\n $('#consent-tracking').remove();\n $.spinner().stop();\n }\n });\n });\n },\n error: function () {\n $('#consent-tracking').remove();\n }\n });\n}\n\n/**\n * Function to set consent tracking for OneTrust banner\n * @param {boolean} trackingConsent - Tracking Consent.\n */\nfunction oneTrustCookieConsent(trackingConsent) {\n var urlAccept = $('.tracking-consent').data('accept');\n var urlReject = $('.tracking-consent').data('reject');\n var consentURL = (trackingConsent ? urlAccept : urlReject);\n\n try {\n $.ajax({\n url: consentURL,\n type: 'get',\n dataType: 'json'\n });\n } catch (e) { // eslint-disable-line\n }\n}\n\nmodule.exports = function () {\n if ($('.consented').length === 0 && $('.tracking-consent').hasClass('api-true')) {\n showConsentModal();\n }\n\n if ($('.tracking-consent').hasClass('api-true')) {\n $('.tracking-consent').click(function () {\n showConsentModal();\n });\n }\n\n $('body').on('click', '#onetrust-reject-all-handler', function (e) {\n e.preventDefault();\n oneTrustCookieConsent(false);\n });\n\n $('body').on('click', '#onetrust-accept-btn-handler', function (e) {\n e.preventDefault();\n oneTrustCookieConsent(true);\n });\n};\n","'use strict';\n\n/**\n * einsteinObserver\n * Initialize an observer for adding a callback to Einstein DOM element injection.\n * @param {string} selector - the selector to observe for changes\n * @param {function} fn - the callback for when changes are found\n */\nvar einsteinObserver = function (selector, fn) {\n var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;\n var einsteinSelector = '[id^=\"cq_recomm_slot\"]';\n var elements = window.document.querySelectorAll(selector);\n var check = function (mutationList, observer) {\n if (mutationList.length) {\n // if new child detected, disconnect observer and call callback\n observer.disconnect();\n fn.call();\n }\n };\n\n var initializeElement = function (element) {\n var einsteinElement = element.querySelector(einsteinSelector);\n if (einsteinElement === null) {\n // not an einstein recommender slot\n if (element.childElementCount > 0) {\n // if child, call callback immediately\n fn.call();\n }\n } else if (einsteinElement.childElementCount > 0) {\n // if child, call callback immediately\n fn.call();\n } else {\n // if no child, attach observer and wait for child to be added\n var observer = new MutationObserver(check);\n observer.observe(einsteinElement, { childList: true });\n }\n };\n\n // check each element for child\n for (var i = 0; i < elements.length; i++) {\n initializeElement(elements[i]);\n }\n};\n\nmodule.exports = { einsteinObserver: einsteinObserver };\n","'use strict';\n\nvar carousels = require('./carousels');\n\nvar einsteinObserver = require('./einstein').einsteinObserver;\nvar baseProductTileCarousels = carousels.blogProductTileCarousels;\n\nmodule.exports = function () {\n einsteinObserver('.einstein-slot', baseProductTileCarousels);\n};\n","'use strict';\n\nvar util = require('lyonscg/util/utils.js');\nvar formValidation = require('base/components/formValidation');\n\n/**\n * Generates the modal window on the first call.\n *\n */\nfunction getModalHtmlElement() {\n if ($('.modal-email-signup').length !== 0) {\n $('.modal-email-signup').remove();\n }\n\n var htmlString = ''\n + '
      '\n + '
      '\n + ''\n + '
      '\n + '
      '\n + ' '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      ';\n $('body').append(htmlString);\n}\n\n/**\n * Parses the html for a modal window\n * @param {string} html - representing the body and footer of the modal window\n *\n * @return {Object} - Object with properties body and footer.\n */\nfunction parseHtml(html) {\n var $html = $('
      ').append($.parseHTML(html));\n\n var body = $html.find('.email-modal');\n\n return { body: body };\n}\n\n/**\n * Replaces the content in the modal window for email updates and offers modal.\n * @param {string} modalUrl - url to be used to retrieve a email optin model\n */\nfunction fillModalElement(modalUrl) {\n $('.modal-content').spinner().start();\n\n $.ajax({\n url: modalUrl,\n method: 'GET',\n dataType: 'html',\n success: function (html) {\n var parsedHtml = parseHtml(html);\n\n parsedHtml.body.removeClass('email-modal');\n $('.modal-email-signup .modal-body').empty();\n $('.modal-email-signup .modal-body').html(parsedHtml.body);\n $('.modal-email-signup .modal-footer').html(parsedHtml.footer);\n $('.modal-email-signup').modal('show');\n $('#email-signup-form-email').val($('#newsletterEmailAddress').val());\n $('#newsletterEmailAddress').val('');\n $('#email-signup-form-firstname').focus();\n $('.modal-content').spinner().stop();\n },\n error: function () {\n $('.modal-content').spinner().stop();\n }\n });\n}\n\nmodule.exports = function () {\n $('#newsletterSignup').on('submit', function (e) {\n e.preventDefault();\n\n var modalUrl = $(this).attr('action');\n getModalHtmlElement();\n fillModalElement(modalUrl);\n });\n\n $(document).on('change blur', '#email-signup-form-email, #email-signup-form-emailconfirm', function () {\n var $confirmInput = $('#email-signup-form-emailconfirm');\n if ($('#email-signup-form-email').val() !== $('#email-signup-form-emailconfirm').val()) {\n $('.newsletterSubscribeButton').attr('disabled', 'disabled');\n\n // show error\n $confirmInput.addClass('is-invalid');\n $confirmInput.closest('.form-group').addClass('is-invalid');\n $confirmInput.parents('.form-group').find('.invalid-feedback')\n .text($confirmInput.data('pattern-mismatch'));\n } else {\n // remove error\n $confirmInput.removeClass('is-invalid');\n $confirmInput.closest('.form-group').removeClass('is-invalid');\n $confirmInput.parents('.form-group').find('.invalid-feedback')\n .text('');\n }\n });\n\n $(document).on('change', 'form.newsletterSubscribe input', function () {\n var emptyInput = false;\n $('form.newsletterSubscribe input:not(\"#email-signup-form-firstname\"):not(\"#email-signup-form-lastname\")').each(function () {\n if ($(this).val() === '') {\n emptyInput = true;\n }\n });\n if (emptyInput) {\n $('.newsletterSubscribeButton').attr('disabled', 'disabled');\n } else if ($('form.newsletterSubscribe #email-signup-form-termsconditions').is(':checked')) {\n if ($('#email-signup-form-email').val() === $('#email-signup-form-emailconfirm').val()) {\n $('.newsletterSubscribeButton').removeAttr('disabled');\n }\n } else {\n $('.newsletterSubscribeButton').attr('disabled', 'disabled');\n }\n });\n\n $(document).on('submit', 'form.newsletterSubscribe', function (e) {\n var form = $(this);\n e.preventDefault();\n var url = form.attr('action');\n $('.modal-email-signup').spinner().start();\n $.ajax({\n url: url,\n type: 'post',\n dataType: 'json',\n data: form.serialize(),\n success: function (data) {\n $('.modal-email-signup').spinner().stop();\n if (!data.success) {\n if (data.errorMessage) {\n $('form.newsletterSubscribe').html('
      ' + data.errorMessage + '
      ');\n } else {\n formValidation(form, data);\n }\n } else {\n var successHTML = '


      ';\n successHTML += '
      ' + data.successMsg + '
      ';\n\n $('form.newsletterSubscribe').html(successHTML);\n }\n },\n error: function (err) {\n if (err.responseJSON.redirectUrl) {\n window.location.href = err.responseJSON.redirectUrl;\n }\n\n $('.modal-email-signup').spinner().stop();\n }\n });\n return false;\n });\n\n $('.footer-item h1').on('click', function () {\n if (util.mediaBreakpointDown('md')) {\n $(this).parents('.footer-item').toggleClass('active');\n }\n });\n};\n","'use strict';\n/* Unified Modal creation and handling functionality */\nvar modal = require('org/util/modal');\n\n/**\n * Function to observe the consent popUp is hide so we can shift down the geoLocation popup.\n */\nfunction observeConsentPopup() {\n var observer = new MutationObserver(function (mutations) {\n mutations.forEach(function (mutationRecord) { // eslint-disable-line no-unused-vars\n $('#geo-redirect-modal .modal-dialog .modal-content').css('bottom', '0');\n if (window.matchMedia('(max-width: 767px)').matches) {\n $('#geo-redirect-modal .modal-dialog .modal-content').css({ position: 'absolute', bottom: '65px' });\n }\n });\n });\n var target = document.getElementById('onetrust-banner-sdk');\n observer.observe(target, { attributes: true, attributeFilter: ['style'] });\n}\n\n\nmodule.exports = function () {\n $('document').ready(function () {\n var url = $('#geoPopupURL').data('url');\n\n $.ajax({\n url: url,\n type: 'get',\n dataType: 'json',\n data: {},\n success: function (response) {\n if (response.displayModal) {\n modal.create(response.renderedTemplate);\n }\n }\n });\n });\n $('body').on('click', '#geoPopupCloseBtn', function () {\n $('#geo-redirect-modal').modal('hide');\n }).on('change', '#geoCountrySelector', function () {\n let reqURL = $('#reqURL').val();\n let selectedCountryCode = $(this).val();\n let redirectURL;\n $.ajax({\n url: reqURL,\n type: 'post',\n dataType: 'json',\n data: { selectedCountryCode: selectedCountryCode },\n success: (res) => {\n if (res.redirectURL !== '') {\n redirectURL = res.redirectURL;\n window.location.replace(redirectURL);\n } else {\n $('#geo-redirect-modal').modal('hide');\n window.location.reload();\n }\n }\n });\n });\n\n $(window).on('load', function () {\n setTimeout(function () {\n try {\n observeConsentPopup();\n } catch (e) { // eslint-disable-line\n }\n }, 500);\n });\n};\n","'use strict';\n\nvar keyboardAccessibility = require('base/components/keyboardAccessibility');\n\nvar clearSelection = function (element) {\n $(element).closest('.dropdown, .dropdown-item').children('.dropdown-menu, .menu-list').children('.top-category')\n .detach();\n $(element).closest('.dropdown.show, .dropdown-item.show').children('.nav-link').attr('aria-expanded', 'false');\n $(element).closest('.dropdown.show, .dropdown-item.show, .menu-caret.show').siblings('.hide').removeClass('hide');\n $(element).closest('.menu-list').siblings().find('dropdown-item')\n .removeClass('hide');\n $(element).closest('.menu-column').siblings().find('.dropdown-item')\n .removeClass('hide');\n\n if ($(element).closest('.dropdown-menu')\n .children('.nav-menu')\n .children('.close-menu')\n .children('.back')\n .get(0) === $(element).get(0)) {\n $(element).closest('.navbar').children('.nav-menu').removeClass('hide');\n } else {\n $(element).closest('.dropdown-menu').children('.nav-menu').removeClass('hide');\n }\n\n $(element).closest('.dropdown.show, .dropdown-item.show, .menu-caret.show').removeClass('show');\n $(element).closest('li').detach();\n};\n\nmodule.exports = function () {\n var isDesktop = function (element) {\n return $(element).parents('.menu-toggleable-left').css('position') !== 'fixed';\n };\n\n $('.header-banner .close').on('click', function () {\n $('.header-banner').addClass('hide');\n });\n\n keyboardAccessibility('.main-menu .nav-link, .main-menu .dropdown-link',\n {\n 40: function (menuItem) { // down\n if (menuItem.hasClass('nav-item')) { // top level\n $('.navbar-nav .show').removeClass('show')\n .children('.dropdown-menu')\n .removeClass('show');\n menuItem.addClass('show').children('.dropdown-menu').addClass('show');\n $(this).attr('aria-expanded', 'true');\n menuItem.find('ul > li > a')\n .first()\n .focus();\n } else {\n menuItem.removeClass('show').children('.dropdown-menu').removeClass('show');\n $(this).attr('aria-expanded', 'false');\n menuItem.next().children().first().focus();\n }\n },\n 39: function (menuItem) { // right\n if (menuItem.hasClass('nav-item')) { // top level\n menuItem.removeClass('show').children('.dropdown-menu').removeClass('show');\n $(this).attr('aria-expanded', 'false');\n menuItem.next().children().first().focus();\n } else if (menuItem.hasClass('dropdown')) {\n menuItem.addClass('show').children('.dropdown-menu').addClass('show');\n $(this).attr('aria-expanded', 'true');\n menuItem.find('ul > li > a')\n .first()\n .focus();\n }\n },\n 38: function (menuItem) { // up\n if (menuItem.hasClass('nav-item')) { // top level\n menuItem.removeClass('show').children('.dropdown-menu').removeClass('show');\n $(this).attr('aria-expanded', 'false');\n } else if (menuItem.prev().length === 0) {\n menuItem.parent().parent().removeClass('show')\n .children('.nav-link')\n .attr('aria-expanded', 'false');\n menuItem.parent().parent().children().first()\n .focus();\n } else {\n menuItem.prev().children().first().focus();\n }\n },\n 37: function (menuItem) { // left\n if (menuItem.hasClass('nav-item')) { // top level\n menuItem.removeClass('show').children('.dropdown-menu').removeClass('show');\n $(this).attr('aria-expanded', 'false');\n menuItem.prev().children().first().focus();\n } else {\n menuItem.closest('.show').removeClass('show')\n .closest('li.show').removeClass('show')\n .children()\n .first()\n .focus()\n .attr('aria-expanded', 'false');\n }\n },\n 27: function (menuItem) { // escape\n var parentMenu = menuItem.hasClass('show')\n ? menuItem\n : menuItem.closest('li.show');\n parentMenu.children('.show').removeClass('show');\n parentMenu.removeClass('show').children('.nav-link')\n .attr('aria-expanded', 'false');\n parentMenu.children().first().focus();\n }\n },\n function () {\n return $(this).parent();\n }\n );\n\n $('.dropdown:not(.disabled) [data-toggle=\"dropdown\"], a.has-sub-cat')\n .on('click', function (e) {\n if (!isDesktop(this)) {\n $('.modal-background').show();\n // copy parent element into current UL\n var li = $('');\n var link = $(this).clone().removeClass('dropdown-toggle')\n .removeAttr('data-toggle')\n .removeAttr('aria-expanded')\n .attr('aria-haspopup', 'false');\n li.append(link);\n\n var closeMenu = $('
    2. ');\n closeMenu.append(li);\n closeMenu.append($('.close-menu').first().clone());\n\n if ($(this).hasClass('has-sub-cat')) {\n if (!$(this).closest('.dropdown-item').find('> .menu-list').children('.nav-menu').length) {\n $(this).closest('.dropdown-item').find('> .menu-list')\n .prepend(closeMenu);\n }\n\n $(this).closest('.dropdown-item').find('.hide').removeClass('hide');\n $(this).closest('.dropdown-item').find('.show').removeClass('show');\n\n // copy navigation menu into view\n $(this).closest('.dropdown-item').addClass('show');\n $(this).closest('.dropdown-item').siblings()\n .removeClass('show')\n .addClass('hide');\n $(this).closest('.menu-list').siblings().find('.dropdown-item')\n .removeClass('show')\n .addClass('hide');\n $(this).closest('.menu-column').siblings().find('.dropdown-item')\n .removeClass('show')\n .addClass('hide');\n $(this).closest('.dropdown-menu').children('.nav-menu').addClass('hide');\n } else {\n if (!$(this).parent().children('.dropdown-menu').children('.nav-menu').length) {\n $(this).parent().children('.dropdown-menu')\n .prepend(closeMenu);\n }\n\n $(this).parent().find('.hide').removeClass('hide');\n $(this).parent().find('.show').removeClass('show');\n\n // copy navigation menu into view\n $(this).parent().addClass('show');\n $(this).parent().siblings(':not(.content-item, .account-mobile-nav)')\n .removeClass('show')\n .addClass('hide');\n $(this).closest('.navbar').children('.nav-menu')\n .removeClass('show')\n .addClass('hide');\n }\n\n $(this).attr('aria-expanded', 'true');\n e.preventDefault();\n }\n })\n .on('mouseenter', function () {\n if (isDesktop(this)) {\n var eventElement = this;\n $('.navbar-nav > li').each(function () {\n if (!$.contains(this, eventElement)) {\n $(this).find('.show').each(function () {\n clearSelection(this);\n });\n if ($(this).hasClass('show')) {\n $(this).removeClass('show');\n $(this).children('ul.dropdown-menu').removeClass('show');\n $(this).children('.nav-link').attr('aria-expanded', 'false');\n }\n }\n });\n // need to close all the dropdowns that are not direct parent of current dropdown\n $(this).parent().addClass('show');\n $(this).siblings('.dropdown-menu').addClass('show');\n $(this).siblings('.menu-caret').addClass('show');\n $(this).attr('aria-expanded', 'true');\n }\n })\n .parent()\n .on('mouseleave', function () {\n if (isDesktop(this)) {\n $(this).removeClass('show');\n $(this).children('.dropdown-menu').removeClass('show');\n $(this).children('.menu-caret').removeClass('show');\n $(this).children('.nav-link').attr('aria-expanded', 'false');\n }\n });\n\n $('.navbar>.close-menu>.close-button').on('click', function (e) {\n e.preventDefault();\n $('.menu-toggleable-left').removeClass('in');\n $('.modal-background').hide();\n });\n\n $('.navbar-nav').on('click', '.back', function (e) {\n e.preventDefault();\n clearSelection(this);\n });\n\n $('.navbar-nav').on('click', '.close-button', function (e) {\n e.preventDefault();\n $('.navbar-nav').find('.top-category').detach();\n $('.navbar-nav').find('.nav-menu').detach();\n $('.navbar-nav').find('.show').removeClass('show');\n $('.navbar').find('.hide').removeClass('hide');\n $('.menu-toggleable-left').removeClass('in');\n $('.modal-background').hide();\n });\n\n $('.navbar-toggler').click(function (e) {\n e.preventDefault();\n $('.main-menu').toggleClass('in');\n $('.modal-background').show();\n });\n\n $('.header-search-svg').click(function (e) {\n e.preventDefault();\n $('.search-mobile ').toggle();\n });\n\n keyboardAccessibility('.navbar-header .user',\n {\n 40: function ($popover) { // down\n if ($popover.children('a').first().is(':focus')) {\n $popover.children('a').first().next().focus();\n } else {\n $popover.children('a').first().focus();\n }\n },\n 38: function ($popover) { // up\n if ($popover.children('a').first().is(':focus')) {\n $(this).focus();\n $popover.removeClass('show');\n } else {\n $popover.children('a').first().focus();\n }\n },\n 27: function ($popover) { // escape\n $(this).focus();\n $popover.removeClass('show');\n },\n 9: function ($popover) { // tab\n $popover.removeClass('show');\n }\n },\n function () {\n var $popover = $('.user .popover');\n if (!($popover.hasClass('show'))) {\n $popover.addClass('show');\n }\n return $popover;\n }\n );\n\n $('.navbar-header .user').on('mouseenter focusin', function () {\n if ($('.navbar-header .user .popover').length > 0) {\n $('.navbar-header .user .popover').addClass('show');\n }\n });\n\n $('.navbar-header .user').on('mouseleave', function () {\n $('.navbar-header .user .popover').removeClass('show');\n });\n\n $('.send-emailVerification-mail').on('click', function () {\n var url = $(this).attr('data-actionUrl');\n\n $('body').spinner().start();\n\n $.ajax({\n url: url,\n type: 'GET',\n success: function (data) {\n if (!data.error) {\n $('.send-emailVerification-mail').addClass('hidden');\n $('.sent-emailVerification-mail').removeClass('hidden');\n }\n $.spinner().stop();\n }\n });\n });\n\n $('.header-search-svg').on('click', ()=>{\n if ($('.search-mobile')) {\n if ($('.search-mobile').hasClass('d-none')) {\n $('.search-mobile').removeClass('d-none');\n } else {\n $('.search-mobile').addClass('d-none');\n }\n }\n });\n};\n","'use strict';\n\nvar wishPlugin = require('wishlists/components/miniCart');\nvar cart = require('../cart/cart');\nvar qtyInput = require('../components/quantityInput');\nvar utils = require('lyonscg/util/utils');\n\nvar updateMiniCart = true;\n\nmodule.exports = function () {\n cart();\n wishPlugin.moveToWishlist();\n\n $('.minicart').on('count:update', function (event, count) {\n if (count && $.isNumeric(count.quantityTotal)) {\n $('.minicart .minicart-quantity').text(count.quantityTotal);\n $('.minicart .minicart-link').attr({\n 'aria-label': count.minicartCountOfItems,\n title: count.minicartCountOfItems\n });\n }\n });\n\n $('.minicart').on('mouseenter focusin touchstart', function () {\n if (window.innerWidth > utils.getViewports('md')) {\n if ($('.search:visible').length === 0) {\n return;\n }\n var url = $('.minicart').data('action-url');\n var count = parseInt($('.minicart .minicart-quantity').text(), 10);\n\n if (count !== 0 && $('.minicart .popover.show').length === 0) {\n if (!updateMiniCart) {\n $('.minicart .popover').addClass('show');\n return;\n }\n\n $('.minicart .popover').addClass('show');\n $('.minicart .popover').spinner().start();\n $.get(url, function (data) {\n $('.minicart .popover').empty();\n $('.minicart .popover').append(data);\n updateMiniCart = false;\n $.spinner().stop();\n qtyInput.miniCartInit();\n });\n }\n }\n });\n $('body').on('touchstart click', function (e) {\n if ($('.minicart').has(e.target).length <= 0) {\n $('.minicart .popover').removeClass('show');\n }\n });\n $('.minicart').on('mouseleave focusout', function (event) {\n if ((event.type === 'focusout' && $('.minicart').has(event.target).length > 0)\n || (event.type === 'mouseleave' && $(event.target).is('.minicart .quantity'))\n || $('body').hasClass('modal-open')) {\n event.stopPropagation();\n return;\n }\n $('.minicart .popover').removeClass('show');\n $.spinner().stop();\n });\n\n $('body').on('change', '.minicart .quantity', function () {\n if ($(this).parents('.bonus-product-line-item').length && $('.cart-page').length) {\n location.reload();\n }\n });\n $('body').on('product:afterAddToCart', function () {\n updateMiniCart = true;\n });\n $('body').on('cart:update', function () {\n updateMiniCart = true;\n });\n $('.checkout-btn').removeClass('disabled');\n};\n","'use strict';\n\nvar base = require('org/components/quantityInput');\n\n/**\n * @desc gets the max input value\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @returns {int} the max value\n */\nfunction getQtyMaxValue(qtyInput) {\n return parseInt(qtyInput.attr('max'), 0);\n}\n\n/**\n * @desc gets the min input value\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @returns {int} the min value\n */\nfunction getQtyMinValue(qtyInput) {\n return parseInt(qtyInput.attr('min'), 0);\n}\n\n/**\n * @desc warns user if desired quantity is close to product inventory\n * @param {string} pid - product id\n * @param {string} url - url of request\n * @param {int} updatedValue - qty value\n */\nfunction warnNearInventoryLimit(pid, url, updatedValue) {\n $.ajax({\n url: url,\n method: 'GET',\n data: {\n pid: pid,\n qty: updatedValue\n },\n dataType: 'json',\n success: (data) => {\n if (data.outsideInventory) {\n $('body').append('
      ' + data.msg + '
      ');\n setTimeout(function () {\n $('.qty-alert').remove();\n }, 5000);\n }\n }\n });\n}\n\n/**\n * @desc quantity increase\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @param {string} pid - product id\n */\nfunction qtyIncrease(qtyInput, pid) {\n var qtyMax = getQtyMaxValue(qtyInput);\n var updatedValue = parseInt(qtyInput.val(), 10) + 1;\n if (updatedValue <= qtyMax) {\n qtyInput.val(updatedValue);\n var url = qtyInput.parents('.quantity-form').data('action-url');\n if (url !== '') {\n warnNearInventoryLimit(pid, url, updatedValue);\n }\n }\n}\n\n/**\n * @desc quantity decrease\n * @param {jqueryObject} qtyInput - jquery selector of input field\n */\nfunction qtyDecrease(qtyInput) {\n var qtyMin = getQtyMinValue(qtyInput);\n var updatedValue = parseInt(qtyInput.val(), 10) - 1;\n if (updatedValue >= qtyMin) {\n qtyInput.val(updatedValue);\n }\n}\n\nvar exportQuantityInput = $.extend({}, base, {\n wishlistInit: function () {\n var $qtyInput = $('.wishlist-product-info .quantity-form input[type=\"text\"]');\n\n $('body').on('click', '.wishlist-product-info .quantity-form .plus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyIncrease($qtyInput);\n });\n\n $('body').on('click', '.wishlist-product-info .quantity-form .minus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyDecrease($qtyInput);\n });\n },\n\n bonusInit: function () {\n var $qtyInput = $('.bonus-quantity input[type=\"text\"]');\n\n $('body').on('click', '.bonus-quantity .plus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyIncrease($qtyInput);\n });\n\n $('body').on('click', '.bonus-quantity .minus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyDecrease($qtyInput);\n });\n }\n});\n\nmodule.exports = exportQuantityInput;\n","'use strict';\n\nvar debounce = require('lodash/debounce');\nvar endpoint = $('.suggestions-wrapper').data('url');\nvar minChars = 1;\nvar UP_KEY = 38;\nvar DOWN_KEY = 40;\nvar DIRECTION_DOWN = 1;\nvar DIRECTION_UP = -1;\n\n/**\n * Retrieves Suggestions element relative to scope\n *\n * @param {Object} scope - Search input field DOM element\n * @return {JQuery} - .suggestions-wrapper element\n */\nfunction getSuggestionsWrapper(scope) {\n return $(scope).siblings('.suggestions-wrapper');\n}\n\n/**\n * Determines whether DOM element is inside the .search-mobile class\n *\n * @param {Object} scope - DOM element, usually the input.search-field element\n * @return {boolean} - Whether DOM element is inside div.search-mobile\n */\nfunction isMobileSearch(scope) {\n return !!$(scope).closest('.search-mobile').length;\n}\n\n/**\n * Remove modal classes needed for mobile suggestions\n *\n */\nfunction clearModals() {\n $('body').removeClass('modal-open');\n $('header').siblings().attr('aria-hidden', 'false');\n $('.suggestions').removeClass('modal');\n}\n\n/**\n * Apply modal classes needed for mobile suggestions\n *\n * @param {Object} scope - Search input field DOM element\n */\nfunction applyModals(scope) {\n if (isMobileSearch(scope)) {\n $('body').addClass('modal-open');\n $('header').siblings().attr('aria-hidden', 'true');\n getSuggestionsWrapper(scope).find('.suggestions').addClass('modal');\n }\n}\n\n/**\n * Tear down Suggestions panel\n */\nfunction tearDownSuggestions() {\n $('input.search-field').val('');\n clearModals();\n $('.search-mobile .suggestions').unbind('scroll');\n $('.suggestions-wrapper').empty();\n}\n\n/**\n * Toggle search field icon from search to close and vice-versa\n *\n * @param {string} action - Action to toggle to\n */\nfunction toggleSuggestionsIcon(action) {\n var mobileSearchIcon = '.search-mobile button.';\n var iconSearch = 'fa-search';\n var iconSearchClose = 'fa-close';\n\n if (action === 'close') {\n $(mobileSearchIcon + iconSearch).removeClass(iconSearch).addClass(iconSearchClose).attr('type', 'button');\n } else {\n $(mobileSearchIcon + iconSearchClose).removeClass(iconSearchClose).addClass(iconSearch).attr('type', 'submit');\n }\n}\n\n/**\n * Determines whether the \"More Content Below\" icon should be displayed\n *\n * @param {Object} scope - DOM element, usually the input.search-field element\n */\nfunction handleMoreContentBelowIcon(scope) {\n if (($(scope).scrollTop() + $(scope).innerHeight()) >= $(scope)[0].scrollHeight) {\n $('.more-below').fadeOut();\n } else {\n $('.more-below').fadeIn();\n }\n}\n\n/**\n * Positions Suggestions panel on page\n *\n * @param {Object} scope - DOM element, usually the input.search-field element\n */\nfunction positionSuggestions(scope) {\n var outerHeight;\n var $scope;\n var $suggestions;\n var arrowHeight;\n var top;\n\n if (isMobileSearch(scope)) {\n $scope = $(scope);\n top = $scope.offset().top;\n outerHeight = $scope.outerHeight();\n $suggestions = getSuggestionsWrapper(scope).find('.suggestions');\n // make room for caret\n arrowHeight = $suggestions.siblings('.tooltip-arrow').outerHeight();\n $suggestions.css('top', top + outerHeight + arrowHeight);\n\n handleMoreContentBelowIcon(scope);\n\n // Unfortunately, we have to bind this dynamically, as the live scroll event was not\n // properly detecting dynamic suggestions element's scroll event\n $suggestions.scroll(function () {\n handleMoreContentBelowIcon(this);\n });\n }\n}\n\n/**\n * Process Ajax response for SearchServices-GetSuggestions\n *\n * @param {Object|string} response - Empty object literal if null response or string with rendered\n * suggestions template contents\n */\nfunction processResponse(response) {\n var $suggestionsWrapper = getSuggestionsWrapper(this).empty();\n\n $.spinner().stop();\n\n if (typeof (response) !== 'object') {\n $suggestionsWrapper.append(response).show();\n $(this).siblings('.reset-button').addClass('d-sm-block');\n positionSuggestions(this);\n\n if (isMobileSearch(this)) {\n toggleSuggestionsIcon('close');\n applyModals(this);\n }\n\n // Trigger screen reader by setting aria-describedby with the new suggestion message.\n var suggestionsList = $('.suggestions .item');\n if ($(suggestionsList).length) {\n $('input.search-field').attr('aria-describedby', 'search-result-count');\n $('div[name=\"simpleSearch\"] form').attr('aria-describedby', 'search-assistive-text');\n } else {\n $('input.search-field').removeAttr('aria-describedby');\n $('div[name=\"simpleSearch\"] form').removeAttr('aria-describedby');\n }\n } else {\n $suggestionsWrapper.hide();\n }\n}\n\n/**\n * Retrieve suggestions\n *\n * @param {Object} scope - Search field DOM element\n */\nfunction getSuggestions(scope) {\n if ($(scope).val().length >= minChars) {\n $.spinner().start();\n $.ajax({\n context: scope,\n url: endpoint + encodeURIComponent($(scope).val()),\n method: 'GET',\n success: processResponse,\n error: function () {\n $.spinner().stop();\n }\n });\n } else {\n toggleSuggestionsIcon('search');\n $(scope).siblings('.reset-button').removeClass('d-sm-block');\n clearModals();\n getSuggestionsWrapper(scope).empty();\n }\n}\n\n/**\n * Handle Search Suggestion Keyboard Arrow Keys\n *\n * @param {Integer} direction takes positive or negative number constant, DIRECTION_UP (-1) or DIRECTION_DOWN (+1)\n */\nfunction handleArrow(direction) {\n // get all li elements in the suggestions list\n var suggestionsList = $('.suggestions .item');\n if (suggestionsList.filter('.selected').length === 0) {\n suggestionsList.first().addClass('selected');\n $('input.search-field').each(function () {\n $(this).attr('aria-activedescendant', suggestionsList.first()[0].id);\n });\n } else {\n suggestionsList.each(function (index) {\n var idx = index + direction;\n if ($(this).hasClass('selected')) {\n $(this).removeClass('selected');\n $(this).removeAttr('aria-selected');\n if (suggestionsList.eq(idx).length !== 0) {\n suggestionsList.eq(idx).addClass('selected');\n suggestionsList.eq(idx).attr('aria-selected', true);\n $(this).removeProp('aria-selected');\n $('input.search-field').each(function () {\n $(this).attr('aria-activedescendant', suggestionsList.eq(idx)[0].id);\n });\n } else {\n suggestionsList.first().addClass('selected');\n suggestionsList.first().attr('aria-selected', true);\n $('input.search-field').each(function () {\n $(this).attr('aria-activedescendant', suggestionsList.first()[0].id);\n });\n }\n return false;\n }\n return true;\n });\n }\n}\n\nmodule.exports = function () {\n $('form[name=\"simpleSearch\"]').submit(function (e) {\n var suggestionsList = $('.suggestions .item');\n if (suggestionsList.filter('.selected').length !== 0) {\n e.preventDefault();\n suggestionsList.filter('.selected').find('a')[0].click();\n }\n });\n\n $('input.search-field').each(function () {\n /**\n * Use debounce to avoid making an Ajax call on every single key press by waiting a few\n * hundred milliseconds before making the request. Without debounce, the user sees the\n * browser blink with every key press.\n */\n var debounceSuggestions = debounce(getSuggestions, 300);\n $(this).on('keyup focus', function (e) {\n // Capture Down/Up Arrow Key Events\n switch (e.which) {\n case DOWN_KEY:\n handleArrow(DIRECTION_DOWN);\n e.preventDefault(); // prevent moving the cursor\n break;\n case UP_KEY:\n handleArrow(DIRECTION_UP);\n e.preventDefault(); // prevent moving the cursor\n break;\n default:\n debounceSuggestions(this, e);\n }\n });\n });\n\n $('body').on('click', function (e) {\n if (!$('.suggestions').has(e.target).length && !$(e.target).hasClass('search-field')) {\n $('.suggestions').hide();\n }\n });\n\n $('body').on('click touchend', '.search-mobile button.fa-close', function (e) {\n e.preventDefault();\n $('.suggestions').hide();\n toggleSuggestionsIcon('search');\n tearDownSuggestions();\n });\n\n $('.site-search .reset-button').on('click', function () {\n $(this).removeClass('d-sm-block');\n });\n\n $('.site-search .fa-search').click(function () {\n if ($('input.search-field').val() !== '') {\n $('input.search-field').attr('value', $('input.search-field').val());\n $('form[name=\"simpleSearch\"]').submit();\n }\n });\n};\n","'use strict';\n\n/**\n * Show a spinner inside a given element\n * @param {element} $target - Element to block by the veil and spinner.\n * Pass body to block the whole page.\n * @param {boolean} veiled - Whether or not the veil should show behind the spinner\n */\nfunction addSpinner($target, veiled) {\n var $veil = $('
      ');\n $veil.append('
      ');\n\n if (!veiled) {\n $veil.addClass('hide-veil');\n }\n\n if ($target.get(0).tagName === 'IMG') {\n $target.after($veil);\n $veil.css({ width: $target.width(), height: $target.height() });\n if ($target.parent().css('position') === 'static') {\n $target.parent().css('position', 'relative');\n }\n } else {\n $target.append($veil);\n if ($target.css('position') === 'static') {\n $target.parent().css('position', 'relative');\n $target.parent().addClass('veiled');\n }\n if ($target.get(0).tagName === 'BODY') {\n $veil.find('.spinner').css('position', 'fixed');\n }\n }\n\n $veil.on('click', function (e) {\n e.stopPropagation();\n });\n}\n\n/**\n * Remove existing spinner\n * @param {element} $veil - jQuery pointer to the veil element\n */\nfunction removeSpinner($veil) {\n if ($veil.parent().hasClass('veiled')) {\n $veil.parent().css('position', '');\n $veil.parent().removeClass('veiled');\n }\n $veil.off('click');\n $veil.remove();\n}\n\n// element level spinner:\n$.fn.spinner = function () {\n var $element = $(this);\n var Fn = function () {\n this.start = function (veiled) {\n if ($element.length) {\n var showVeiled = typeof veiled !== 'undefined' ? veiled : true;\n\n addSpinner($element, showVeiled);\n }\n };\n this.stop = function () {\n if ($element.length) {\n var $veil = $('.veil');\n removeSpinner($veil);\n }\n };\n };\n return new Fn();\n};\n\n// page-level spinner:\n$.spinner = function () {\n var Fn = function () {\n this.start = function (veiled) {\n var showVeiled = typeof veiled !== 'undefined' ? veiled : true;\n addSpinner($('body'), showVeiled);\n };\n this.stop = function () {\n removeSpinner($('.veil'));\n };\n };\n return new Fn();\n};\n","'use strict';\nvar utils = require('lyonscg/util/utils');\nvar imagesPerRow = $('#category-tiles-per-row').data('category-items') || 6;\nvar imagesPerRowMobile = $('#category-tiles-per-row').data('category-items-mobile') || 3;\nvar imageslideDesktop = $('.thumb-section').data('slide');\nvar imageslideMobile = $('.thumb-section').data('count');\n\n/**\n * Reusable slick carousel configurations\n * @example - $('.product-carousel').slick(slickConfigs.pdp)\n */\n\nmodule.exports = {\n hero: {\n autoplay: true,\n autoplaySpeed: 5000,\n easing: 'swing',\n infinite: true,\n speed: 800,\n dots: true,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1,\n appendDots: '.hero-slider-custom-navigation .slick-dots-custom',\n prevArrow: $('.hero-slider-custom-navigation .slick-arrows-custom-prev'),\n nextArrow: $('.hero-slider-custom-navigation .slick-arrows-custom-next')\n },\n\n categorySubFolderTiles: {\n infinite: false,\n speed: 300,\n dots: true,\n arrows: true,\n slidesToShow: imagesPerRow,\n slidesToScroll: 1,\n appendDots: '.category-subfolder-tile-caro-arrows .slick-dots-custom',\n prevArrow: $('.category-subfolder-tile-caro-arrows .slick-arrows-custom-prev'),\n nextArrow: $('.category-subfolder-tile-caro-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: imagesPerRow,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: imagesPerRow - 1,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: imagesPerRowMobile,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n productTiles: {\n infinite: false,\n speed: 300,\n dots: true,\n arrows: true,\n slidesToShow: 4,\n slidesToScroll: 1,\n appendDots: $('.product-carousel-arrows .slick-dots-custom'),\n prevArrow: $('.product-carousel-arrows .slick-arrows-custom-prev'),\n nextArrow: $('.product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 4,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n productRecommendationTile: {\n infinite: false,\n speed: 300,\n dots: true,\n arrows: true,\n variableWidth: false,\n slidesToShow: 2,\n slidesToScroll: 1,\n appendDots: $('.product-carousel-arrows .slick-dots-custom'),\n prevArrow: $('.product-carousel-arrows .slick-arrows-custom-prev'),\n nextArrow: $('.product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n productGiroRecommendationTile: {\n infinite: false,\n speed: 300,\n dots: true,\n arrows: true,\n slidesToShow: 3,\n slidesToScroll: 1,\n appendDots: $('.product-carousel-arrows .slick-dots-custom'),\n prevArrow: $('.product-carousel-arrows .slick-arrows-custom-prev'),\n nextArrow: $('.product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 3,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n\n // one tile on mobile for product carousel\n productTilesHeroMobile: {\n infinite: false,\n speed: 300,\n dots: true,\n arrows: true,\n slidesToShow: 4,\n slidesToScroll: 1,\n appendDots: $('.landing-page .product-carousel-arrows .slick-dots-custom'),\n prevArrow: $('.landing-page .product-carousel-arrows .slick-arrows-custom-pre'),\n nextArrow: $('.landing-page .product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 4,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: 1,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n QuickViewHorizontalSlick: {\n infinite: false,\n dots: false,\n arrows: true,\n slidesToShow: 6,\n slidesToScroll: 1,\n centerMode: false,\n focusOnSelect: true,\n accessibility: false,\n asNavFor: '.primary-images .pdp-slideCarousel'\n },\n\n pdpHorizontalSlick: {\n infinite: false,\n dots: false,\n arrows: true,\n slidesToShow: imageslideDesktop,\n slidesToScroll: 1,\n centerMode: false,\n focusOnSelect: true,\n accessibility: false,\n asNavFor: '.primary-images .pdp-slideCarousel',\n responsive: [\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: imageslideMobile,\n slidesToScroll: 1,\n arrows: false\n }\n }\n ]\n },\n\n pdpSlick: {\n infinite: false,\n dots: false,\n arrows: false,\n slidesToShow: 1,\n slidesToScroll: 1,\n centerMode: false,\n asNavFor: '.primary-images .scrollThumb'\n },\n\n // pdp: {\n // infinite: true,\n // speed: 400,\n // dots: true,\n // arrows: true,\n // slidesToShow: 1,\n // slidesToScroll: 1,\n // variableWidth: true,\n // centerMode: false,\n // appendDots: '.primary-images .product-carousel-arrows .slick-dots-custom',\n // prevArrow: $('.primary-images .product-carousel-arrows .slick-arrows-custom-prev'),\n // nextArrow: $('.primary-images .product-carousel-arrows .slick-arrows-custom-next')\n // },\n\n bonusModal: {\n infinite: true,\n speed: 400,\n dots: true,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1\n },\n\n quickView: {\n infinite: true,\n speed: 400,\n dots: true,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1,\n appendDots: '.quick-view-dialog .product-carousel-arrows .slick-dots-custom'\n },\n\n video: {\n speed: 400,\n infinite: false,\n dots: true,\n arrows: true,\n mobileFirst: true,\n slidesToShow: 1,\n slidesToScroll: 1,\n appendDots: '.pdp-videos .product-carousel-arrows .slick-dots-custom',\n prevArrow: $('.pdp-videos .product-carousel-arrows .slick-arrows-custom-prev'),\n nextArrow: $('.pdp-videos .product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 1,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: 1,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: 1,\n slidesToScroll: 1\n }\n }\n ]\n },\n\n tech: {\n speed: 400,\n dots: true,\n arrows: true,\n slidesToShow: 1,\n slidesToScroll: 1,\n slide: '.tech-slide',\n mobileFirst: true,\n appendDots: '#technology .product-carousel-arrows .slick-dots-custom',\n prevArrow: $('#technology .product-carousel-arrows .slick-arrows-custom-prev'),\n nextArrow: $('#technology .product-carousel-arrows .slick-arrows-custom-next'),\n responsive: [\n {\n breakpoint: utils.getViewports('lg'),\n settings: {\n slidesToShow: 3,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('md'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n },\n {\n breakpoint: utils.getViewports('sm'),\n settings: {\n slidesToShow: 2,\n slidesToScroll: 1\n }\n }\n ]\n },\n reinitPagination: function (event, slick, defaultSlidesToShow) {\n if (slick.length === 0) {\n return;\n }\n var breakpoints = slick.breakpointSettings;\n var currentBreakpoint = slick.activeBreakpoint;\n var slidesToShow = defaultSlidesToShow; // default slides count (desktop) // 4 for product carousel and articles\n if (currentBreakpoint || slick.options.mobileFirst) { // allow for mobileFirst configs (xs is not active)\n slidesToShow = currentBreakpoint ? breakpoints[currentBreakpoint].slidesToShow : slick.options.slidesToShow;\n if (slick.slideCount <= slidesToShow) {\n slick.$dots.hide();\n slick.$nextArrow.hide();\n slick.$prevArrow.hide();\n } else {\n slick.$dots.show();\n slick.$nextArrow.show();\n slick.$prevArrow.show();\n }\n } else if (slick.slideCount <= slidesToShow) {\n slick.$dots.hide();\n slick.$nextArrow.hide();\n slick.$prevArrow.hide();\n } else {\n slick.$dots.show();\n slick.$nextArrow.show();\n slick.$prevArrow.show();\n }\n }\n};\n","require('@babel/polyfill');\nwindow.jQuery = window.$ = require('jquery');\nvar processInclude = require('base/util');\n\n$(document).ready(function () {\n processInclude(require('./components/menu'));\n processInclude(require('base/components/cookie'));\n processInclude(require('./components/consentTracking'));\n processInclude(require('./components/footer'));\n processInclude(require('lyonscg/components/backtotop'));\n processInclude(require('./components/miniCart'));\n processInclude(require('base/components/collapsibleItem'));\n processInclude(require('./components/search'));\n processInclude(require('./components/clientSideValidation'));\n processInclude(require('base/components/countrySelector'));\n processInclude(require('./components/carousels'));\n processInclude(require('lyonscg/components/tooltips'));\n processInclude(require('./components/einsteinObserver'));\n processInclude(require('org/util/formZoom'));\n processInclude(require('./components/geoLookup'));\n});\n\nrequire('lyonscg/thirdparty/bootstrap');\nrequire('actionsports/components/spinner');\nrequire('slick-carousel');\nrequire('lazysizes');\n","'use strict';\n\nvar base = require('lyonscg/product/base');\nvar slickConfigs = require('actionsports/config/slickConfigs');\nvar zoomConfigs = require('lyonscg/config/zoomConfigs');\nvar imagesloaded = require('imagesloaded');\nvar utils = require('../util/utils');\nvar qtyInput = require('../components/quantityInput');\nvar glide = require('org/config/glide');\nvar glideConfigs = require('site/config/glideConfigs');\nvar pinchzoom = require('org/pinchzoom/pinch-zoom');\n\n/**\n * @param {json} product - Product json\n * @param {json} imgTemp - Product json\n */\nfunction updateMainImages(product, imgTemp) {\n var productID = product.id;\n var $varName = $('.product-detail .set-items, .quick-view-dialog .set-items');\n var $carouselImage;\n var $tempDiv;\n\n if ($varName.length) {\n $tempDiv = $('div.set-items').find('div[data-pid=\"' + productID + '\"]');\n $carouselImage = $tempDiv.find$('.product-carousel, .primary-images .glideScrollThumb, .primary-images .pdp-slideCarousel');\n } else {\n $carouselImage = $('.primary-images');\n }\n $carouselImage.html(imgTemp);\n}\n\n/**\n * Retrieves the relevant pid value\n * @param {jquery} $el - DOM container for a given add to cart button\n * @return {string} - value to be used when adding product to cart\n */\nfunction getPidValue($el) {\n var pid;\n\n if ($('#quickViewModal').hasClass('show') && !$('.product-set').length) {\n pid = $($el).closest('.modal-content').find('.product-quickview').data('pid');\n } else if ($('.product-set-detail').length || $('.product-set').length) {\n pid = $($el).closest('.product-detail').find('.product-id').text();\n } else {\n pid = $('.product-detail:not(.bundle-item)').data('pid');\n }\n\n return pid;\n}\n\n/**\n * Disable PDP Zoom\n */\nfunction disableZoom() {\n $('.slide-link').trigger('zoom.destroy');\n}\n\n/**\n * Init PDP Zoom\n */\nfunction initZoom() {\n disableZoom();\n\n var isDesktop = utils.mediaBreakpointUp('lg');\n var $activeSlide = $('.product-carousel .slick-active');\n var $activeBundleSlides = $('.bundle-item .product-carousel .slick-active');\n var $image = $activeSlide.find('.slide-link.zoom-hires');\n var url = $image.attr('href');\n\n if ($image.length > 0 && url && url !== 'null' && isDesktop) {\n // Start spinner while zoom image loads\n $activeSlide.spinner().start(false);\n\n var config = {\n url: url,\n callback: function () {\n // Stop spinner when zoom image loaded\n $activeSlide.spinner().stop();\n }\n };\n config = $.extend({}, zoomConfigs, config);\n\n $image.zoom(config);\n\n // Enable image zoom on bundled products\n if ($activeBundleSlides.length > 0) {\n $activeBundleSlides.each(function () {\n var $bundleImage = $(this).find('.slide-link.zoom-hires');\n var bundleUrl = $bundleImage.attr('href');\n\n var bundleConfig = {\n url: bundleUrl,\n callback: function () {\n // Stop spinner when zoom image loaded\n $(this).spinner().stop();\n }\n };\n\n bundleConfig = $.extend({}, zoomConfigs, bundleConfig);\n\n $(this).zoom(bundleConfig);\n });\n }\n }\n}\n\n/**\n * Init the product carousel using a predefined slick configuration\n */\nfunction carouselInit() {\n // var $carousels = $('.page .product-carousel');\n var $vidCarousel = $('.video-carousel');\n var $techCarousel = $('.technology-carousel');\n var $syncCarousel = $('.primary-images .pdp-slideCarousel');\n var $glideSlide = $('.product-carousel');\n var glideLength = glide.getGlideSlideLength;\n var pinchzoomElement = $('.pinch-zoom-init');\n var isDesktop = utils.mediaBreakpointUp('lg');\n // var $glideSlideThumb = $('.primary-images .scrollThumb');\n\n // if ($syncCarousel.length) {\n // $('.primary-images .pdp-slideCarousel').not('.slick-initialized').slick(slickConfigs.pdpSlick);\n // if ($('.modal-dialog').hasClass('quick-view-dialog')) {\n // $('.quick-view-dialog .scrollThumb').not('.slick-initialized').slick(slickConfigs.QuickViewHorizontalSlick);\n // } else {\n // $('.primary-images .scrollThumb').not('.slick-initialized').slick(slickConfigs.pdpHorizontalSlick);\n // }\n // }\n\n if ($syncCarousel.length && !($syncCarousel.hasClass('glide--carousel')) && $('.quick-view-dialog .pdp-slideCarousel').length === 0) {\n var glideSyncCarousel = glide.carouselInit('.pdp-slideCarousel', glideConfigs.bellPdp).mount();\n var glideSlideThumb = glide.carouselInit('.scrollThumb', glideConfigs.scrollThumb);\n glide.checkGlideDisableLength(glideSlideThumb);\n glideSlideThumb.mount({ glideLength });\n\n glide.thumbCarousel(glideSyncCarousel, glideSlideThumb);\n glide.clickGlide('.scrollThumb', glideSyncCarousel, glideSlideThumb);\n if (pinchzoomElement.length > 0 && pinchzoom && !isDesktop) {\n pinchzoomElement.addClass('pinch-zoom');\n pinchzoom.pinchZoomPdp([glideSyncCarousel, glideSlideThumb], false, true);\n }\n } else if ($glideSlide.length && $('.quick-view-dialog .product-carousel').length === 0) {\n var glideSingleCarousel = glide.carouselInit('.product-carousel', glideConfigs.pdp);\n glide.checkGlideDisableLength(glideSingleCarousel);\n glideSingleCarousel.mount({ glideLength });\n if (pinchzoomElement.length > 0 && pinchzoom && !isDesktop) {\n pinchzoomElement.addClass('pinch-zoom');\n pinchzoom.pinchZoomPdp([glideSingleCarousel], false, true);\n }\n }\n\n // if ($carousels.length) {\n // // many product tile sliders can exist on a page; loop through them to init individually\n // $carousels.not('.slick-initialized').each(function (idx) {\n // imagesloaded($(this)).on('done', function () {\n // var $carousel = $(this.elements);\n // var sliderID = 'prodSlider' + idx;\n // var defaultConfig = slickConfigs.pdp;\n // // assign unique ID to each slider and target only that slider's controls\n // $carousel.attr('id', sliderID);\n // var $sliderControls = $(`#${sliderID}`).next('.product-carousel-arrows');\n // // hide visible components\n // $sliderControls.addClass('d-none');\n\n // // update slider config to use unique controls and prevent multiple sliders on same page from affecting each other\n // slickConfigs.pdp.appendDots = $sliderControls.find('.slick-dots-custom');\n // slickConfigs.pdp.prevArrow = $sliderControls.find('.slick-arrows-custom-prev');\n // slickConfigs.pdp.nextArrow = $sliderControls.find('.slick-arrows-custom-next');\n\n // // destroy and re-init slider after images are loaded\n // if ($carousel.hasClass('slick-initialized')) {\n // $carousel.off('init', initZoom);\n // $carousel.off('afterChange', initZoom);\n // $carousel.slick('unslick');\n // }\n\n // $carousel.not('.slick-initialized').on('init reInit', function (event, slick) {\n // // show slider nav after init\n // $sliderControls.removeClass('d-none');\n // slickConfigs.reinitPagination(event, slick, 1);\n // $carousels.find('.slick-slide').removeAttr('aria-describedby');\n // }).slick(slickConfigs.pdp);\n\n // utils.smartResize(function () {\n // slickConfigs.reinitPagination(event, $carousel.slick('getSlick'), 1);\n // });\n\n // // Reverts config to default to prevent chaining\n // slickConfigs.pdp = defaultConfig;\n // });\n // });\n // }\n if ($vidCarousel.length && !glide.checkGlideMounted('.video-carousel')) {\n glide.sameClassMultiCarousel('.video-carousel', glideConfigs.video);\n // $vidCarousel.not('.slick-initialized').on('init reInit', function (event, slick) {\n // slickConfigs.reinitPagination(event, slick, 1);\n // $vidCarousel.find('.slick-slide').removeAttr('aria-describedby');\n // }).slick(slickConfigs.video);\n\n // utils.smartResize(function () {\n // slickConfigs.reinitPagination(event, $vidCarousel.slick('getSlick'), 1);\n // });\n }\n if ($techCarousel.length && !glide.checkGlideMounted('.technology-carousel')) {\n glide.sameClassMultiCarousel('.technology-carousel', glideConfigs.tech);\n // $techCarousel.not('.slick-initialized').on('init reInit', function (event, slick) {\n // slickConfigs.reinitPagination(event, slick, 3);\n // $techCarousel.find('.slick-slide').removeAttr('aria-describedby');\n // }).slick(slickConfigs.tech);\n\n // utils.smartResize(function () {\n // slickConfigs.reinitPagination(event, $techCarousel.slick('getSlick'), 3);\n // });\n }\n}\n\n/**\n * Init the quickview carousel using a predefined slick configuration\n */\nfunction carouselInitQuickview() {\n var $carouselsQuickview = $('.quick-view-dialog .product-carousel');\n var $carouselSync = $('.quick-view-dialog .pdp-slideCarousel');\n var $glideSlideThumb = $('.primary-images .scrollThumb');\n const glideLength = glide.getGlideSlideLength;\n\n if ($carouselSync.length && $glideSlideThumb.length && !glide.checkGlideMounted('.pdp-slideCarousel')) {\n const glideSyncCarousel = glide.carouselInit('.pdp-slideCarousel', glideConfigs.plpQuickview);\n glide.checkGlideDisableLength(glideSyncCarousel);\n glideSyncCarousel.mount({ glideLength });\n const glideSlideThumb = glide.carouselInit('.scrollThumb', glideConfigs.quickViewScrollThumb);\n glide.checkGlideDisableLength(glideSlideThumb);\n glideSlideThumb.mount({ glideLength });\n glide.thumbCarousel(glideSyncCarousel, glideSlideThumb);\n glide.clickGlide('.scrollThumb', glideSyncCarousel, glideSlideThumb);\n } else if ($carouselsQuickview.length && !glide.checkGlideMounted('.product-carousel')) {\n var currentCarousel = glide.carouselInit('.product-carousel', glideConfigs.plpQuickview);\n glide.checkGlideDisableLength(currentCarousel);\n currentCarousel.mount({ glideLength });\n }\n\n // if ($carouselSync.length) {\n // $('.quick-view-dialog .pdp-slideCarousel').not('.slick-initialized').slick(slickConfigs.pdpSlick);\n // $('.quick-view-dialog .scrollThumb').not('.slick-initialized').slick(slickConfigs.QuickViewHorizontalSlick);\n // }\n if ($carouselsQuickview.length) {\n // many product tile sliders can exist on a page; loop through them to init individually\n $carouselsQuickview.not('.glide--swipeable').each(function (idx) {\n imagesloaded($(this)).on('done', function () {\n var $carousel = $(this.elements);\n var sliderID = 'qvSlider' + idx;\n var defaultConfig = glideConfigs.pdp;\n // assign unique ID to each slider and target only that slider's controls\n $carousel.attr('id', sliderID);\n var quickViewGlide = glide.carouselInit('.' + $carousel.attr('class'), defaultConfig);\n // destroy and re-init slider after images are loaded\n quickViewGlide.on('mount.before', function () {\n if ($carousel.hasClass('glide--swipeable')) {\n $carousel.off('init', initZoom);\n $carousel.off('afterChange', initZoom);\n quickViewGlide.destroy();\n }\n });\n $carousel.not('.glide--swipeable').each(function () {\n quickViewGlide.mount();\n });\n });\n });\n }\n}\n\n/**\n * Init the product carousel using a predefined slick configuration for bonus item\n */\nfunction carouselInitBonus() {\n var $carouselBonus = $('.bonus-product-item');\n if ($carouselBonus.length) {\n $carouselBonus.each(function () {\n var $carouselBonusDiv = $(this).find('.product-carousel');\n if ($carouselBonusDiv.length) {\n imagesloaded($carouselBonusDiv).on('done', function () {\n $carouselBonusDiv.not('.slick-initialized').slick($.extend(slickConfigs.bonusModal, {\n appendDots: $carouselBonusDiv.siblings('.product-carousel-arrows').find('.slick-dots-custom'),\n prevArrow: $carouselBonusDiv.siblings('.product-carousel-arrows').find('.slick-arrows-custom-prev'),\n nextArrow: $carouselBonusDiv.siblings('.product-carousel-arrows').find('.slick-arrows-custom-next')\n }));\n });\n }\n });\n }\n}\n\n/**\n * Init the product carousel with recommendation in add to cart modal\n */\nfunction addedCartRecommendations() {\n if ($('.addedToCartCarouselAS').length) {\n // many product tile sliders can exist on a page; loop through them to init individually\n // $('.addedToCartCarouselAS').not('.slick-initialized').each(function () {\n // var $slider = $(this);\n // var defaultConfig = slickConfigs.productRecommendationTile;\n // if ($slider.hasClass('recommendation')) {\n // defaultConfig.slide = '.grid-tile';\n // }\n\n // // each slider target only that slider's controls\n // var $sliderControls = $slider.next('.product-carousel-arrows');\n\n // // update slider config to use unique controls and prevent multiple sliders on same page from affecting each other\n // slickConfigs.productRecommendationTile.appendDots = $sliderControls.find('.slick-dots-custom');\n // slickConfigs.productRecommendationTile.prevArrow = $sliderControls.find('.slick-arrows-custom-prev');\n // slickConfigs.productRecommendationTile.nextArrow = $sliderControls.find('.slick-arrows-custom-next');\n\n // $slider.slick(defaultConfig);\n\n // utils.smartResize(function () {\n // slickConfigs.reinitPagination(event, $slider.slick('getSlick'), 4);\n // });\n // });\n glide.sameClassMultiCarousel('.addedToCartCarouselAS', glideConfigs.productRecommendationTile);\n }\n\n if ($('.addedToCartCarouselGiro').length) {\n // many product tile sliders can exist on a page; loop through them to init individually\n // $('.addedToCartCarouselGiro').not('.slick-initialized').each(function () {\n // var $slider = $(this);\n // var defaultConfig = slickConfigs.productGiroRecommendationTile;\n // if ($slider.hasClass('recommendation')) {\n // defaultConfig.slide = '.grid-tile';\n // }\n\n // // each slider target only that slider's controls\n // var $sliderControls = $slider.next('.product-carousel-arrows');\n\n // // update slider config to use unique controls and prevent multiple sliders on same page from affecting each other\n // slickConfigs.productGiroRecommendationTile.appendDots = $sliderControls.find('.slick-dots-custom');\n // slickConfigs.productGiroRecommendationTile.prevArrow = $sliderControls.find('.slick-arrows-custom-prev');\n // slickConfigs.productGiroRecommendationTile.nextArrow = $sliderControls.find('.slick-arrows-custom-next');\n\n // $slider.on('init reInit', function (event, slick) {\n // slickConfigs.reinitPagination(event, slick, 4);\n // }).slick(defaultConfig);\n\n // utils.smartResize(function () {\n // slickConfigs.reinitPagination(event, $slider.slick('getSlick'), 4);\n // });\n // });\n glide.sameClassMultiCarousel('.addedToCartCarouselGiro', glideConfigs.productRecommendationTile);\n }\n}\n\n/**\n * Parses the html for a modal window\n * @param {string} html - representing the body and footer of the modal window\n *\n * @return {Object} - Object with properties body and footer.\n */\nfunction parseHtml(html) {\n var $html = $('
      ').append($.parseHTML(html));\n\n var body = $html.find('.choice-of-bonus-product');\n var footer = $html.find('.modal-footer').children();\n\n return { body: body, footer: footer };\n}\n\n/**\n * Retrieves url to use when adding a product to the cart\n *\n * @param {Object} data - data object used to fill in dynamic portions of the html\n */\nfunction chooseBonusProducts(data) {\n $('.modal-body').spinner().start(false);\n\n if ($('#chooseBonusProductModal').length !== 0) {\n $('#chooseBonusProductModal').remove();\n }\n var bonusUrl;\n if (data.bonusChoiceRuleBased) {\n bonusUrl = data.showProductsUrlRuleBased;\n } else {\n bonusUrl = data.showProductsUrlListBased;\n }\n\n var htmlString = ''\n + '
      '\n + ''\n + '
      '\n + ''\n + '
      '\n + '
      '\n + ' ' + data.labels.selectprods + ''\n + ' '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      ';\n $('body').append(htmlString);\n $('.modal-body').spinner().start(false);\n\n $.ajax({\n url: bonusUrl,\n method: 'GET',\n dataType: 'json',\n success: function (response) {\n var parsedHtml = parseHtml(response.renderedTemplate);\n $('#chooseBonusProductModal .modal-body').empty();\n $('#chooseBonusProductModal .enter-message').text(response.enterDialogMessage);\n $('#chooseBonusProductModal .modal-header .close .sr-only').text(response.closeButtonText);\n $('#chooseBonusProductModal .modal-body').html(parsedHtml.body);\n $('#chooseBonusProductModal .modal-footer').html(parsedHtml.footer);\n $('#chooseBonusProductModal').modal('show');\n $.spinner().stop();\n qtyInput.qvInit();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n}\n\n/**\n * Retrieve contextual quantity selector\n * @param {jquery} $el - DOM container for the relevant quantity\n * @return {jquery} - quantity selector DOM container\n */\nfunction getQuantitySelector($el) {\n return $el && $('.set-items').length\n ? $($el).closest('.set-item').find('.quantity-select')\n : $('.quantity-select');\n}\n\n/**\n * Updates DOM using post-option selection Ajax response\n *\n * @param {OptionSelectionResponse} optionsHtml - Ajax response optionsHtml from selecting a product option\n * @param {jQuery} $productContainer - DOM element for current product\n */\nfunction updateOptions(optionsHtml, $productContainer) {\n // Update options\n $productContainer.find('.product-options').empty().html(optionsHtml);\n}\n\n/**\n * Process the attribute values for an attribute that has image swatches\n *\n * @param {Object} attr - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {Object[]} attr.values - Array of attribute value objects\n * @param {string} attr.values.value - Attribute coded value\n * @param {string} attr.values.url - URL to de/select an attribute value of the product\n * @param {boolean} attr.values.isSelectable - Flag as to whether an attribute value can be\n * selected. If there is no variant that corresponds to a specific combination of attribute\n * values, an attribute may be disabled in the Product Detail Page\n * @param {jQuery} $productContainer - DOM container for a given product\n * @param {Object} msgs - object containing resource messages\n */\nfunction processSwatchValues(attr, $productContainer, msgs) {\n attr.values.forEach(function (attrValue) {\n var $attrValue = $productContainer.find('[data-attr=\"' + attr.id + '\"] [data-attr-value=\"' +\n attrValue.value + '\"]');\n var $swatchButton = $attrValue.parent();\n\n if (attrValue.selected) {\n $attrValue.addClass('selected');\n $attrValue.siblings('.selected-assistive-text').text(msgs.assistiveSelectedText);\n } else {\n $attrValue.removeClass('selected');\n $attrValue.siblings('.selected-assistive-text').empty();\n }\n\n if (attrValue.url) {\n $swatchButton.attr('data-url', attrValue.url).data('url', attrValue.url);\n } else {\n $swatchButton.removeAttr('data-url');\n }\n\n // Disable if not selectable\n $attrValue.removeClass('selectable unselectable');\n\n\n if (attr.attributeId === 'size') {\n var currentSize = $productContainer.find('[data-attr=\"' + attr.id + '\"] [data-attr-value=\"' +\n attrValue.value + '\"]');\n var sizeBtn = currentSize.parent();\n if (typeof attrValue.display !== 'undefined') {\n if (attrValue.display === true) {\n sizeBtn.show();\n } else {\n sizeBtn.hide();\n }\n }\n }\n\n $attrValue.addClass(attrValue.selectable ? 'selectable' : 'unselectable');\n });\n}\n\n/**\n * Process attribute values associated with an attribute that does not have image swatches\n *\n * @param {Object} attr - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {Object[]} attr.values - Array of attribute value objects\n * @param {string} attr.values.value - Attribute coded value\n * @param {string} attr.values.url - URL to de/select an attribute value of the product\n * @param {boolean} attr.values.isSelectable - Flag as to whether an attribute value can be\n * selected. If there is no variant that corresponds to a specific combination of attribute\n * values, an attribute may be disabled in the Product Detail Page\n * @param {jQuery} $productContainer - DOM container for a given product\n */\nfunction processNonSwatchValues(attr, $productContainer) {\n var $attr = '[data-attr=\"' + attr.id + '\"]';\n var $defaultOption = $productContainer.find($attr + ' .select-' + attr.id + ' option:first');\n $defaultOption.attr('value', attr.resetUrl);\n\n attr.values.forEach(function (attrValue) {\n var $attrValue = $productContainer\n .find($attr + ' [data-attr-value=\"' + attrValue.value + '\"]');\n $attrValue.attr('value', attrValue.url)\n .removeAttr('disabled');\n if (!attrValue.selectable) {\n // $attrValue.attr('disabled', true);\n }\n });\n}\n\n/**\n * Routes the handling of attribute processing depending on whether the attribute has image\n * swatches or not\n *\n * @param {Object} attrs - Attribute\n * @param {string} attr.id - Attribute ID\n * @param {jQuery} $productContainer - DOM element for a given product\n * @param {Object} msgs - object containing resource messages\n */\nfunction updateAttrs(attrs, $productContainer, msgs) {\n // For Vista Action Sports, all variation attributes are swatches\n var attrsWithSwatches = ['color', 'size', 'lensTint', 'side'];\n\n attrs.variationAttributes.forEach(function (attr) {\n if (attrsWithSwatches.indexOf(attr.id) > -1) {\n processSwatchValues(attr, $productContainer, msgs, attrs);\n } else {\n processNonSwatchValues(attr, $productContainer);\n }\n });\n}\n\n/**\n *\n * @param {string} msg - the avialablity message\n * @returns {string} - markup to be rendered\n */\nfunction getAvailabilityMarkup(msg) {\n return '
    3. ' + msg + '
    4. ';\n}\n\n/**\n * Determine if the current selection is complete, i.e. a selection has been made for all variation attributes.\n * @param {Object} response - ajax response\n * @returns {boolean} - true if the selection is incomplete\n */\nfunction isSelectionIncomplete(response) {\n return response.product.variationAttributes.some(variation => variation.selectedValue === null);\n}\n\n/**\n * Determine if the currently selected attribute combo is valid or not, i.e. whether it represents\n * a potentially orderable sku at all.\n * @param {Object} response - ajax response\n * @returns {boolean} - true if the selected combination does not exist or is not orderable\n */\nfunction isSelectionInvalid(response) {\n if (response.product.readyToOrder) return false;\n return response.product.variationAttributes.some(variation => variation.values.some(v => v.selected && !v.selectable));\n}\n\n/**\n * Updates the availability status in the Product Detail Page\n *\n * @param {Object} response - Ajax response object after an\n * attribute value has been [de]selected\n * @param {jQuery} $productContainer - DOM element for a given product\n */\nfunction updateAvailability(response, $productContainer) {\n var isAvailable = response.product.available;\n var showBackInStockMsg = response.product.productType !== 'master' && response.product.online && response.product.isBackInStockNotificationEligible && !response.product.available;\n var bisPID = response.product.id;\n\n var availabilityValue = '';\n var availabilityMessages = response.product.availability.messages;\n response.product.availability.incompletecombo = isSelectionIncomplete(response);\n response.product.availability.invalidcombo = isSelectionInvalid(response);\n if (!response.product.readyToOrder) {\n if (response.product.availability.invalidcombo) {\n availabilityValue = getAvailabilityMarkup(response.resources.info_invalidcombination);\n } else if (response.product.availability.incompletecombo) {\n availabilityValue = getAvailabilityMarkup(response.resources.info_selectforstock);\n } else {\n availabilityValue = getAvailabilityMarkup(response.resources.info_selectforstock);\n }\n } else if (isAvailable) {\n availabilityValue += getAvailabilityMarkup(response.resources.available);\n } else if (!isAvailable) {\n availabilityValue += getAvailabilityMarkup(response.resources.unavailable);\n response.product.availability.outofstock = true;\n } else {\n availabilityMessages.forEach(function (message) {\n availabilityValue += getAvailabilityMarkup(message);\n });\n }\n\n // handle back in stock notification display\n if (showBackInStockMsg) {\n $('.back-in-stock-notification').removeClass('hidden');\n $('.back-in-stock-notification .bisPID').val(bisPID);\n } else {\n $('.back-in-stock-notification').addClass('hidden');\n }\n\n $($productContainer).trigger('product:updateAvailability', {\n product: response.product,\n $productContainer: $productContainer,\n message: availabilityValue,\n resources: response.resources\n });\n}\n\n/**\n * Generates html for product attributes section\n *\n * @param {array} attributes - list of attributes\n * @return {string} - Compiled HTML\n */\nfunction getAttributesHtml(attributes) {\n if (!attributes) {\n return '';\n }\n\n var html = '';\n\n attributes.forEach(function (attributeGroup) {\n if (attributeGroup.ID === 'mainAttributes') {\n attributeGroup.attributes.forEach(function (attribute) {\n html += '
      ' + attribute.label + ': '\n + attribute.value + '
      ';\n });\n }\n });\n\n return html;\n}\n\n/**\n * Retrieve product options\n *\n * @param {jQuery} $productContainer - DOM element for current product\n * @return {string} - Product options and their selected values\n */\nfunction getOptions($productContainer) {\n var options = $productContainer\n .find('.product-option')\n .map(function () {\n var $elOption = $(this).find('.options-select');\n var urlValue = $elOption.val();\n var selectedValueId = $elOption.find('option[value=\"' + urlValue + '\"]')\n .data('value-id');\n return {\n optionId: $(this).data('option-id'),\n selectedValueId: selectedValueId\n };\n }).toArray();\n\n return JSON.stringify(options);\n}\n\n/**\n * Parses JSON from Ajax call made whenever an attribute value is [de]selected\n * @param {Object} response - response from Ajax call\n * @param {Object} response.product - Product object\n * @param {string} response.product.id - Product ID\n * @param {Object[]} response.product.variationAttributes - Product attributes\n * @param {Object[]} response.product.images - Product images\n * @param {boolean} response.product.hasRequiredAttrsSelected - Flag as to whether all required\n * attributes have been selected. Used partially to\n * determine whether the Add to Cart button can be enabled\n * @param {jQuery} $productContainer - DOM element for a given product.\n */\nfunction handleVariantResponse(response, $productContainer) {\n var isChoiceOfBonusProducts =\n $productContainer.parents('.choose-bonus-product-dialog').length > 0;\n var isVaraint;\n if (response.product.variationAttributes) {\n // updateAttrs(response.product.variationAttributes, $productContainer, response.resources);\n updateAttrs(response.product, $productContainer, response.resources);\n isVaraint = response.product.productType === 'variant';\n if (isChoiceOfBonusProducts && isVaraint) {\n $productContainer.parent('.bonus-product-item')\n .attr('data-pid', response.product.id)\n .data('pid', response.product.id);\n\n $productContainer.parent('.bonus-product-item')\n .attr('data-ready-to-order', response.product.readyToOrder)\n .data('ready-to-order', response.product.readyToOrder);\n }\n }\n\n // Update primary images\n base.carouselUnslick();\n // $('.primary-images .scrollThumb, .primary-images .pdp-slideCarousel').removeClass('slick-initialized');\n updateMainImages(response.product, response.imgTemp);\n\n // Update pricing\n if (!isChoiceOfBonusProducts) {\n var $priceSelector = $('.prices .price', $productContainer).length\n ? $('.prices .price', $productContainer)\n : $('.prices .price');\n $priceSelector.replaceWith(response.product.price.html);\n }\n\n // Update promotions\n $productContainer.find('.promotions').empty().html(response.product.promotionsHtml);\n\n updateAvailability(response, $productContainer);\n\n if (isChoiceOfBonusProducts) {\n var $selectButton = $productContainer.find('.select-bonus-product');\n $selectButton.trigger('bonusproduct:updateSelectButton', {\n product: response.product, $productContainer: $productContainer\n });\n } else {\n // Enable \"Add to Cart\" button if all required attributes have been selected\n $('button.add-to-cart, button.add-to-cart-global, button.update-cart-product-global').trigger('product:updateAddToCart', {\n product: response.product, $productContainer: $productContainer, resources: response.resources\n }).trigger('product:statusUpdate', response.product);\n }\n\n // PFAS\n try {\n var pfasEnable = $productContainer.find('.pfas-pdp-row').data('enablepfas');\n if (pfasEnable) {\n if (response.product.pfasBlockCheckout) {\n $('.pfas-pdp-row').show();\n } else {\n $('.pfas-pdp-row').hide();\n }\n }\n } catch(e) { // eslint-disable-line\n }\n\n // Update attributes\n $productContainer.find('.main-attributes').empty()\n .html(getAttributesHtml(response.product.attributes));\n\n var locallyBtn = $productContainer.find('#lcly-button-0');\n if (locallyBtn.length > 0) {\n if (response.product.UPC !== null) {\n lcly_reload_0({ 'upc': response.product.UPC }); // eslint-disable-line\n $('.localretailer').css('visibility', 'visible');\n } else {\n $('.localretailer').css('visibility', 'hidden');\n }\n }\n}\n\n/**\n * Updates the quantity DOM elements post Ajax call\n * @param {UpdatedQuantity[]} quantities -\n * @param {jQuery} $productContainer - DOM container for a given product\n */\nfunction updateQuantities(quantities, $productContainer) {\n if ($productContainer.parent('.bonus-product-item').length <= 0) {\n var optionsHtml = quantities.map(function (quantity) {\n var selected = quantity.selected ? ' selected ' : '';\n return '';\n }).join('');\n getQuantitySelector($productContainer).empty().html(optionsHtml);\n }\n}\n\n/**\n * updates the product view when a product attribute is selected or deselected or when\n * changing quantity\n * @param {string} selectedValueUrl - the Url for the selected variation value\n * @param {jQuery} $productContainer - DOM element for current product\n */\nfunction attributeSelect(selectedValueUrl, $productContainer) {\n if (selectedValueUrl) {\n $('body').trigger('product:beforeAttributeSelect',\n { url: selectedValueUrl, container: $productContainer });\n\n $.ajax({\n url: selectedValueUrl,\n method: 'GET',\n success: function (data) {\n handleVariantResponse(data, $productContainer);\n updateOptions(data.product.optionsHtml, $productContainer);\n updateQuantities(data.product.quantities, $productContainer);\n $('body').trigger('product:afterAttributeSelect',\n { data: data, container: $productContainer });\n $.spinner().stop();\n qtyInput.pdpInit();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n }\n}\n\n/**\n * Retrieves url to use when adding a product to the cart\n *\n * @return {string} - The provided URL to use when adding a product to the cart\n */\nfunction getAddToCartUrl() {\n return $('.add-to-cart-url').val();\n}\n\n/**\n * Retrieves the bundle product item ID's for the Controller to replace bundle master product\n * items with their selected variants\n *\n * @return {string[]} - List of selected bundle product item ID's\n */\nfunction getChildProducts() {\n var childProducts = [];\n $('.bundle-item').each(function () {\n childProducts.push({\n pid: $(this).find('.product-id').text(),\n quantity: parseInt($(this).find('label.quantity').data('quantity'), 10)\n });\n });\n\n return childProducts.length ? JSON.stringify(childProducts) : [];\n}\n\n/**\n * Retrieves the value associated with the Quantity pull-down menu\n * @param {jquery} $el - DOM container for the relevant quantity\n * @return {string} - value found in the quantity input\n */\nfunction getQuantitySelected($el) {\n return getQuantitySelector($el).val();\n}\n\n/**\n * Updates the Mini-Cart quantity value after the customer has pressed the \"Add to Cart\" button\n * @param {string} response - ajax response from clicking the add to cart button\n */\nfunction handlePostCartAdd(response) {\n $('.minicart').trigger('count:update', response);\n var messageType = response.error ? 'alert-danger' : 'alert-success';\n // show add to cart toast\n if (response.newBonusDiscountLineItem\n && Object.keys(response.newBonusDiscountLineItem).length !== 0) {\n chooseBonusProducts(response.newBonusDiscountLineItem);\n } else {\n if ($('.add-to-cart-messages').length === 0) {\n $('body').append(\n '
      '\n );\n }\n if ($('#added-to-cart').length > 0) {\n try {\n if ($('.added-to-cart-slot .recommendation-carousel').length) {\n $('#added-to-cart').modal('show');\n $('body').on('shown.bs.modal', '#added-to-cart', function () {\n addedCartRecommendations();\n });\n } else {\n $('#added-to-cart').modal('show');\n $('body').on('shown.bs.modal', '#added-to-cart', function () {\n // $('.added-to-cart-slot [id^=\"cq_recomm_slot-\"]').text('Item Added to Cart');\n $('.noCartRecommendations').show();\n });\n }\n } catch (e) { // eslint-disable-line\n }\n } else {\n $('.add-to-cart-messages').append(\n '
      '\n + response.message\n + '
      '\n );\n setTimeout(function () {\n $('.add-to-basket-alert').remove();\n }, 5000);\n }\n }\n}\n\nvar exportBase = $.extend({}, base, {\n attributeSelect: attributeSelect,\n carouselInit: carouselInit,\n carouselInitQuickview: carouselInitQuickview,\n carouselInitBonus: carouselInitBonus,\n disableZoom: disableZoom,\n addedCartRecommendations: addedCartRecommendations,\n updateMainImages: updateMainImages,\n methods: {\n editBonusProducts: function (data) {\n chooseBonusProducts(data);\n }\n },\n colorAttribute: function () {\n $(document).on('click ', '[data-attr=\"color\"] button', function (e) {\n e.preventDefault();\n\n // if ($(this).data('disabled') || !($(this).data('url'))) {\n // return;\n // }\n var $productContainer = $(this).closest('.set-item');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.product-detail');\n }\n\n attributeSelect($(this).attr('data-url'), $productContainer);\n });\n },\n selectAttribute: function () {\n $(document).on('click ', 'button.noncolor-attribute, button.lensTint-attribute', function (e) {\n e.preventDefault();\n\n // if ($(this).data('disabled') || !($(this).data('url'))) {\n // return;\n // }\n\n var $productContainer = $(this).closest('.set-item');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.product-detail');\n }\n attributeSelect($(this).attr('data-url'), $productContainer);\n });\n },\n availability: function () {\n $(document).on('change', '.quantity-select', function (e) {\n e.preventDefault();\n\n var $productContainer = $(this).closest('.product-detail');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.modal-content').find('.product-quickview');\n }\n\n if ($('.bundle-items', $productContainer).length === 0) {\n attributeSelect($(e.currentTarget).find('option:selected').data('url'),\n $productContainer);\n }\n });\n },\n\n addToCart: function () {\n $(document).on('click', 'button.add-to-cart, button.add-to-cart-global', function () {\n var addToCartUrl;\n var pid;\n var pidsObj;\n var setPids;\n\n // Determine if the variant selection process is complete yet. if not, prevent a2c submit\n if ($(this).is('.incomplete')) {\n const unfinishedGroups = $('.attributes').find('.attribute:not(.selected)');\n if (unfinishedGroups.length > 0) {\n unfinishedGroups.each(function () {\n $(this).addClass('selection-error');\n });\n return;\n }\n }\n\n $('body').trigger('product:beforeAddToCart', this);\n\n if ($('.set-items').length && $(this).hasClass('add-to-cart-global')) {\n setPids = [];\n\n $('.product-detail.set-item').each(function () {\n if (!$(this).hasClass('product-set-detail')) {\n setPids.push({\n pid: $(this).data('pid'),\n qty: $(this).find('.quantity-select').val(),\n options: getOptions($(this))\n });\n }\n });\n pidsObj = JSON.stringify(setPids);\n }\n\n pid = getPidValue($(this));\n\n var $productContainer = $(this).closest('.product-detail');\n if (!$productContainer.length) {\n $productContainer = $(this).closest('.quick-view-dialog').find('.product-detail');\n }\n addToCartUrl = getAddToCartUrl();\n\n var form = {\n pid: pid,\n pidsObj: pidsObj,\n childProducts: getChildProducts(),\n quantity: getQuantitySelected($(this))\n };\n\n if (!$('.bundle-item').length) {\n form.options = getOptions($productContainer);\n }\n\n $(this).trigger('updateAddToCartFormData', form);\n if (addToCartUrl) {\n $.ajax({\n url: addToCartUrl,\n method: 'POST',\n data: form,\n success: function (data) {\n handlePostCartAdd(data);\n $('body').trigger('product:afterAddToCart', data);\n $.spinner().stop();\n },\n error: function () {\n $.spinner().stop();\n }\n });\n }\n });\n },\n selectBonusProduct: function () {\n $(document).on('click', '.select-bonus-product', function () {\n var $choiceOfBonusProduct = $(this).parents('.choice-of-bonus-product');\n var pid = $(this).data('pid');\n var maxPids = $('.choose-bonus-product-dialog').data('total-qty');\n var submittedQty = parseInt($choiceOfBonusProduct.find('.bonus-quantity-select').val(), 10);\n var totalQty = 0;\n $.each($('#chooseBonusProductModal .selected-bonus-products .selected-pid'), function () {\n totalQty += $(this).data('qty');\n });\n totalQty += submittedQty;\n var optionID = $choiceOfBonusProduct.find('.product-option').data('option-id');\n var valueId = $choiceOfBonusProduct.find('.options-select option:selected').data('valueId');\n if (totalQty <= maxPids) {\n var selectedBonusProductHtml = ''\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + $choiceOfBonusProduct.find('.product-name').html()\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n + '
      '\n ;\n $('#chooseBonusProductModal .selected-bonus-products').append(selectedBonusProductHtml);\n $('.pre-cart-products').html(totalQty);\n $('.selected-bonus-products .bonus-summary').removeClass('alert-danger');\n } else {\n $('.selected-bonus-products .bonus-summary').addClass('alert-danger');\n }\n });\n },\n getChildProducts: getChildProducts,\n getOptions: getOptions,\n handlePostCartAdd: handlePostCartAdd,\n getPidValue: getPidValue\n});\n\n$('.customContact').on('click touchstart', function () {\n sessionStorage.setItem('customContact', 'true');\n});\n\nwindow.addEventListener('LOCALLY_data_update', function () {\n if (!$('#lcly-button-0').hasClass('localretailer')) {\n $('#lcly-button-0').addClass('localretailer text-center');\n }\n\n $('.lcly-primary-trigger').prepend($('', { id: 'map-pin', src: '/on/demandware.static/Sites-giro-Site/-/default/dw7356f338/images/Map-pin.svg', class: 'locallyImg', alt: 'FIND IT LOCALLY' }));\n\n var lclyMapPin = {};\n $('.locallyImg').each(function () {\n var txt = $(this).attr('src');\n if (lclyMapPin[txt]) {\n $(this).remove();\n } else {\n lclyMapPin[txt] = true;\n }\n });\n}, false);\n\nmodule.exports = exportBase;\n","'use strict';\n\nvar baseUtils = require('lyonscg/util/utils');\nvar _ = require('lodash');\n\nvar utils = {\n /**\n * @function\n * @alias module:util.appendCSRFTokenToURL\n * @description Append csrf token to url\n * @param {string} url - URL\n * @param {Object} $form - The form object\n * @returns {string} - URL with csrf token\n */\n appendCSRFTokenToURL: function (url, $form) {\n return this.appendParamToURL(url, 'csrf_token', $form.find('[name=\"csrf_token\"]').val());\n },\n\n /**\n * @function\n * @description Executes a callback function when the user has stopped resizing the screen.\n * It will only execute on changes to page width.\n * @param {function} callback - Callback function for the resize event\n */\n smartResize: function (callback) {\n let windowWidth = $(window).innerWidth();\n\n $(window).resize(_.debounce(() => {\n if (windowWidth !== $(window).innerWidth()) {\n callback();\n windowWidth = $(window).innerWidth();\n }\n }, 100));\n }\n};\n\nmodule.exports = $.extend({}, baseUtils, utils);\n","'use strict';\n\n/**\n * @desc gets the max input value\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @returns {int} the max value\n */\nfunction getQtyMaxValue(qtyInput) {\n return parseInt(qtyInput.attr('max'), 0);\n}\n\n/**\n * @desc gets the min input value\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @returns {int} the min value\n */\nfunction getQtyMinValue(qtyInput) {\n return parseInt(qtyInput.attr('min'), 0);\n}\n\n/**\n * @desc warns user if desired quantity is close to product inventory\n * @param {string} pid - product id\n * @param {string} url - url of request\n * @param {int} updatedValue - qty value\n */\nfunction warnNearInventoryLimit(pid, url, updatedValue) {\n $.ajax({\n url: url,\n method: 'GET',\n data: {\n pid: pid,\n qty: updatedValue\n },\n dataType: 'json',\n success: (data) => {\n if (data.outsideInventory) {\n $('body').append('
      ' + data.msg + '
      ');\n setTimeout(function () {\n $('.qty-alert').remove();\n }, 5000);\n }\n }\n });\n}\n\n/**\n * @desc quantity increase\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @param {string} pid - product id\n */\nfunction qtyIncrease(qtyInput, pid) {\n var dataInv = $('.modal.show .product-quickview').attr('data-inv');\n var prodId = (dataInv && dataInv.length > 0 ? dataInv : pid);\n var qtyMax = getQtyMaxValue(qtyInput);\n var updatedValue = parseInt(qtyInput.val(), 10) + 1;\n if (updatedValue <= qtyMax) {\n qtyInput.val(updatedValue);\n var url = qtyInput.parents('.quantity-form').data('action-url');\n if (url !== '') {\n warnNearInventoryLimit(prodId, url, updatedValue);\n }\n }\n}\n\n/**\n * @desc quantity decrease\n * @param {jqueryObject} qtyInput - jquery selector of input field\n */\nfunction qtyDecrease(qtyInput) {\n var qtyMin = getQtyMinValue(qtyInput);\n var updatedValue = parseInt(qtyInput.val(), 10) - 1;\n if (updatedValue >= qtyMin) {\n qtyInput.val(updatedValue);\n }\n}\n\n/**\n * Handles a user physically typing in more or less that the max or min\n * @param {jqueryObject} qtyInput - jquery selector of input field\n * @param {string} pid = product id\n */\nfunction qtyTextUpdate(qtyInput, pid) {\n var qtyMax = getQtyMaxValue(qtyInput);\n var qtyMin = getQtyMinValue(qtyInput);\n if (qtyInput.val() > qtyMax) {\n $('body').append('
      You cannot add more than ' + qtyMax + '.
      ');\n setTimeout(function () {\n $('.qty-alert').remove();\n }, 5000);\n qtyInput.val(qtyMax);\n }\n\n if (qtyInput.val() < qtyMin) {\n $('body').append('
      You cannot add less than ' + qtyMin + '.
      ');\n setTimeout(function () {\n $('.qty-alert').remove();\n }, 5000);\n qtyInput.val(qtyMin);\n }\n var url = qtyInput.parents('.quantity-form').data('action-url');\n warnNearInventoryLimit(pid, url, qtyInput.val());\n}\n\nmodule.exports = {\n pdpInit: function () {\n var $qtyInput = $('.prices-add-to-cart-actions .quantity-form #quantity, .attributes .quantity-form #quantity');\n var pid = $('.product-detail').data('pid');\n\n // Prevent multiple inits of events when selecting attributes\n $('body').off('click', '.prices-add-to-cart-actions .quantity-form .plus, .attributes .quantity-form .plus');\n $('body').off('click', '.prices-add-to-cart-actions .quantity-form .minus, .attributes .quantity-form .minus');\n\n $('body').on('click', '.prices-add-to-cart-actions .quantity-form .plus, .attributes .quantity-form .plus', function () {\n qtyIncrease($qtyInput, pid);\n });\n\n $('body').on('click', '.prices-add-to-cart-actions .quantity-form .minus, .attributes .quantity-form .minus', function () {\n qtyDecrease($qtyInput);\n });\n\n $('#quantity').on('blur', function () {\n qtyTextUpdate($qtyInput, pid);\n });\n },\n qvInit: function () {\n var $qtyInput = $('.quick-view-dialog .quantity-form #quantity');\n var pid = $('.product-quickview').data('pid');\n\n $('body').off('click', '.quick-view-dialog .quantity-form .plus').on('click', '.quick-view-dialog .quantity-form .plus', function () {\n qtyIncrease($qtyInput, pid);\n });\n\n $('body').on('click', '.quick-view-dialog .quantity-form .minus', function () {\n qtyDecrease($qtyInput);\n });\n\n $('body').on('blur', '#quantity', function () {\n qtyTextUpdate($qtyInput, pid);\n });\n },\n miniCartInit: function () {\n var $qtyInput = $('.minicart .quantity-form input[type=\"text\"]');\n\n $('body').on('click', '.minicart .quantity-form .plus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyIncrease($qtyInput);\n\n if ($('.popover .cart').length || $('.cart-page').length) {\n $qtyInput.trigger('change');\n }\n });\n\n $('body').on('click', '.minicart .quantity-form .minus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyDecrease($qtyInput);\n\n if ($('.popover .cart').length || $('.cart-page').length) {\n $qtyInput.trigger('change');\n }\n });\n },\n miniCartDeInit: function () {\n $('body').off('click', '.minicart .quantity-form .plus');\n\n $('body').off('click', '.minicart .quantity-form .minus');\n },\n cartInit: function () {\n var $qtyInput = $('.cart-page .quantity-form input[type=\"text\"]');\n\n $('body').on('click', '.cart-page .quantity-form .plus, .cart-page .quantity-form-SFL .plus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyIncrease($qtyInput);\n\n if ($('.popover .cart').length || $('.cart-page').length) {\n $qtyInput.trigger('change');\n }\n });\n\n $('body').on('click', '.cart-page .quantity-form .minus, .cart-page .quantity-form-SFL .minus', function () {\n $qtyInput = $(this).siblings('input[type=\"text\"]');\n qtyDecrease($qtyInput);\n\n if ($('.popover .cart').length || $('.cart-page').length) {\n $qtyInput.trigger('change');\n }\n });\n }\n};\n","/* eslint-disable radix */\n'use strict';\nvar Glide = require('@glidejs/glide/dist/glide.js');\n\nconst createSlideLength = function createSlideLength(glide, Components, Events) {\n return {\n mount() {\n Events.emit('slider.length', Components.Sizes.length);\n }\n };\n};\n\n/**\n * Sets the aria-live property on current slide\n * @param {*} glide - instance of glidejs\n *\n */\nconst setAriaAttributes = (glide) => {\n glide.on('run', () => {\n const thisGlideSlide = glide._c.Html.slides[glide.index]; // eslint-disable-line\n glide._c.Html.slides.forEach((slide) => { // eslint-disable-line\n slide.setAttribute('aria-live', 'off');\n });\n thisGlideSlide.setAttribute('aria-live', 'polite');\n });\n};\n\n/**\n * Display the returned message.\n * @param {string} galleryScroller - data returned from the server's ajax call\n * @param {string} galleryItemSize - button that was clicked for contact us sign-up\n */\nfunction scrollToNextPage(galleryScroller, galleryItemSize) {\n var items = galleryScroller.children;\n if (galleryScroller.scrollTop >= galleryItemSize) {\n galleryScroller.append(items[0]);\n }\n galleryScroller.scrollBy(0, galleryItemSize);\n}\n\n/**\n * Display the returned message.\n * @param {string} galleryScroller - data returned from the server's ajax call\n * @param {string} galleryItemSize - button that was clicked for contact us sign-up\n */\nfunction scrollToPrevPage(galleryScroller, galleryItemSize) {\n var items = galleryScroller.children;\n if (galleryScroller.scrollTop <= galleryItemSize) {\n galleryScroller.prepend(items[items.length - 1]);\n }\n galleryScroller.scrollBy(0, -galleryItemSize);\n}\n\n/**\n * Display the returned message.\n * @param {string} el - data returned from the server's ajax call\n * @param {string} linkCarousel - data returned from the server's ajax call\n * @param {string} activeSlide - data returned from the server's ajax call\n */\nfunction setActive(el, linkCarousel, activeSlide) {\n var scrollTop = el.scrollTop;\n var slides = el.children;\n var slideHeight = 0;\n for (var j = 0; j < slides.length; j++) {\n slides[j].classList.remove('active');\n slides[j].setAttribute('aria-live', 'off');\n }\n for (var i = 0; i < slides.length; i++) {\n slideHeight = slides[i].clientHeight;\n if (activeSlide && !slides[activeSlide].classList.contains('active')) {\n slides[activeSlide].classList.add('active');\n slides[activeSlide].setAttribute('aria-live', 'polite');\n linkCarousel.update({\n startAt: parseInt(slides[activeSlide].dataset.slideId)\n });\n } else if ((Math.abs(slides[i].offsetTop - scrollTop) <= slideHeight) && !activeSlide) {\n slides[i].classList.add('active');\n linkCarousel.update({\n startAt: slides[i].dataset.slideId\n });\n }\n }\n}\n\nvar glideFunction = {\n sameClassMultiCarousel: function (className, config) {\n var sliders = document.querySelectorAll(className);\n if (sliders.length > 0) {\n for (var i = 0; i < sliders.length; i++) {\n var glide = new Glide(sliders[i], config);\n this.checkGlideDisableLength(glide);\n this.glideArrowControll(glide, sliders[i]);\n this.removeExtraBullets(glide, sliders[i]);\n glide.mount({\n createSlideLength\n });\n }\n }\n },\n\n carouselInit: function (className, config) {\n var glide = new Glide(className, config);\n this.glideArrowControll(glide, document.querySelector(className));\n return glide;\n },\n\n syncGlide: function (master, slave) {\n master.on('run', function (e) {\n if (e.steps === 0 && e.direction !== '=') {\n slave.go(e.direction);\n if (slave.settings.bound) {\n var slideLength = $(slave.selector + ' .glide__slide').length;\n if ((slideLength - slave.settings.perView) < master.index) {\n slave.update({\n startAt: master.index,\n focusAt: (slave.settings.perView - (slideLength - (parseInt(master.index))))\n });\n } else {\n slave.update({\n startAt: master.index,\n focusAt: 0\n });\n }\n }\n }\n });\n },\n\n thumbCarousel: function (carousel1, carousel2) {\n this.syncGlide(carousel1, carousel2);\n this.syncGlide(carousel2, carousel1);\n },\n\n clickGlide: function (className, mainSlide, thumbSlide) {\n var slideLength = $(thumbSlide.selector + ' .glide__slide').length;\n $(className + ' .glide-click').click(function (event) {\n var val = event.target.dataset.count;\n if (!mainSlide.disabled && !thumbSlide.disabled) {\n mainSlide.go('=' + val);\n thumbSlide.go('=' + val);\n if (thumbSlide.settings.bound) {\n if ((slideLength - thumbSlide.settings.perView) < parseInt(val)) {\n thumbSlide.update({\n startAt: val,\n focusAt: (thumbSlide.settings.perView - (slideLength - (parseInt(val))))\n });\n } else {\n thumbSlide.update({\n startAt: val,\n focusAt: 0\n });\n }\n }\n }\n });\n },\n\n checkGlideMounted: function (className) {\n var checkClassName = $(className);\n if (checkClassName.hasClass('glide--ltr') || checkClassName.hasClass('glide--carousel') || checkClassName.hasClass('glide--swipeable')) {\n return true;\n }\n return false;\n },\n\n initGlideByElement(element, config) {\n return new Glide(element, config).mount();\n },\n\n camelbakPDCarouselSameClassInit: function (className, config) {\n var sliders = document.querySelectorAll(className);\n for (var i = 0; i < sliders.length; i++) {\n var glide = new Glide(sliders[i], config);\n $('#' + sliders[i].id + ' .arrows').removeClass('d-none');\n this.getSlideLength(glide, sliders[i]);\n this.camelbakSetUpdatedSlideValue(glide, sliders[i]);\n this.glideArrowControll(glide, sliders[i]);\n glide.mount({\n createSlideLength\n });\n }\n },\n\n getSlideLength: function (glide, currentSlide) {\n glide.on('slider.length', (length) => {\n currentSlide.querySelector('.glide-pageinfo').setAttribute('data-slidecount', length);\n });\n },\n\n camelbakSetUpdatedSlideValue: function (glide, currentSlide) {\n glide.on(['mount.after', 'run'], () => {\n const currentIndex = glide.index + 1;\n var currentDiv = currentSlide.querySelector('.glide-pageinfo');\n var length = currentIndex + '/' + currentDiv.getAttribute('data-slidecount');\n $(currentDiv).html(length);\n });\n },\n\n getDynamicBullets: function (className, config, wrapperClassName, isArrow) {\n var sliders = document.querySelectorAll(className);\n for (var i = 0; i < sliders.length; i++) {\n var glide = new Glide(sliders[i], config);\n this.getBulletsCount(glide, sliders[i], wrapperClassName, isArrow);\n glide.mount({\n createSlideLength\n });\n setAriaAttributes(glide);\n }\n },\n getBulletsCount: function (glide, currentSlide, wrapperClassName, isArrow) {\n if (isArrow) {\n glide.on('slider.length', (length) => {\n var bulletDiv = currentSlide.querySelector(wrapperClassName);\n bulletDiv.setAttribute('data-glide-el', 'controls');\n if (length > 1) {\n var buttonDiv = '';\n for (let index = 0; index < length; index++) {\n buttonDiv += '';\n }\n var html = '
      ' +\n '
      ' + buttonDiv +\n '
      ' +\n '
      \">
      ' +\n '
      ';\n bulletDiv.innerHTML = html;\n glide.destroy();\n glide.mount();\n }\n });\n } else {\n glide.on('slider.length', (length) => {\n var bulletDiv = currentSlide.querySelector(wrapperClassName);\n if (length > 1) {\n var buttonDiv = '';\n for (let index = 0; index < length; index++) {\n buttonDiv += '';\n }\n var html =\n '
      ' + buttonDiv +\n '
      ' +\n '
      ';\n bulletDiv.innerHTML = html;\n glide.destroy();\n glide.mount();\n }\n });\n }\n },\n getGlideSlideLength: createSlideLength,\n checkGlideDisableLength: function (glide) {\n glide.on('slider.length', (length) => {\n if (length <= glide.settings.perView) {\n glide.update({\n type: 'slider',\n rewind: false\n });\n glide.disable();\n if ($(glide.selector).find('.glide__arrow--left').length && $(glide.selector).find('.glide__arrow--right').length) {\n $(glide.selector).find('.glide__arrow--left').addClass('d-none');\n $(glide.selector).find('.glide__arrow--right').addClass('d-none');\n $(glide.selector).find('.glide__bullets').addClass('d-none');\n }\n }\n });\n },\n glideArrowControll: function (glide, currentSlider) {\n const slide = $(currentSlider);\n var length;\n if (slide.find('.glide__arrow--left').length && slide.find('.glide__arrow--right').length) {\n const glideArrowLeft = slide.find('.glide__arrow--left');\n const glideArrowRight = slide.find('.glide__arrow--right');\n glideArrowLeft.on('click', function () {\n glide.go('<');\n });\n glideArrowRight.on('click', function () {\n glide.go('>');\n });\n if (glide.settings.bound && !glide.settings.rewind) {\n Object.keys(glide.settings.breakpoints).forEach((e)=> {\n if (window.matchMedia('(max-width: ' + e + 'px)').matches && glide.settings.breakpoints[e].perView) {\n length = glide.settings.breakpoints[e].perView;\n return;\n // eslint-disable-next-line no-else-return\n } else {\n length = glide.settings.perView;\n }\n });\n glide.on('mount.after', function () {\n if (glide.index === 0) {\n glideArrowLeft.addClass('glide__arrow--disabled');\n }\n });\n glide.on('run.after', function () {\n if (glide.index === 0 && !glideArrowLeft.hasClass('glide__arrow--disabled')) {\n glideArrowLeft.addClass('glide__arrow--disabled');\n } else if (glide.index !== 0 && glideArrowLeft.hasClass('glide__arrow--disabled')) {\n glideArrowLeft.removeClass('glide__arrow--disabled');\n }\n\n if ((glide.index === (currentSlider.querySelectorAll('.glide__slide').length) - length) && !glideArrowRight.hasClass('glide__arrow--disabled')) {\n glideArrowRight.addClass('glide__arrow--disabled');\n } else if ((glide.index !== (currentSlider.querySelectorAll('.glide__slide').length) - length) && glideArrowRight.hasClass('glide__arrow--disabled')) {\n glideArrowRight.removeClass('glide__arrow--disabled');\n }\n });\n } else if (!glide.settings.rewind) {\n glide.on('mount.after', function () {\n if (glide.index === 0) {\n glideArrowLeft.addClass('glide__arrow--disabled');\n }\n });\n glide.on('run.after', function () {\n if (glide.index === 0 && !glideArrowLeft.hasClass('glide__arrow--disabled')) {\n glideArrowLeft.addClass('glide__arrow--disabled');\n } else if (glide.index !== 0 && glideArrowLeft.hasClass('glide__arrow--disabled')) {\n glideArrowLeft.removeClass('glide__arrow--disabled');\n }\n\n if ((glide.index === (currentSlider.querySelectorAll('.glide__slide').length) - 1) && !glideArrowRight.hasClass('glide__arrow--disabled')) {\n glideArrowRight.addClass('glide__arrow--disabled');\n } else if ((glide.index !== (currentSlider.querySelectorAll('.glide__slide').length) - 1) && glideArrowRight.hasClass('glide__arrow--disabled')) {\n glideArrowRight.removeClass('glide__arrow--disabled');\n }\n });\n }\n }\n },\n verticalCarousel: function (container, linkCarousel) {\n if (container) {\n var galleryScroller = container.querySelector('.vertical-slides');\n var galleryItemSize = galleryScroller.querySelector('.slides-item').clientHeight;\n var prev = container.querySelector('.btn.prev');\n var next = container.querySelector('.btn.next');\n prev.addEventListener('click', () => scrollToPrevPage(galleryScroller, galleryItemSize));\n next.addEventListener('click', () => scrollToNextPage(galleryScroller, galleryItemSize));\n if (container.querySelectorAll('.slides-item').length > 5) {\n prev.classList.remove('d-none');\n next.classList.remove('d-none');\n }\n galleryScroller.addEventListener('scroll', function () {\n var el = this;\n setActive(el, linkCarousel);\n });\n if (galleryScroller.dataset.sliderBind && linkCarousel) {\n linkCarousel.on('run.after', function () {\n setActive(galleryScroller, linkCarousel, linkCarousel.index);\n });\n var allSlides = galleryScroller.querySelectorAll('.slides-item');\n allSlides.forEach(item => item.addEventListener('click', function () {\n linkCarousel.update({\n startAt: this.dataset.slideId\n });\n var i = Array.prototype.indexOf.call(galleryScroller.children, this);\n setActive(galleryScroller, linkCarousel, i);\n }));\n }\n }\n },\n removeExtraBullets: function (glide, currentSlide) {\n if (!($(currentSlide).hasClass('glide--ltr') || $(currentSlide).hasClass('glide--carousel') || $(currentSlide).hasClass('glide--swipeable'))) {\n var length;\n Object.keys(glide.settings.breakpoints).forEach((e)=> {\n if (window.matchMedia('(max-width: ' + e + 'px)').matches && glide.settings.breakpoints[e].perView) {\n length = glide.settings.breakpoints[e].perView;\n return;\n // eslint-disable-next-line no-else-return\n } else {\n length = glide.settings.perView;\n }\n });\n if (glide.settings.bound && !glide.settings.rewind) {\n var bullets = currentSlide.querySelector('.glide__bullets');\n if ($(bullets).children().length > length) {\n for (var i = 1; i < (length); i++) {\n $(bullets.children).last().remove();\n }\n }\n }\n }\n }\n};\n\nmodule.exports = glideFunction;\n","'use strict';\n\nvar PinchZoomLibrary = require('pinch-zoom-js/src/pinch-zoom').default;\n\n// eslint-disable-next-line valid-jsdoc\n/**\n * Display the returned message.\n * @param {Object} slider- glide slider\n */\nfunction disableZoom(slider) {\n if (slider.length) {\n for (let i = 0; i < slider.length; i++) {\n if (slider[i] && !slider[i].disabled) {\n slider[i].disable();\n }\n }\n }\n}\n\n/**\n * Display the returned message.\n * @param {Object} slider - glide slider\n */\nfunction enableZoom(slider) {\n if (slider.length) {\n for (let i = 0; i < slider.length; i++) {\n if (slider[i] && slider[i].disabled) {\n slider[i].enable();\n }\n }\n }\n}\n\n/**\n * Display the returned message.\n * @param {Object} pzpdimg -image element\n */\nfunction initPinchZoom(pzpdimg) {\n var imgHeight = pzpdimg.offsetHeight;\n var imageWidth = pzpdimg.offsetWidth;\n\n var pz = new PinchZoomLibrary(pzpdimg, {\n draggableUnzoomed: false,\n minZoom: 0.9,\n zoomOutFactor: 0,\n onDragStart: function (o, e) {\n e.preventDefault();\n },\n onZoomUpdate: function (obj) {\n pz.currentScale = obj.zoomFactor;\n if (pz.currentScale > 1) {\n // eslint-disable-next-line no-undef\n disableZoom(pdpGlide);\n } else if (pz.currentScale === 0.9) {\n // eslint-disable-next-line no-undef\n enableZoom(pdpGlide);\n obj.options.zoomOutFactor = 1;\n } else {\n // eslint-disable-next-line no-undef\n enableZoom(pdpGlide);\n obj.options.zoomOutFactor = 1;\n }\n obj.update();\n }\n });\n if (pzpdimg.parentElement.offsetHeight === 0 && imgHeight !== 0) {\n pzpdimg.closest('.slide-link').style.height = imgHeight + 'px';\n pzpdimg.parentElement.style.height = imgHeight + 'px';\n }\n if (pzpdimg.parentElement.offsetWidth === 0 && imageWidth !== 0) {\n pzpdimg.closest('.slide-link').style.width = '100%';\n pzpdimg.parentElement.style.width = imageWidth + 'px';\n }\n pz.enable();\n}\n\nmodule.exports = {\n pinchZoomPdp: function (glideSlider, isCampchef, isActionSports) {\n window.pdpGlide = glideSlider;\n var pdImage = document.querySelectorAll('.pinch-zoom');\n if (isCampchef) {\n pdImage.forEach(element => {\n element.onload = function () {\n initPinchZoom(element);\n };\n });\n } else if (isActionSports) {\n pdImage.forEach(element => {\n if (element.complete) {\n element.closest('.slide-link').style.height = element.closest('.slide-link').offsetHeight + 'px';\n initPinchZoom(element);\n } else {\n element.onload = function () {\n element.closest('.slide-link').style.height = element.closest('.slide-link').offsetHeight + 'px';\n initPinchZoom(element);\n };\n }\n });\n } else {\n pdImage.forEach(element => {\n if (element.complete) {\n initPinchZoom(element);\n } else {\n element.onload = function () {\n initPinchZoom(element);\n };\n }\n });\n }\n }\n};\n\n","'use strict';\n\nvar utils = require('./utils');\n\n/**\n * disables the zoom and then reenables so iphone doesn't zoom in\n */\nfunction zoomHandle() {\n utils.disableZoom();\n setTimeout(function () {\n utils.enableZoom();\n }, 500);\n}\n\n/**\n * initializes zoom handle form select via touch event\n */\nfunction init() {\n $('body').on('touchstart', 'select, input, textarea', function () {\n zoomHandle();\n });\n}\n\nmodule.exports = init;\n","'use strict';\n\n/* Use this file to generate generic modals, just pass in the rendered HTML */\n\n/**\n * Generates the modal based on an ajax response\n * @param {HTML} renderedTemplate - the given modal HTML (must be contained in one wrapping element that has a unique id)\n * @return {Object} $modal - jQuery object of the newly generated modal.\n */\nfunction generateModal(renderedTemplate) {\n var $body = $('body');\n $body.spinner().start();\n\n var parsedHTML = $.parseHTML($.trim(renderedTemplate));\n $body.append(parsedHTML);\n\n var $modal = $body.find(parsedHTML);\n\n // Remove any previous generated modals with this ID value (except this one)\n $('#' + $modal.attr('id') + ':not(:last-child)').remove();\n\n $modal.modal('show');\n $.spinner().stop();\n\n return $modal;\n}\n\nmodule.exports.create = generateModal;\n","'use strict';\n\nvar baseUtils = require('lyonscg/util/utils');\n\nvar utils = {\n getHost: function (href) {\n var l = document.createElement('a');\n l.href = href;\n return l.hostname;\n },\n /**\n * Disables Zoom on mobile\n */\n disableZoom: function () {\n $('head meta[name=viewport]').remove();\n $('head').prepend('');\n },\n\n /**\n * Enables Zoom on mobile\n */\n enableZoom: function () {\n $('head meta[name=viewport]').remove();\n $('head').prepend('');\n },\n /*\n * Trims text or returns empty string\n */\n trim: function (text) {\n return typeof (text) === 'string' ? text.trim() : '';\n }\n};\n\nmodule.exports = $.extend({}, baseUtils, utils);\n","'use strict';\n\nvar base = require('base/components/miniCart');\n\n/**\n * appends params to a url\n * @param {string} data - data returned from the server's ajax call\n */\nfunction displayMessageAndRemoveFromCart(data) {\n $.spinner().stop();\n var status = data.success ? 'alert-success' : 'alert-danger';\n\n if ($('.add-to-wishlist-messages').length === 0) {\n $('body').append('
      ');\n }\n $('.add-to-wishlist-messages')\n .append('
      ' + data.msg + '
      ');\n\n setTimeout(function () {\n $('.add-to-wishlist-messages').remove();\n }, 1500);\n var $targetElement = $('a[data-pid=\"' + data.pid + '\"]').closest('.product-info').find('.remove-product');\n var itemToMove = {\n actionUrl: $targetElement.data('action'),\n productID: $targetElement.data('pid'),\n productName: $targetElement.data('name'),\n uuid: $targetElement.data('uuid')\n };\n $('body').trigger('afterRemoveFromCart', itemToMove);\n setTimeout(function () {\n $('.cart.cart-page #removeProductModal').modal();\n }, 2000);\n}\n/**\n * move items from Cart to wishlist\n * returns json object with success or error message\n */\nfunction moveToWishlist() {\n $('body').on('click', '.product-move .move', function (e) {\n e.preventDefault();\n var url = $(this).attr('href');\n var pid = $(this).data('pid');\n var optionId = $(this).closest('.product-info').find('.lineItem-options-values').data('option-id');\n var optionVal = $(this).closest('.product-info').find('.lineItem-options-values').data('value-id');\n optionId = optionId || null;\n optionVal = optionVal || null;\n if (!url || !pid) {\n return;\n }\n\n $.spinner().start();\n $.ajax({\n url: url,\n type: 'post',\n dataType: 'json',\n data: {\n pid: pid,\n optionId: optionId,\n optionVal: optionVal\n },\n success: function (data) {\n displayMessageAndRemoveFromCart(data);\n },\n error: function (err) {\n displayMessageAndRemoveFromCart(err);\n }\n });\n });\n}\n\nmodule.exports = {\n base: base,\n moveToWishlist: moveToWishlist\n};\n","'use strict';\nvar glideFunction = {\n pdp: {\n type: 'slider',\n perView: 1,\n gap: 10,\n peek: {\n before: 200,\n after: 0\n },\n breakpoints: {\n 767: {\n gap: 0,\n peek: {\n before: 0,\n after: 0\n }\n }\n }\n },\n\n bellPdp: {\n type: 'slider',\n perView: 1,\n gap: 10,\n rewind: false,\n peek: {\n before: 200,\n after: 0\n },\n breakpoints: {\n 767: {\n gap: 0,\n peek: {\n before: 0,\n after: 0\n }\n }\n }\n },\n\n plpQuickview: {\n type: 'slider',\n rewind: false,\n perView: 1,\n gap: 0\n },\n\n recommendation: {\n type: 'slider',\n rewind: false,\n perView: 4,\n bound: true,\n gap: 20,\n breakpoints: {\n 767: {\n perView: 2\n }\n }\n },\n\n scrollThumb: {\n perView: 5,\n type: 'slider',\n rewind: false,\n bound: true,\n focusAt: 0,\n breakpoints: {\n 767: {\n perView: 3.5,\n gap: 0\n }\n }\n },\n\n quickViewScrollThumb: {\n perView: 5,\n type: 'slider',\n rewind: false,\n bound: true,\n focusAt: 0,\n breakpoints: {\n 767: {\n perView: 4,\n gap: 0\n }\n }\n },\n\n tech: {\n type: 'carousel',\n perView: 3,\n breakpoints: {\n 767: {\n perView: 1\n },\n 991: {\n perView: 2\n }\n }\n },\n\n video: {\n type: 'slider',\n perView: 1,\n rewind: false,\n bound: true,\n breakpoints: {\n 767: {\n perView: 1\n }\n }\n },\n\n productRecommendationTile: {\n type: 'slider',\n rewind: false,\n perView: 2,\n gap: 0,\n bound: true,\n breakpoints: {\n 991: {\n perView: 2\n }\n }\n }\n};\n\nmodule.exports = glideFunction;\n","\"use strict\";\n\nrequire(\"./noConflict\");\n\nvar _global = _interopRequireDefault(require(\"core-js/library/fn/global\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nif (_global[\"default\"]._babelPolyfill && typeof console !== \"undefined\" && console.warn) {\n console.warn(\"@babel/polyfill is loaded more than once on this page. This is probably not desirable/intended \" + \"and may have consequences if different versions of the polyfills are applied sequentially. \" + \"If you do need to load the polyfill more than once, use @babel/polyfill/noConflict \" + \"instead to bypass the warning.\");\n}\n\n_global[\"default\"]._babelPolyfill = true;","\"use strict\";\n\nrequire(\"core-js/es6\");\n\nrequire(\"core-js/fn/array/includes\");\n\nrequire(\"core-js/fn/array/flat-map\");\n\nrequire(\"core-js/fn/string/pad-start\");\n\nrequire(\"core-js/fn/string/pad-end\");\n\nrequire(\"core-js/fn/string/trim-start\");\n\nrequire(\"core-js/fn/string/trim-end\");\n\nrequire(\"core-js/fn/symbol/async-iterator\");\n\nrequire(\"core-js/fn/object/get-own-property-descriptors\");\n\nrequire(\"core-js/fn/object/values\");\n\nrequire(\"core-js/fn/object/entries\");\n\nrequire(\"core-js/fn/promise/finally\");\n\nrequire(\"core-js/web\");\n\nrequire(\"regenerator-runtime/runtime\");","/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nvar runtime = (function (exports) {\n \"use strict\";\n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n function define(obj, key, value) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n return obj[key];\n }\n try {\n // IE 8 has a broken Object.defineProperty that only works on DOM objects.\n define({}, \"\");\n } catch (err) {\n define = function(obj, key, value) {\n return obj[key] = value;\n };\n }\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n exports.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n define(IteratorPrototype, iteratorSymbol, function () {\n return this;\n });\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = GeneratorFunctionPrototype;\n define(Gp, \"constructor\", GeneratorFunctionPrototype);\n define(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction);\n GeneratorFunction.displayName = define(\n GeneratorFunctionPrototype,\n toStringTagSymbol,\n \"GeneratorFunction\"\n );\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function(method) {\n define(prototype, method, function(arg) {\n return this._invoke(method, arg);\n });\n });\n }\n\n exports.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n exports.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n define(genFun, toStringTagSymbol, \"GeneratorFunction\");\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n exports.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator, PromiseImpl) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return PromiseImpl.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return PromiseImpl.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new PromiseImpl(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n define(AsyncIterator.prototype, asyncIteratorSymbol, function () {\n return this;\n });\n exports.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {\n if (PromiseImpl === void 0) PromiseImpl = Promise;\n\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList),\n PromiseImpl\n );\n\n return exports.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n // Note: [\"return\"] must be used for ES3 parsing compatibility.\n if (delegate.iterator[\"return\"]) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n define(Gp, toStringTagSymbol, \"Generator\");\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n define(Gp, iteratorSymbol, function() {\n return this;\n });\n\n define(Gp, \"toString\", function() {\n return \"[object Generator]\";\n });\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n exports.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n exports.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n\n // Regardless of whether this script is executing as a CommonJS module\n // or not, return the runtime object so that we can declare the variable\n // regeneratorRuntime in the outer scope, which allows this module to be\n // injected easily by `bin/regenerator --include-runtime script.js`.\n return exports;\n\n}(\n // If this script is executing as a CommonJS module, use module.exports\n // as the regeneratorRuntime namespace. Otherwise create a new empty\n // object. Either way, the resulting object will be used to initialize\n // the regeneratorRuntime variable at the top of this file.\n typeof module === \"object\" ? module.exports : {}\n));\n\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n // This module should not be running in strict mode, so the above\n // assignment should always work unless something is misconfigured. Just\n // in case runtime.js accidentally runs in strict mode, in modern engines\n // we can explicitly access globalThis. In older engines we can escape\n // strict mode using a global Function call. This could conceivably fail\n // if a Content Security Policy forbids using Function, but in that case\n // the proper solution is to fix the accidental strict mode problem. If\n // you've misconfigured your bundler to force strict mode and applied a\n // CSP to forbid Function, and you're not willing to fix either of those\n // problems, please detail your unique predicament in a GitHub issue.\n if (typeof globalThis === \"object\") {\n globalThis.regeneratorRuntime = runtime;\n } else {\n Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n }\n}\n","/*!\n * Glide.js v3.6.0\n * (c) 2013-2022 Jędrzej Chałubek (https://github.com/jedrzejchalubek/)\n * Released under the MIT License.\n */\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Glide = factory());\n})(this, (function () { 'use strict';\n\n function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n _typeof = function (obj) {\n return typeof obj;\n };\n } else {\n _typeof = function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n };\n }\n\n return _typeof(obj);\n }\n\n function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n }\n\n function _defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, descriptor.key, descriptor);\n }\n }\n\n function _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n return Constructor;\n }\n\n function _inherits(subClass, superClass) {\n if (typeof superClass !== \"function\" && superClass !== null) {\n throw new TypeError(\"Super expression must either be null or a function\");\n }\n\n subClass.prototype = Object.create(superClass && superClass.prototype, {\n constructor: {\n value: subClass,\n writable: true,\n configurable: true\n }\n });\n if (superClass) _setPrototypeOf(subClass, superClass);\n }\n\n function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\n }\n\n function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\n\n return _setPrototypeOf(o, p);\n }\n\n function _isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n if (Reflect.construct.sham) return false;\n if (typeof Proxy === \"function\") return true;\n\n try {\n Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n return true;\n } catch (e) {\n return false;\n }\n }\n\n function _assertThisInitialized(self) {\n if (self === void 0) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n\n return self;\n }\n\n function _possibleConstructorReturn(self, call) {\n if (call && (typeof call === \"object\" || typeof call === \"function\")) {\n return call;\n } else if (call !== void 0) {\n throw new TypeError(\"Derived constructors may only return object or undefined\");\n }\n\n return _assertThisInitialized(self);\n }\n\n function _createSuper(Derived) {\n var hasNativeReflectConstruct = _isNativeReflectConstruct();\n\n return function _createSuperInternal() {\n var Super = _getPrototypeOf(Derived),\n result;\n\n if (hasNativeReflectConstruct) {\n var NewTarget = _getPrototypeOf(this).constructor;\n\n result = Reflect.construct(Super, arguments, NewTarget);\n } else {\n result = Super.apply(this, arguments);\n }\n\n return _possibleConstructorReturn(this, result);\n };\n }\n\n function _superPropBase(object, property) {\n while (!Object.prototype.hasOwnProperty.call(object, property)) {\n object = _getPrototypeOf(object);\n if (object === null) break;\n }\n\n return object;\n }\n\n function _get() {\n if (typeof Reflect !== \"undefined\" && Reflect.get) {\n _get = Reflect.get;\n } else {\n _get = function _get(target, property, receiver) {\n var base = _superPropBase(target, property);\n\n if (!base) return;\n var desc = Object.getOwnPropertyDescriptor(base, property);\n\n if (desc.get) {\n return desc.get.call(arguments.length < 3 ? target : receiver);\n }\n\n return desc.value;\n };\n }\n\n return _get.apply(this, arguments);\n }\n\n var defaults = {\n /**\n * Type of the movement.\n *\n * Available types:\n * `slider` - Rewinds slider to the start/end when it reaches the first or last slide.\n * `carousel` - Changes slides without starting over when it reaches the first or last slide.\n *\n * @type {String}\n */\n type: 'slider',\n\n /**\n * Start at specific slide number defined with zero-based index.\n *\n * @type {Number}\n */\n startAt: 0,\n\n /**\n * A number of slides visible on the single viewport.\n *\n * @type {Number}\n */\n perView: 1,\n\n /**\n * Focus currently active slide at a specified position in the track.\n *\n * Available inputs:\n * `center` - Current slide will be always focused at the center of a track.\n * `0,1,2,3...` - Current slide will be focused on the specified zero-based index.\n *\n * @type {String|Number}\n */\n focusAt: 0,\n\n /**\n * A size of the gap added between slides.\n *\n * @type {Number}\n */\n gap: 10,\n\n /**\n * Change slides after a specified interval. Use `false` for turning off autoplay.\n *\n * @type {Number|Boolean}\n */\n autoplay: false,\n\n /**\n * Stop autoplay on mouseover event.\n *\n * @type {Boolean}\n */\n hoverpause: true,\n\n /**\n * Allow for changing slides with left and right keyboard arrows.\n *\n * @type {Boolean}\n */\n keyboard: true,\n\n /**\n * Stop running `perView` number of slides from the end. Use this\n * option if you don't want to have an empty space after\n * a slider. Works only with `slider` type and a\n * non-centered `focusAt` setting.\n *\n * @type {Boolean}\n */\n bound: false,\n\n /**\n * Minimal swipe distance needed to change the slide. Use `false` for turning off a swiping.\n *\n * @type {Number|Boolean}\n */\n swipeThreshold: 80,\n\n /**\n * Minimal mouse drag distance needed to change the slide. Use `false` for turning off a dragging.\n *\n * @type {Number|Boolean}\n */\n dragThreshold: 120,\n\n /**\n * A number of slides moved on single swipe.\n *\n * Available types:\n * `` - Moves slider by one slide per swipe\n * `|` - Moves slider between views per swipe (number of slides defined in `perView` options)\n *\n * @type {String}\n */\n perSwipe: '',\n\n /**\n * Moving distance ratio of the slides on a swiping and dragging.\n *\n * @type {Number}\n */\n touchRatio: 0.5,\n\n /**\n * Angle required to activate slides moving on swiping or dragging.\n *\n * @type {Number}\n */\n touchAngle: 45,\n\n /**\n * Duration of the animation in milliseconds.\n *\n * @type {Number}\n */\n animationDuration: 400,\n\n /**\n * Allows looping the `slider` type. Slider will rewind to the first/last slide when it's at the start/end.\n *\n * @type {Boolean}\n */\n rewind: true,\n\n /**\n * Duration of the rewinding animation of the `slider` type in milliseconds.\n *\n * @type {Number}\n */\n rewindDuration: 800,\n\n /**\n * Easing function for the animation.\n *\n * @type {String}\n */\n animationTimingFunc: 'cubic-bezier(.165, .840, .440, 1)',\n\n /**\n * Wait for the animation to finish until the next user input can be processed\n *\n * @type {boolean}\n */\n waitForTransition: true,\n\n /**\n * Throttle costly events at most once per every wait milliseconds.\n *\n * @type {Number}\n */\n throttle: 10,\n\n /**\n * Moving direction mode.\n *\n * Available inputs:\n * - 'ltr' - left to right movement,\n * - 'rtl' - right to left movement.\n *\n * @type {String}\n */\n direction: 'ltr',\n\n /**\n * The distance value of the next and previous viewports which\n * have to peek in the current view. Accepts number and\n * pixels as a string. Left and right peeking can be\n * set up separately with a directions object.\n *\n * For example:\n * `100` - Peek 100px on the both sides.\n * { before: 100, after: 50 }` - Peek 100px on the left side and 50px on the right side.\n *\n * @type {Number|String|Object}\n */\n peek: 0,\n\n /**\n * Defines how many clones of current viewport will be generated.\n *\n * @type {Number}\n */\n cloningRatio: 1,\n\n /**\n * Collection of options applied at specified media breakpoints.\n * For example: display two slides per view under 800px.\n * `{\n * '800px': {\n * perView: 2\n * }\n * }`\n */\n breakpoints: {},\n\n /**\n * Collection of internally used HTML classes.\n *\n * @todo Refactor `slider` and `carousel` properties to single `type: { slider: '', carousel: '' }` object\n * @type {Object}\n */\n classes: {\n swipeable: 'glide--swipeable',\n dragging: 'glide--dragging',\n direction: {\n ltr: 'glide--ltr',\n rtl: 'glide--rtl'\n },\n type: {\n slider: 'glide--slider',\n carousel: 'glide--carousel'\n },\n slide: {\n clone: 'glide__slide--clone',\n active: 'glide__slide--active'\n },\n arrow: {\n disabled: 'glide__arrow--disabled'\n },\n nav: {\n active: 'glide__bullet--active'\n }\n }\n };\n\n /**\n * Outputs warning message to the bowser console.\n *\n * @param {String} msg\n * @return {Void}\n */\n function warn(msg) {\n console.error(\"[Glide warn]: \".concat(msg));\n }\n\n /**\n * Converts value entered as number\n * or string to integer value.\n *\n * @param {String} value\n * @returns {Number}\n */\n function toInt(value) {\n return parseInt(value);\n }\n /**\n * Converts value entered as number\n * or string to flat value.\n *\n * @param {String} value\n * @returns {Number}\n */\n\n function toFloat(value) {\n return parseFloat(value);\n }\n /**\n * Indicates whether the specified value is a string.\n *\n * @param {*} value\n * @return {Boolean}\n */\n\n function isString(value) {\n return typeof value === 'string';\n }\n /**\n * Indicates whether the specified value is an object.\n *\n * @param {*} value\n * @return {Boolean}\n *\n * @see https://github.com/jashkenas/underscore\n */\n\n function isObject(value) {\n var type = _typeof(value);\n\n return type === 'function' || type === 'object' && !!value; // eslint-disable-line no-mixed-operators\n }\n /**\n * Indicates whether the specified value is a function.\n *\n * @param {*} value\n * @return {Boolean}\n */\n\n function isFunction(value) {\n return typeof value === 'function';\n }\n /**\n * Indicates whether the specified value is undefined.\n *\n * @param {*} value\n * @return {Boolean}\n */\n\n function isUndefined(value) {\n return typeof value === 'undefined';\n }\n /**\n * Indicates whether the specified value is an array.\n *\n * @param {*} value\n * @return {Boolean}\n */\n\n function isArray(value) {\n return value.constructor === Array;\n }\n\n /**\n * Creates and initializes specified collection of extensions.\n * Each extension receives access to instance of glide and rest of components.\n *\n * @param {Object} glide\n * @param {Object} extensions\n *\n * @returns {Object}\n */\n\n function mount(glide, extensions, events) {\n var components = {};\n\n for (var name in extensions) {\n if (isFunction(extensions[name])) {\n components[name] = extensions[name](glide, components, events);\n } else {\n warn('Extension must be a function');\n }\n }\n\n for (var _name in components) {\n if (isFunction(components[_name].mount)) {\n components[_name].mount();\n }\n }\n\n return components;\n }\n\n /**\n * Defines getter and setter property on the specified object.\n *\n * @param {Object} obj Object where property has to be defined.\n * @param {String} prop Name of the defined property.\n * @param {Object} definition Get and set definitions for the property.\n * @return {Void}\n */\n function define(obj, prop, definition) {\n Object.defineProperty(obj, prop, definition);\n }\n /**\n * Sorts aphabetically object keys.\n *\n * @param {Object} obj\n * @return {Object}\n */\n\n function sortKeys(obj) {\n return Object.keys(obj).sort().reduce(function (r, k) {\n r[k] = obj[k];\n return r[k], r;\n }, {});\n }\n /**\n * Merges passed settings object with default options.\n *\n * @param {Object} defaults\n * @param {Object} settings\n * @return {Object}\n */\n\n function mergeOptions(defaults, settings) {\n var options = Object.assign({}, defaults, settings); // `Object.assign` do not deeply merge objects, so we\n // have to do it manually for every nested object\n // in options. Although it does not look smart,\n // it's smaller and faster than some fancy\n // merging deep-merge algorithm script.\n\n if (settings.hasOwnProperty('classes')) {\n options.classes = Object.assign({}, defaults.classes, settings.classes);\n\n if (settings.classes.hasOwnProperty('direction')) {\n options.classes.direction = Object.assign({}, defaults.classes.direction, settings.classes.direction);\n }\n\n if (settings.classes.hasOwnProperty('type')) {\n options.classes.type = Object.assign({}, defaults.classes.type, settings.classes.type);\n }\n\n if (settings.classes.hasOwnProperty('slide')) {\n options.classes.slide = Object.assign({}, defaults.classes.slide, settings.classes.slide);\n }\n\n if (settings.classes.hasOwnProperty('arrow')) {\n options.classes.arrow = Object.assign({}, defaults.classes.arrow, settings.classes.arrow);\n }\n\n if (settings.classes.hasOwnProperty('nav')) {\n options.classes.nav = Object.assign({}, defaults.classes.nav, settings.classes.nav);\n }\n }\n\n if (settings.hasOwnProperty('breakpoints')) {\n options.breakpoints = Object.assign({}, defaults.breakpoints, settings.breakpoints);\n }\n\n return options;\n }\n\n var EventsBus = /*#__PURE__*/function () {\n /**\n * Construct a EventBus instance.\n *\n * @param {Object} events\n */\n function EventsBus() {\n var events = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, EventsBus);\n\n this.events = events;\n this.hop = events.hasOwnProperty;\n }\n /**\n * Adds listener to the specifed event.\n *\n * @param {String|Array} event\n * @param {Function} handler\n */\n\n\n _createClass(EventsBus, [{\n key: \"on\",\n value: function on(event, handler) {\n if (isArray(event)) {\n for (var i = 0; i < event.length; i++) {\n this.on(event[i], handler);\n }\n\n return;\n } // Create the event's object if not yet created\n\n\n if (!this.hop.call(this.events, event)) {\n this.events[event] = [];\n } // Add the handler to queue\n\n\n var index = this.events[event].push(handler) - 1; // Provide handle back for removal of event\n\n return {\n remove: function remove() {\n delete this.events[event][index];\n }\n };\n }\n /**\n * Runs registered handlers for specified event.\n *\n * @param {String|Array} event\n * @param {Object=} context\n */\n\n }, {\n key: \"emit\",\n value: function emit(event, context) {\n if (isArray(event)) {\n for (var i = 0; i < event.length; i++) {\n this.emit(event[i], context);\n }\n\n return;\n } // If the event doesn't exist, or there's no handlers in queue, just leave\n\n\n if (!this.hop.call(this.events, event)) {\n return;\n } // Cycle through events queue, fire!\n\n\n this.events[event].forEach(function (item) {\n item(context || {});\n });\n }\n }]);\n\n return EventsBus;\n }();\n\n var Glide$1 = /*#__PURE__*/function () {\n /**\r\n * Construct glide.\r\n *\r\n * @param {String} selector\r\n * @param {Object} options\r\n */\n function Glide(selector) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _classCallCheck(this, Glide);\n\n this._c = {};\n this._t = [];\n this._e = new EventsBus();\n this.disabled = false;\n this.selector = selector;\n this.settings = mergeOptions(defaults, options);\n this.index = this.settings.startAt;\n }\n /**\r\n * Initializes glide.\r\n *\r\n * @param {Object} extensions Collection of extensions to initialize.\r\n * @return {Glide}\r\n */\n\n\n _createClass(Glide, [{\n key: \"mount\",\n value: function mount$1() {\n var extensions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n this._e.emit('mount.before');\n\n if (isObject(extensions)) {\n this._c = mount(this, extensions, this._e);\n } else {\n warn('You need to provide a object on `mount()`');\n }\n\n this._e.emit('mount.after');\n\n return this;\n }\n /**\r\n * Collects an instance `translate` transformers.\r\n *\r\n * @param {Array} transformers Collection of transformers.\r\n * @return {Void}\r\n */\n\n }, {\n key: \"mutate\",\n value: function mutate() {\n var transformers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n\n if (isArray(transformers)) {\n this._t = transformers;\n } else {\n warn('You need to provide a array on `mutate()`');\n }\n\n return this;\n }\n /**\r\n * Updates glide with specified settings.\r\n *\r\n * @param {Object} settings\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"update\",\n value: function update() {\n var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.settings = mergeOptions(this.settings, settings);\n\n if (settings.hasOwnProperty('startAt')) {\n this.index = settings.startAt;\n }\n\n this._e.emit('update');\n\n return this;\n }\n /**\r\n * Change slide with specified pattern. A pattern must be in the special format:\r\n * `>` - Move one forward\r\n * `<` - Move one backward\r\n * `={i}` - Go to {i} zero-based slide (eq. '=1', will go to second slide)\r\n * `>>` - Rewinds to end (last slide)\r\n * `<<` - Rewinds to start (first slide)\r\n * `|>` - Move one viewport forward\r\n * `|<` - Move one viewport backward\r\n *\r\n * @param {String} pattern\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"go\",\n value: function go(pattern) {\n this._c.Run.make(pattern);\n\n return this;\n }\n /**\r\n * Move track by specified distance.\r\n *\r\n * @param {String} distance\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"move\",\n value: function move(distance) {\n this._c.Transition.disable();\n\n this._c.Move.make(distance);\n\n return this;\n }\n /**\r\n * Destroy instance and revert all changes done by this._c.\r\n *\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this._e.emit('destroy');\n\n return this;\n }\n /**\r\n * Start instance autoplaying.\r\n *\r\n * @param {Boolean|Number} interval Run autoplaying with passed interval regardless of `autoplay` settings\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"play\",\n value: function play() {\n var interval = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (interval) {\n this.settings.autoplay = interval;\n }\n\n this._e.emit('play');\n\n return this;\n }\n /**\r\n * Stop instance autoplaying.\r\n *\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"pause\",\n value: function pause() {\n this._e.emit('pause');\n\n return this;\n }\n /**\r\n * Sets glide into a idle status.\r\n *\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"disable\",\n value: function disable() {\n this.disabled = true;\n return this;\n }\n /**\r\n * Sets glide into a active status.\r\n *\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"enable\",\n value: function enable() {\n this.disabled = false;\n return this;\n }\n /**\r\n * Adds cuutom event listener with handler.\r\n *\r\n * @param {String|Array} event\r\n * @param {Function} handler\r\n * @return {Glide}\r\n */\n\n }, {\n key: \"on\",\n value: function on(event, handler) {\n this._e.on(event, handler);\n\n return this;\n }\n /**\r\n * Checks if glide is a precised type.\r\n *\r\n * @param {String} name\r\n * @return {Boolean}\r\n */\n\n }, {\n key: \"isType\",\n value: function isType(name) {\n return this.settings.type === name;\n }\n /**\r\n * Gets value of the core options.\r\n *\r\n * @return {Object}\r\n */\n\n }, {\n key: \"settings\",\n get: function get() {\n return this._o;\n }\n /**\r\n * Sets value of the core options.\r\n *\r\n * @param {Object} o\r\n * @return {Void}\r\n */\n ,\n set: function set(o) {\n if (isObject(o)) {\n this._o = o;\n } else {\n warn('Options must be an `object` instance.');\n }\n }\n /**\r\n * Gets current index of the slider.\r\n *\r\n * @return {Object}\r\n */\n\n }, {\n key: \"index\",\n get: function get() {\n return this._i;\n }\n /**\r\n * Sets current index a slider.\r\n *\r\n * @return {Object}\r\n */\n ,\n set: function set(i) {\n this._i = toInt(i);\n }\n /**\r\n * Gets type name of the slider.\r\n *\r\n * @return {String}\r\n */\n\n }, {\n key: \"type\",\n get: function get() {\n return this.settings.type;\n }\n /**\r\n * Gets value of the idle status.\r\n *\r\n * @return {Boolean}\r\n */\n\n }, {\n key: \"disabled\",\n get: function get() {\n return this._d;\n }\n /**\r\n * Sets value of the idle status.\r\n *\r\n * @return {Boolean}\r\n */\n ,\n set: function set(status) {\n this._d = !!status;\n }\n }]);\n\n return Glide;\n }();\n\n function Run (Glide, Components, Events) {\n var Run = {\n /**\n * Initializes autorunning of the glide.\n *\n * @return {Void}\n */\n mount: function mount() {\n this._o = false;\n },\n\n /**\n * Makes glides running based on the passed moving schema.\n *\n * @param {String} move\n */\n make: function make(move) {\n var _this = this;\n\n if (!Glide.disabled) {\n !Glide.settings.waitForTransition || Glide.disable();\n this.move = move;\n Events.emit('run.before', this.move);\n this.calculate();\n Events.emit('run', this.move);\n Components.Transition.after(function () {\n if (_this.isStart()) {\n Events.emit('run.start', _this.move);\n }\n\n if (_this.isEnd()) {\n Events.emit('run.end', _this.move);\n }\n\n if (_this.isOffset()) {\n _this._o = false;\n Events.emit('run.offset', _this.move);\n }\n\n Events.emit('run.after', _this.move);\n Glide.enable();\n });\n }\n },\n\n /**\n * Calculates current index based on defined move.\n *\n * @return {Number|Undefined}\n */\n calculate: function calculate() {\n var move = this.move,\n length = this.length;\n var steps = move.steps,\n direction = move.direction; // By default assume that size of view is equal to one slide\n\n var viewSize = 1; // While direction is `=` we want jump to\n // a specified index described in steps.\n\n if (direction === '=') {\n // Check if bound is true, \n // as we want to avoid whitespaces.\n if (Glide.settings.bound && toInt(steps) > length) {\n Glide.index = length;\n return;\n }\n\n Glide.index = steps;\n return;\n } // When pattern is equal to `>>` we want\n // fast forward to the last slide.\n\n\n if (direction === '>' && steps === '>') {\n Glide.index = length;\n return;\n } // When pattern is equal to `<<` we want\n // fast forward to the first slide.\n\n\n if (direction === '<' && steps === '<') {\n Glide.index = 0;\n return;\n } // pagination movement\n\n\n if (direction === '|') {\n viewSize = Glide.settings.perView || 1;\n } // we are moving forward\n\n\n if (direction === '>' || direction === '|' && steps === '>') {\n var index = calculateForwardIndex(viewSize);\n\n if (index > length) {\n this._o = true;\n }\n\n Glide.index = normalizeForwardIndex(index, viewSize);\n return;\n } // we are moving backward\n\n\n if (direction === '<' || direction === '|' && steps === '<') {\n var _index = calculateBackwardIndex(viewSize);\n\n if (_index < 0) {\n this._o = true;\n }\n\n Glide.index = normalizeBackwardIndex(_index, viewSize);\n return;\n }\n\n warn(\"Invalid direction pattern [\".concat(direction).concat(steps, \"] has been used\"));\n },\n\n /**\n * Checks if we are on the first slide.\n *\n * @return {Boolean}\n */\n isStart: function isStart() {\n return Glide.index <= 0;\n },\n\n /**\n * Checks if we are on the last slide.\n *\n * @return {Boolean}\n */\n isEnd: function isEnd() {\n return Glide.index >= this.length;\n },\n\n /**\n * Checks if we are making a offset run.\n *\n * @param {String} direction\n * @return {Boolean}\n */\n isOffset: function isOffset() {\n var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;\n\n if (!direction) {\n return this._o;\n }\n\n if (!this._o) {\n return false;\n } // did we view to the right?\n\n\n if (direction === '|>') {\n return this.move.direction === '|' && this.move.steps === '>';\n } // did we view to the left?\n\n\n if (direction === '|<') {\n return this.move.direction === '|' && this.move.steps === '<';\n }\n\n return this.move.direction === direction;\n },\n\n /**\n * Checks if bound mode is active\n *\n * @return {Boolean}\n */\n isBound: function isBound() {\n return Glide.isType('slider') && Glide.settings.focusAt !== 'center' && Glide.settings.bound;\n }\n };\n /**\n * Returns index value to move forward/to the right\n *\n * @param viewSize\n * @returns {Number}\n */\n\n function calculateForwardIndex(viewSize) {\n var index = Glide.index;\n\n if (Glide.isType('carousel')) {\n return index + viewSize;\n }\n\n return index + (viewSize - index % viewSize);\n }\n /**\n * Normalizes the given forward index based on glide settings, preventing it to exceed certain boundaries\n *\n * @param index\n * @param length\n * @param viewSize\n * @returns {Number}\n */\n\n\n function normalizeForwardIndex(index, viewSize) {\n var length = Run.length;\n\n if (index <= length) {\n return index;\n }\n\n if (Glide.isType('carousel')) {\n return index - (length + 1);\n }\n\n if (Glide.settings.rewind) {\n // bound does funny things with the length, therefor we have to be certain\n // that we are on the last possible index value given by bound\n if (Run.isBound() && !Run.isEnd()) {\n return length;\n }\n\n return 0;\n }\n\n if (Run.isBound()) {\n return length;\n }\n\n return Math.floor(length / viewSize) * viewSize;\n }\n /**\n * Calculates index value to move backward/to the left\n *\n * @param viewSize\n * @returns {Number}\n */\n\n\n function calculateBackwardIndex(viewSize) {\n var index = Glide.index;\n\n if (Glide.isType('carousel')) {\n return index - viewSize;\n } // ensure our back navigation results in the same index as a forward navigation\n // to experience a homogeneous paging\n\n\n var view = Math.ceil(index / viewSize);\n return (view - 1) * viewSize;\n }\n /**\n * Normalizes the given backward index based on glide settings, preventing it to exceed certain boundaries\n *\n * @param index\n * @param length\n * @param viewSize\n * @returns {*}\n */\n\n\n function normalizeBackwardIndex(index, viewSize) {\n var length = Run.length;\n\n if (index >= 0) {\n return index;\n }\n\n if (Glide.isType('carousel')) {\n return index + (length + 1);\n }\n\n if (Glide.settings.rewind) {\n // bound does funny things with the length, therefor we have to be certain\n // that we are on first possible index value before we to rewind to the length given by bound\n if (Run.isBound() && Run.isStart()) {\n return length;\n }\n\n return Math.floor(length / viewSize) * viewSize;\n }\n\n return 0;\n }\n\n define(Run, 'move', {\n /**\n * Gets value of the move schema.\n *\n * @returns {Object}\n */\n get: function get() {\n return this._m;\n },\n\n /**\n * Sets value of the move schema.\n *\n * @returns {Object}\n */\n set: function set(value) {\n var step = value.substr(1);\n this._m = {\n direction: value.substr(0, 1),\n steps: step ? toInt(step) ? toInt(step) : step : 0\n };\n }\n });\n define(Run, 'length', {\n /**\n * Gets value of the running distance based\n * on zero-indexing number of slides.\n *\n * @return {Number}\n */\n get: function get() {\n var settings = Glide.settings;\n var length = Components.Html.slides.length; // If the `bound` option is active, a maximum running distance should be\n // reduced by `perView` and `focusAt` settings. Running distance\n // should end before creating an empty space after instance.\n\n if (this.isBound()) {\n return length - 1 - (toInt(settings.perView) - 1) + toInt(settings.focusAt);\n }\n\n return length - 1;\n }\n });\n define(Run, 'offset', {\n /**\n * Gets status of the offsetting flag.\n *\n * @return {Boolean}\n */\n get: function get() {\n return this._o;\n }\n });\n return Run;\n }\n\n /**\n * Returns a current time.\n *\n * @return {Number}\n */\n function now() {\n return new Date().getTime();\n }\n\n /**\n * Returns a function, that, when invoked, will only be triggered\n * at most once during a given window of time.\n *\n * @param {Function} func\n * @param {Number} wait\n * @param {Object=} options\n * @return {Function}\n *\n * @see https://github.com/jashkenas/underscore\n */\n\n function throttle(func, wait, options) {\n var timeout, context, args, result;\n var previous = 0;\n if (!options) options = {};\n\n var later = function later() {\n previous = options.leading === false ? 0 : now();\n timeout = null;\n result = func.apply(context, args);\n if (!timeout) context = args = null;\n };\n\n var throttled = function throttled() {\n var at = now();\n if (!previous && options.leading === false) previous = at;\n var remaining = wait - (at - previous);\n context = this;\n args = arguments;\n\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n\n previous = at;\n result = func.apply(context, args);\n if (!timeout) context = args = null;\n } else if (!timeout && options.trailing !== false) {\n timeout = setTimeout(later, remaining);\n }\n\n return result;\n };\n\n throttled.cancel = function () {\n clearTimeout(timeout);\n previous = 0;\n timeout = context = args = null;\n };\n\n return throttled;\n }\n\n var MARGIN_TYPE = {\n ltr: ['marginLeft', 'marginRight'],\n rtl: ['marginRight', 'marginLeft']\n };\n function Gaps (Glide, Components, Events) {\n var Gaps = {\n /**\n * Applies gaps between slides. First and last\n * slides do not receive it's edge margins.\n *\n * @param {HTMLCollection} slides\n * @return {Void}\n */\n apply: function apply(slides) {\n for (var i = 0, len = slides.length; i < len; i++) {\n var style = slides[i].style;\n var direction = Components.Direction.value;\n\n if (i !== 0) {\n style[MARGIN_TYPE[direction][0]] = \"\".concat(this.value / 2, \"px\");\n } else {\n style[MARGIN_TYPE[direction][0]] = '';\n }\n\n if (i !== slides.length - 1) {\n style[MARGIN_TYPE[direction][1]] = \"\".concat(this.value / 2, \"px\");\n } else {\n style[MARGIN_TYPE[direction][1]] = '';\n }\n }\n },\n\n /**\n * Removes gaps from the slides.\n *\n * @param {HTMLCollection} slides\n * @returns {Void}\n */\n remove: function remove(slides) {\n for (var i = 0, len = slides.length; i < len; i++) {\n var style = slides[i].style;\n style.marginLeft = '';\n style.marginRight = '';\n }\n }\n };\n define(Gaps, 'value', {\n /**\n * Gets value of the gap.\n *\n * @returns {Number}\n */\n get: function get() {\n return toInt(Glide.settings.gap);\n }\n });\n define(Gaps, 'grow', {\n /**\n * Gets additional dimensions value caused by gaps.\n * Used to increase width of the slides wrapper.\n *\n * @returns {Number}\n */\n get: function get() {\n return Gaps.value * Components.Sizes.length;\n }\n });\n define(Gaps, 'reductor', {\n /**\n * Gets reduction value caused by gaps.\n * Used to subtract width of the slides.\n *\n * @returns {Number}\n */\n get: function get() {\n var perView = Glide.settings.perView;\n return Gaps.value * (perView - 1) / perView;\n }\n });\n /**\n * Apply calculated gaps:\n * - after building, so slides (including clones) will receive proper margins\n * - on updating via API, to recalculate gaps with new options\n */\n\n Events.on(['build.after', 'update'], throttle(function () {\n Gaps.apply(Components.Html.wrapper.children);\n }, 30));\n /**\n * Remove gaps:\n * - on destroying to bring markup to its inital state\n */\n\n Events.on('destroy', function () {\n Gaps.remove(Components.Html.wrapper.children);\n });\n return Gaps;\n }\n\n /**\n * Finds siblings nodes of the passed node.\n *\n * @param {Element} node\n * @return {Array}\n */\n function siblings(node) {\n if (node && node.parentNode) {\n var n = node.parentNode.firstChild;\n var matched = [];\n\n for (; n; n = n.nextSibling) {\n if (n.nodeType === 1 && n !== node) {\n matched.push(n);\n }\n }\n\n return matched;\n }\n\n return [];\n }\n /**\n * Checks if passed node exist and is a valid element.\n *\n * @param {Element} node\n * @return {Boolean}\n */\n\n function exist(node) {\n if (node && node instanceof window.HTMLElement) {\n return true;\n }\n\n return false;\n }\n /**\n * Coerces a NodeList to an Array.\n *\n * @param {NodeList} nodeList\n * @return {Array}\n */\n\n function toArray(nodeList) {\n return Array.prototype.slice.call(nodeList);\n }\n\n var TRACK_SELECTOR = '[data-glide-el=\"track\"]';\n function Html (Glide, Components, Events) {\n var Html = {\n /**\n * Setup slider HTML nodes.\n *\n * @param {Glide} glide\n */\n mount: function mount() {\n this.root = Glide.selector;\n this.track = this.root.querySelector(TRACK_SELECTOR);\n this.collectSlides();\n },\n\n /**\n * Collect slides\n */\n collectSlides: function collectSlides() {\n this.slides = toArray(this.wrapper.children).filter(function (slide) {\n return !slide.classList.contains(Glide.settings.classes.slide.clone);\n });\n }\n };\n define(Html, 'root', {\n /**\n * Gets node of the glide main element.\n *\n * @return {Object}\n */\n get: function get() {\n return Html._r;\n },\n\n /**\n * Sets node of the glide main element.\n *\n * @return {Object}\n */\n set: function set(r) {\n if (isString(r)) {\n r = document.querySelector(r);\n }\n\n if (exist(r)) {\n Html._r = r;\n } else {\n warn('Root element must be a existing Html node');\n }\n }\n });\n define(Html, 'track', {\n /**\n * Gets node of the glide track with slides.\n *\n * @return {Object}\n */\n get: function get() {\n return Html._t;\n },\n\n /**\n * Sets node of the glide track with slides.\n *\n * @return {Object}\n */\n set: function set(t) {\n if (exist(t)) {\n Html._t = t;\n } else {\n warn(\"Could not find track element. Please use \".concat(TRACK_SELECTOR, \" attribute.\"));\n }\n }\n });\n define(Html, 'wrapper', {\n /**\n * Gets node of the slides wrapper.\n *\n * @return {Object}\n */\n get: function get() {\n return Html.track.children[0];\n }\n });\n /**\n * Add/remove/reorder dynamic slides\n */\n\n Events.on('update', function () {\n Html.collectSlides();\n });\n return Html;\n }\n\n function Peek (Glide, Components, Events) {\n var Peek = {\n /**\n * Setups how much to peek based on settings.\n *\n * @return {Void}\n */\n mount: function mount() {\n this.value = Glide.settings.peek;\n }\n };\n define(Peek, 'value', {\n /**\n * Gets value of the peek.\n *\n * @returns {Number|Object}\n */\n get: function get() {\n return Peek._v;\n },\n\n /**\n * Sets value of the peek.\n *\n * @param {Number|Object} value\n * @return {Void}\n */\n set: function set(value) {\n if (isObject(value)) {\n value.before = toInt(value.before);\n value.after = toInt(value.after);\n } else {\n value = toInt(value);\n }\n\n Peek._v = value;\n }\n });\n define(Peek, 'reductor', {\n /**\n * Gets reduction value caused by peek.\n *\n * @returns {Number}\n */\n get: function get() {\n var value = Peek.value;\n var perView = Glide.settings.perView;\n\n if (isObject(value)) {\n return value.before / perView + value.after / perView;\n }\n\n return value * 2 / perView;\n }\n });\n /**\n * Recalculate peeking sizes on:\n * - when resizing window to update to proper percents\n */\n\n Events.on(['resize', 'update'], function () {\n Peek.mount();\n });\n return Peek;\n }\n\n function Move (Glide, Components, Events) {\n var Move = {\n /**\n * Constructs move component.\n *\n * @returns {Void}\n */\n mount: function mount() {\n this._o = 0;\n },\n\n /**\n * Calculates a movement value based on passed offset and currently active index.\n *\n * @param {Number} offset\n * @return {Void}\n */\n make: function make() {\n var _this = this;\n\n var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n this.offset = offset;\n Events.emit('move', {\n movement: this.value\n });\n Components.Transition.after(function () {\n Events.emit('move.after', {\n movement: _this.value\n });\n });\n }\n };\n define(Move, 'offset', {\n /**\n * Gets an offset value used to modify current translate.\n *\n * @return {Object}\n */\n get: function get() {\n return Move._o;\n },\n\n /**\n * Sets an offset value used to modify current translate.\n *\n * @return {Object}\n */\n set: function set(value) {\n Move._o = !isUndefined(value) ? toInt(value) : 0;\n }\n });\n define(Move, 'translate', {\n /**\n * Gets a raw movement value.\n *\n * @return {Number}\n */\n get: function get() {\n return Components.Sizes.slideWidth * Glide.index;\n }\n });\n define(Move, 'value', {\n /**\n * Gets an actual movement value corrected by offset.\n *\n * @return {Number}\n */\n get: function get() {\n var offset = this.offset;\n var translate = this.translate;\n\n if (Components.Direction.is('rtl')) {\n return translate + offset;\n }\n\n return translate - offset;\n }\n });\n /**\n * Make movement to proper slide on:\n * - before build, so glide will start at `startAt` index\n * - on each standard run to move to newly calculated index\n */\n\n Events.on(['build.before', 'run'], function () {\n Move.make();\n });\n return Move;\n }\n\n function Sizes (Glide, Components, Events) {\n var Sizes = {\n /**\n * Setups dimensions of slides.\n *\n * @return {Void}\n */\n setupSlides: function setupSlides() {\n var width = \"\".concat(this.slideWidth, \"px\");\n var slides = Components.Html.slides;\n\n for (var i = 0; i < slides.length; i++) {\n slides[i].style.width = width;\n }\n },\n\n /**\n * Setups dimensions of slides wrapper.\n *\n * @return {Void}\n */\n setupWrapper: function setupWrapper() {\n Components.Html.wrapper.style.width = \"\".concat(this.wrapperSize, \"px\");\n },\n\n /**\n * Removes applied styles from HTML elements.\n *\n * @returns {Void}\n */\n remove: function remove() {\n var slides = Components.Html.slides;\n\n for (var i = 0; i < slides.length; i++) {\n slides[i].style.width = '';\n }\n\n Components.Html.wrapper.style.width = '';\n }\n };\n define(Sizes, 'length', {\n /**\n * Gets count number of the slides.\n *\n * @return {Number}\n */\n get: function get() {\n return Components.Html.slides.length;\n }\n });\n define(Sizes, 'width', {\n /**\n * Gets width value of the slider (visible area).\n *\n * @return {Number}\n */\n get: function get() {\n return Components.Html.track.offsetWidth;\n }\n });\n define(Sizes, 'wrapperSize', {\n /**\n * Gets size of the slides wrapper.\n *\n * @return {Number}\n */\n get: function get() {\n return Sizes.slideWidth * Sizes.length + Components.Gaps.grow + Components.Clones.grow;\n }\n });\n define(Sizes, 'slideWidth', {\n /**\n * Gets width value of a single slide.\n *\n * @return {Number}\n */\n get: function get() {\n return Sizes.width / Glide.settings.perView - Components.Peek.reductor - Components.Gaps.reductor;\n }\n });\n /**\n * Apply calculated glide's dimensions:\n * - before building, so other dimensions (e.g. translate) will be calculated propertly\n * - when resizing window to recalculate sildes dimensions\n * - on updating via API, to calculate dimensions based on new options\n */\n\n Events.on(['build.before', 'resize', 'update'], function () {\n Sizes.setupSlides();\n Sizes.setupWrapper();\n });\n /**\n * Remove calculated glide's dimensions:\n * - on destoting to bring markup to its inital state\n */\n\n Events.on('destroy', function () {\n Sizes.remove();\n });\n return Sizes;\n }\n\n function Build (Glide, Components, Events) {\n var Build = {\n /**\n * Init glide building. Adds classes, sets\n * dimensions and setups initial state.\n *\n * @return {Void}\n */\n mount: function mount() {\n Events.emit('build.before');\n this.typeClass();\n this.activeClass();\n Events.emit('build.after');\n },\n\n /**\n * Adds `type` class to the glide element.\n *\n * @return {Void}\n */\n typeClass: function typeClass() {\n Components.Html.root.classList.add(Glide.settings.classes.type[Glide.settings.type]);\n },\n\n /**\n * Sets active class to current slide.\n *\n * @return {Void}\n */\n activeClass: function activeClass() {\n var classes = Glide.settings.classes;\n var slide = Components.Html.slides[Glide.index];\n\n if (slide) {\n slide.classList.add(classes.slide.active);\n siblings(slide).forEach(function (sibling) {\n sibling.classList.remove(classes.slide.active);\n });\n }\n },\n\n /**\n * Removes HTML classes applied at building.\n *\n * @return {Void}\n */\n removeClasses: function removeClasses() {\n var _Glide$settings$class = Glide.settings.classes,\n type = _Glide$settings$class.type,\n slide = _Glide$settings$class.slide;\n Components.Html.root.classList.remove(type[Glide.settings.type]);\n Components.Html.slides.forEach(function (sibling) {\n sibling.classList.remove(slide.active);\n });\n }\n };\n /**\n * Clear building classes:\n * - on destroying to bring HTML to its initial state\n * - on updating to remove classes before remounting component\n */\n\n Events.on(['destroy', 'update'], function () {\n Build.removeClasses();\n });\n /**\n * Remount component:\n * - on resizing of the window to calculate new dimensions\n * - on updating settings via API\n */\n\n Events.on(['resize', 'update'], function () {\n Build.mount();\n });\n /**\n * Swap active class of current slide:\n * - after each move to the new index\n */\n\n Events.on('move.after', function () {\n Build.activeClass();\n });\n return Build;\n }\n\n function Clones (Glide, Components, Events) {\n var Clones = {\n /**\n * Create pattern map and collect slides to be cloned.\n */\n mount: function mount() {\n this.items = [];\n\n if (Glide.isType('carousel')) {\n this.items = this.collect();\n }\n },\n\n /**\n * Collect clones with pattern.\n *\n * @return {[]}\n */\n collect: function collect() {\n var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var slides = Components.Html.slides;\n var _Glide$settings = Glide.settings,\n perView = _Glide$settings.perView,\n classes = _Glide$settings.classes,\n cloningRatio = _Glide$settings.cloningRatio;\n\n if (slides.length !== 0) {\n var peekIncrementer = +!!Glide.settings.peek;\n var cloneCount = perView + peekIncrementer + Math.round(perView / 2);\n var append = slides.slice(0, cloneCount).reverse();\n var prepend = slides.slice(cloneCount * -1);\n\n for (var r = 0; r < Math.max(cloningRatio, Math.floor(perView / slides.length)); r++) {\n for (var i = 0; i < append.length; i++) {\n var clone = append[i].cloneNode(true);\n clone.classList.add(classes.slide.clone);\n items.push(clone);\n }\n\n for (var _i = 0; _i < prepend.length; _i++) {\n var _clone = prepend[_i].cloneNode(true);\n\n _clone.classList.add(classes.slide.clone);\n\n items.unshift(_clone);\n }\n }\n }\n\n return items;\n },\n\n /**\n * Append cloned slides with generated pattern.\n *\n * @return {Void}\n */\n append: function append() {\n var items = this.items;\n var _Components$Html = Components.Html,\n wrapper = _Components$Html.wrapper,\n slides = _Components$Html.slides;\n var half = Math.floor(items.length / 2);\n var prepend = items.slice(0, half).reverse();\n var append = items.slice(half * -1).reverse();\n var width = \"\".concat(Components.Sizes.slideWidth, \"px\");\n\n for (var i = 0; i < append.length; i++) {\n wrapper.appendChild(append[i]);\n }\n\n for (var _i2 = 0; _i2 < prepend.length; _i2++) {\n wrapper.insertBefore(prepend[_i2], slides[0]);\n }\n\n for (var _i3 = 0; _i3 < items.length; _i3++) {\n items[_i3].style.width = width;\n }\n },\n\n /**\n * Remove all cloned slides.\n *\n * @return {Void}\n */\n remove: function remove() {\n var items = this.items;\n\n for (var i = 0; i < items.length; i++) {\n Components.Html.wrapper.removeChild(items[i]);\n }\n }\n };\n define(Clones, 'grow', {\n /**\n * Gets additional dimensions value caused by clones.\n *\n * @return {Number}\n */\n get: function get() {\n return (Components.Sizes.slideWidth + Components.Gaps.value) * Clones.items.length;\n }\n });\n /**\n * Append additional slide's clones:\n * - while glide's type is `carousel`\n */\n\n Events.on('update', function () {\n Clones.remove();\n Clones.mount();\n Clones.append();\n });\n /**\n * Append additional slide's clones:\n * - while glide's type is `carousel`\n */\n\n Events.on('build.before', function () {\n if (Glide.isType('carousel')) {\n Clones.append();\n }\n });\n /**\n * Remove clones HTMLElements:\n * - on destroying, to bring HTML to its initial state\n */\n\n Events.on('destroy', function () {\n Clones.remove();\n });\n return Clones;\n }\n\n var EventsBinder = /*#__PURE__*/function () {\n /**\n * Construct a EventsBinder instance.\n */\n function EventsBinder() {\n var listeners = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, EventsBinder);\n\n this.listeners = listeners;\n }\n /**\n * Adds events listeners to arrows HTML elements.\n *\n * @param {String|Array} events\n * @param {Element|Window|Document} el\n * @param {Function} closure\n * @param {Boolean|Object} capture\n * @return {Void}\n */\n\n\n _createClass(EventsBinder, [{\n key: \"on\",\n value: function on(events, el, closure) {\n var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n\n if (isString(events)) {\n events = [events];\n }\n\n for (var i = 0; i < events.length; i++) {\n this.listeners[events[i]] = closure;\n el.addEventListener(events[i], this.listeners[events[i]], capture);\n }\n }\n /**\n * Removes event listeners from arrows HTML elements.\n *\n * @param {String|Array} events\n * @param {Element|Window|Document} el\n * @param {Boolean|Object} capture\n * @return {Void}\n */\n\n }, {\n key: \"off\",\n value: function off(events, el) {\n var capture = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (isString(events)) {\n events = [events];\n }\n\n for (var i = 0; i < events.length; i++) {\n el.removeEventListener(events[i], this.listeners[events[i]], capture);\n }\n }\n /**\n * Destroy collected listeners.\n *\n * @returns {Void}\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n delete this.listeners;\n }\n }]);\n\n return EventsBinder;\n }();\n\n function Resize (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var Resize = {\n /**\n * Initializes window bindings.\n */\n mount: function mount() {\n this.bind();\n },\n\n /**\n * Binds `rezsize` listener to the window.\n * It's a costly event, so we are debouncing it.\n *\n * @return {Void}\n */\n bind: function bind() {\n Binder.on('resize', window, throttle(function () {\n Events.emit('resize');\n }, Glide.settings.throttle));\n },\n\n /**\n * Unbinds listeners from the window.\n *\n * @return {Void}\n */\n unbind: function unbind() {\n Binder.off('resize', window);\n }\n };\n /**\n * Remove bindings from window:\n * - on destroying, to remove added EventListener\n */\n\n Events.on('destroy', function () {\n Resize.unbind();\n Binder.destroy();\n });\n return Resize;\n }\n\n var VALID_DIRECTIONS = ['ltr', 'rtl'];\n var FLIPED_MOVEMENTS = {\n '>': '<',\n '<': '>',\n '=': '='\n };\n function Direction (Glide, Components, Events) {\n var Direction = {\n /**\n * Setups gap value based on settings.\n *\n * @return {Void}\n */\n mount: function mount() {\n this.value = Glide.settings.direction;\n },\n\n /**\n * Resolves pattern based on direction value\n *\n * @param {String} pattern\n * @returns {String}\n */\n resolve: function resolve(pattern) {\n var token = pattern.slice(0, 1);\n\n if (this.is('rtl')) {\n return pattern.split(token).join(FLIPED_MOVEMENTS[token]);\n }\n\n return pattern;\n },\n\n /**\n * Checks value of direction mode.\n *\n * @param {String} direction\n * @returns {Boolean}\n */\n is: function is(direction) {\n return this.value === direction;\n },\n\n /**\n * Applies direction class to the root HTML element.\n *\n * @return {Void}\n */\n addClass: function addClass() {\n Components.Html.root.classList.add(Glide.settings.classes.direction[this.value]);\n },\n\n /**\n * Removes direction class from the root HTML element.\n *\n * @return {Void}\n */\n removeClass: function removeClass() {\n Components.Html.root.classList.remove(Glide.settings.classes.direction[this.value]);\n }\n };\n define(Direction, 'value', {\n /**\n * Gets value of the direction.\n *\n * @returns {Number}\n */\n get: function get() {\n return Direction._v;\n },\n\n /**\n * Sets value of the direction.\n *\n * @param {String} value\n * @return {Void}\n */\n set: function set(value) {\n if (VALID_DIRECTIONS.indexOf(value) > -1) {\n Direction._v = value;\n } else {\n warn('Direction value must be `ltr` or `rtl`');\n }\n }\n });\n /**\n * Clear direction class:\n * - on destroy to bring HTML to its initial state\n * - on update to remove class before reappling bellow\n */\n\n Events.on(['destroy', 'update'], function () {\n Direction.removeClass();\n });\n /**\n * Remount component:\n * - on update to reflect changes in direction value\n */\n\n Events.on('update', function () {\n Direction.mount();\n });\n /**\n * Apply direction class:\n * - before building to apply class for the first time\n * - on updating to reapply direction class that may changed\n */\n\n Events.on(['build.before', 'update'], function () {\n Direction.addClass();\n });\n return Direction;\n }\n\n /**\n * Reflects value of glide movement.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n function Rtl (Glide, Components) {\n return {\n /**\n * Negates the passed translate if glide is in RTL option.\n *\n * @param {Number} translate\n * @return {Number}\n */\n modify: function modify(translate) {\n if (Components.Direction.is('rtl')) {\n return -translate;\n }\n\n return translate;\n }\n };\n }\n\n /**\n * Updates glide movement with a `gap` settings.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n function Gap (Glide, Components) {\n return {\n /**\n * Modifies passed translate value with number in the `gap` settings.\n *\n * @param {Number} translate\n * @return {Number}\n */\n modify: function modify(translate) {\n var multiplier = Math.floor(translate / Components.Sizes.slideWidth);\n return translate + Components.Gaps.value * multiplier;\n }\n };\n }\n\n /**\n * Updates glide movement with width of additional clones width.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n function Grow (Glide, Components) {\n return {\n /**\n * Adds to the passed translate width of the half of clones.\n *\n * @param {Number} translate\n * @return {Number}\n */\n modify: function modify(translate) {\n return translate + Components.Clones.grow / 2;\n }\n };\n }\n\n /**\n * Updates glide movement with a `peek` settings.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n\n function Peeking (Glide, Components) {\n return {\n /**\n * Modifies passed translate value with a `peek` setting.\n *\n * @param {Number} translate\n * @return {Number}\n */\n modify: function modify(translate) {\n if (Glide.settings.focusAt >= 0) {\n var peek = Components.Peek.value;\n\n if (isObject(peek)) {\n return translate - peek.before;\n }\n\n return translate - peek;\n }\n\n return translate;\n }\n };\n }\n\n /**\n * Updates glide movement with a `focusAt` settings.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n function Focusing (Glide, Components) {\n return {\n /**\n * Modifies passed translate value with index in the `focusAt` setting.\n *\n * @param {Number} translate\n * @return {Number}\n */\n modify: function modify(translate) {\n var gap = Components.Gaps.value;\n var width = Components.Sizes.width;\n var focusAt = Glide.settings.focusAt;\n var slideWidth = Components.Sizes.slideWidth;\n\n if (focusAt === 'center') {\n return translate - (width / 2 - slideWidth / 2);\n }\n\n return translate - slideWidth * focusAt - gap * focusAt;\n }\n };\n }\n\n /**\n * Applies diffrent transformers on translate value.\n *\n * @param {Object} Glide\n * @param {Object} Components\n * @return {Object}\n */\n\n function mutator (Glide, Components, Events) {\n /**\n * Merge instance transformers with collection of default transformers.\n * It's important that the Rtl component be last on the list,\n * so it reflects all previous transformations.\n *\n * @type {Array}\n */\n var TRANSFORMERS = [Gap, Grow, Peeking, Focusing].concat(Glide._t, [Rtl]);\n return {\n /**\n * Piplines translate value with registered transformers.\n *\n * @param {Number} translate\n * @return {Number}\n */\n mutate: function mutate(translate) {\n for (var i = 0; i < TRANSFORMERS.length; i++) {\n var transformer = TRANSFORMERS[i];\n\n if (isFunction(transformer) && isFunction(transformer().modify)) {\n translate = transformer(Glide, Components, Events).modify(translate);\n } else {\n warn('Transformer should be a function that returns an object with `modify()` method');\n }\n }\n\n return translate;\n }\n };\n }\n\n function Translate (Glide, Components, Events) {\n var Translate = {\n /**\n * Sets value of translate on HTML element.\n *\n * @param {Number} value\n * @return {Void}\n */\n set: function set(value) {\n var transform = mutator(Glide, Components).mutate(value);\n var translate3d = \"translate3d(\".concat(-1 * transform, \"px, 0px, 0px)\");\n Components.Html.wrapper.style.mozTransform = translate3d; // needed for supported Firefox 10-15\n\n Components.Html.wrapper.style.webkitTransform = translate3d; // needed for supported Chrome 10-35, Safari 5.1-8, and Opera 15-22\n\n Components.Html.wrapper.style.transform = translate3d;\n },\n\n /**\n * Removes value of translate from HTML element.\n *\n * @return {Void}\n */\n remove: function remove() {\n Components.Html.wrapper.style.transform = '';\n },\n\n /**\n * @return {number}\n */\n getStartIndex: function getStartIndex() {\n var length = Components.Sizes.length;\n var index = Glide.index;\n var perView = Glide.settings.perView;\n\n if (Components.Run.isOffset('>') || Components.Run.isOffset('|>')) {\n return length + (index - perView);\n } // \"modulo length\" converts an index that equals length to zero\n\n\n return (index + perView) % length;\n },\n\n /**\n * @return {number}\n */\n getTravelDistance: function getTravelDistance() {\n var travelDistance = Components.Sizes.slideWidth * Glide.settings.perView;\n\n if (Components.Run.isOffset('>') || Components.Run.isOffset('|>')) {\n // reverse travel distance so that we don't have to change subtract operations\n return travelDistance * -1;\n }\n\n return travelDistance;\n }\n };\n /**\n * Set new translate value:\n * - on move to reflect index change\n * - on updating via API to reflect possible changes in options\n */\n\n Events.on('move', function (context) {\n if (!Glide.isType('carousel') || !Components.Run.isOffset()) {\n return Translate.set(context.movement);\n }\n\n Components.Transition.after(function () {\n Events.emit('translate.jump');\n Translate.set(Components.Sizes.slideWidth * Glide.index);\n });\n var startWidth = Components.Sizes.slideWidth * Components.Translate.getStartIndex();\n return Translate.set(startWidth - Components.Translate.getTravelDistance());\n });\n /**\n * Remove translate:\n * - on destroying to bring markup to its inital state\n */\n\n Events.on('destroy', function () {\n Translate.remove();\n });\n return Translate;\n }\n\n function Transition (Glide, Components, Events) {\n /**\n * Holds inactivity status of transition.\n * When true transition is not applied.\n *\n * @type {Boolean}\n */\n var disabled = false;\n var Transition = {\n /**\n * Composes string of the CSS transition.\n *\n * @param {String} property\n * @return {String}\n */\n compose: function compose(property) {\n var settings = Glide.settings;\n\n if (!disabled) {\n return \"\".concat(property, \" \").concat(this.duration, \"ms \").concat(settings.animationTimingFunc);\n }\n\n return \"\".concat(property, \" 0ms \").concat(settings.animationTimingFunc);\n },\n\n /**\n * Sets value of transition on HTML element.\n *\n * @param {String=} property\n * @return {Void}\n */\n set: function set() {\n var property = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'transform';\n Components.Html.wrapper.style.transition = this.compose(property);\n },\n\n /**\n * Removes value of transition from HTML element.\n *\n * @return {Void}\n */\n remove: function remove() {\n Components.Html.wrapper.style.transition = '';\n },\n\n /**\n * Runs callback after animation.\n *\n * @param {Function} callback\n * @return {Void}\n */\n after: function after(callback) {\n setTimeout(function () {\n callback();\n }, this.duration);\n },\n\n /**\n * Enable transition.\n *\n * @return {Void}\n */\n enable: function enable() {\n disabled = false;\n this.set();\n },\n\n /**\n * Disable transition.\n *\n * @return {Void}\n */\n disable: function disable() {\n disabled = true;\n this.set();\n }\n };\n define(Transition, 'duration', {\n /**\n * Gets duration of the transition based\n * on currently running animation type.\n *\n * @return {Number}\n */\n get: function get() {\n var settings = Glide.settings;\n\n if (Glide.isType('slider') && Components.Run.offset) {\n return settings.rewindDuration;\n }\n\n return settings.animationDuration;\n }\n });\n /**\n * Set transition `style` value:\n * - on each moving, because it may be cleared by offset move\n */\n\n Events.on('move', function () {\n Transition.set();\n });\n /**\n * Disable transition:\n * - before initial build to avoid transitioning from `0` to `startAt` index\n * - while resizing window and recalculating dimensions\n * - on jumping from offset transition at start and end edges in `carousel` type\n */\n\n Events.on(['build.before', 'resize', 'translate.jump'], function () {\n Transition.disable();\n });\n /**\n * Enable transition:\n * - on each running, because it may be disabled by offset move\n */\n\n Events.on('run', function () {\n Transition.enable();\n });\n /**\n * Remove transition:\n * - on destroying to bring markup to its inital state\n */\n\n Events.on('destroy', function () {\n Transition.remove();\n });\n return Transition;\n }\n\n /**\n * Test via a getter in the options object to see\n * if the passive property is accessed.\n *\n * @see https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection\n */\n var supportsPassive = false;\n\n try {\n var opts = Object.defineProperty({}, 'passive', {\n get: function get() {\n supportsPassive = true;\n }\n });\n window.addEventListener('testPassive', null, opts);\n window.removeEventListener('testPassive', null, opts);\n } catch (e) {}\n\n var supportsPassive$1 = supportsPassive;\n\n var START_EVENTS = ['touchstart', 'mousedown'];\n var MOVE_EVENTS = ['touchmove', 'mousemove'];\n var END_EVENTS = ['touchend', 'touchcancel', 'mouseup', 'mouseleave'];\n var MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];\n function Swipe (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var swipeSin = 0;\n var swipeStartX = 0;\n var swipeStartY = 0;\n var disabled = false;\n var capture = supportsPassive$1 ? {\n passive: true\n } : false;\n var Swipe = {\n /**\n * Initializes swipe bindings.\n *\n * @return {Void}\n */\n mount: function mount() {\n this.bindSwipeStart();\n },\n\n /**\n * Handler for `swipestart` event. Calculates entry points of the user's tap.\n *\n * @param {Object} event\n * @return {Void}\n */\n start: function start(event) {\n if (!disabled && !Glide.disabled) {\n this.disable();\n var swipe = this.touches(event);\n swipeSin = null;\n swipeStartX = toInt(swipe.pageX);\n swipeStartY = toInt(swipe.pageY);\n this.bindSwipeMove();\n this.bindSwipeEnd();\n Events.emit('swipe.start');\n }\n },\n\n /**\n * Handler for `swipemove` event. Calculates user's tap angle and distance.\n *\n * @param {Object} event\n */\n move: function move(event) {\n if (!Glide.disabled) {\n var _Glide$settings = Glide.settings,\n touchAngle = _Glide$settings.touchAngle,\n touchRatio = _Glide$settings.touchRatio,\n classes = _Glide$settings.classes;\n var swipe = this.touches(event);\n var subExSx = toInt(swipe.pageX) - swipeStartX;\n var subEySy = toInt(swipe.pageY) - swipeStartY;\n var powEX = Math.abs(subExSx << 2);\n var powEY = Math.abs(subEySy << 2);\n var swipeHypotenuse = Math.sqrt(powEX + powEY);\n var swipeCathetus = Math.sqrt(powEY);\n swipeSin = Math.asin(swipeCathetus / swipeHypotenuse);\n\n if (swipeSin * 180 / Math.PI < touchAngle) {\n event.stopPropagation();\n Components.Move.make(subExSx * toFloat(touchRatio));\n Components.Html.root.classList.add(classes.dragging);\n Events.emit('swipe.move');\n } else {\n return false;\n }\n }\n },\n\n /**\n * Handler for `swipeend` event. Finitializes user's tap and decides about glide move.\n *\n * @param {Object} event\n * @return {Void}\n */\n end: function end(event) {\n if (!Glide.disabled) {\n var _Glide$settings2 = Glide.settings,\n perSwipe = _Glide$settings2.perSwipe,\n touchAngle = _Glide$settings2.touchAngle,\n classes = _Glide$settings2.classes;\n var swipe = this.touches(event);\n var threshold = this.threshold(event);\n var swipeDistance = swipe.pageX - swipeStartX;\n var swipeDeg = swipeSin * 180 / Math.PI;\n this.enable();\n\n if (swipeDistance > threshold && swipeDeg < touchAngle) {\n Components.Run.make(Components.Direction.resolve(\"\".concat(perSwipe, \"<\")));\n } else if (swipeDistance < -threshold && swipeDeg < touchAngle) {\n Components.Run.make(Components.Direction.resolve(\"\".concat(perSwipe, \">\")));\n } else {\n // While swipe don't reach distance apply previous transform.\n Components.Move.make();\n }\n\n Components.Html.root.classList.remove(classes.dragging);\n this.unbindSwipeMove();\n this.unbindSwipeEnd();\n Events.emit('swipe.end');\n }\n },\n\n /**\n * Binds swipe's starting event.\n *\n * @return {Void}\n */\n bindSwipeStart: function bindSwipeStart() {\n var _this = this;\n\n var _Glide$settings3 = Glide.settings,\n swipeThreshold = _Glide$settings3.swipeThreshold,\n dragThreshold = _Glide$settings3.dragThreshold;\n\n if (swipeThreshold) {\n Binder.on(START_EVENTS[0], Components.Html.wrapper, function (event) {\n _this.start(event);\n }, capture);\n }\n\n if (dragThreshold) {\n Binder.on(START_EVENTS[1], Components.Html.wrapper, function (event) {\n _this.start(event);\n }, capture);\n }\n },\n\n /**\n * Unbinds swipe's starting event.\n *\n * @return {Void}\n */\n unbindSwipeStart: function unbindSwipeStart() {\n Binder.off(START_EVENTS[0], Components.Html.wrapper, capture);\n Binder.off(START_EVENTS[1], Components.Html.wrapper, capture);\n },\n\n /**\n * Binds swipe's moving event.\n *\n * @return {Void}\n */\n bindSwipeMove: function bindSwipeMove() {\n var _this2 = this;\n\n Binder.on(MOVE_EVENTS, Components.Html.wrapper, throttle(function (event) {\n _this2.move(event);\n }, Glide.settings.throttle), capture);\n },\n\n /**\n * Unbinds swipe's moving event.\n *\n * @return {Void}\n */\n unbindSwipeMove: function unbindSwipeMove() {\n Binder.off(MOVE_EVENTS, Components.Html.wrapper, capture);\n },\n\n /**\n * Binds swipe's ending event.\n *\n * @return {Void}\n */\n bindSwipeEnd: function bindSwipeEnd() {\n var _this3 = this;\n\n Binder.on(END_EVENTS, Components.Html.wrapper, function (event) {\n _this3.end(event);\n });\n },\n\n /**\n * Unbinds swipe's ending event.\n *\n * @return {Void}\n */\n unbindSwipeEnd: function unbindSwipeEnd() {\n Binder.off(END_EVENTS, Components.Html.wrapper);\n },\n\n /**\n * Normalizes event touches points accorting to different types.\n *\n * @param {Object} event\n */\n touches: function touches(event) {\n if (MOUSE_EVENTS.indexOf(event.type) > -1) {\n return event;\n }\n\n return event.touches[0] || event.changedTouches[0];\n },\n\n /**\n * Gets value of minimum swipe distance settings based on event type.\n *\n * @return {Number}\n */\n threshold: function threshold(event) {\n var settings = Glide.settings;\n\n if (MOUSE_EVENTS.indexOf(event.type) > -1) {\n return settings.dragThreshold;\n }\n\n return settings.swipeThreshold;\n },\n\n /**\n * Enables swipe event.\n *\n * @return {self}\n */\n enable: function enable() {\n disabled = false;\n Components.Transition.enable();\n return this;\n },\n\n /**\n * Disables swipe event.\n *\n * @return {self}\n */\n disable: function disable() {\n disabled = true;\n Components.Transition.disable();\n return this;\n }\n };\n /**\n * Add component class:\n * - after initial building\n */\n\n Events.on('build.after', function () {\n Components.Html.root.classList.add(Glide.settings.classes.swipeable);\n });\n /**\n * Remove swiping bindings:\n * - on destroying, to remove added EventListeners\n */\n\n Events.on('destroy', function () {\n Swipe.unbindSwipeStart();\n Swipe.unbindSwipeMove();\n Swipe.unbindSwipeEnd();\n Binder.destroy();\n });\n return Swipe;\n }\n\n function Images (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var Images = {\n /**\n * Binds listener to glide wrapper.\n *\n * @return {Void}\n */\n mount: function mount() {\n this.bind();\n },\n\n /**\n * Binds `dragstart` event on wrapper to prevent dragging images.\n *\n * @return {Void}\n */\n bind: function bind() {\n Binder.on('dragstart', Components.Html.wrapper, this.dragstart);\n },\n\n /**\n * Unbinds `dragstart` event on wrapper.\n *\n * @return {Void}\n */\n unbind: function unbind() {\n Binder.off('dragstart', Components.Html.wrapper);\n },\n\n /**\n * Event handler. Prevents dragging.\n *\n * @return {Void}\n */\n dragstart: function dragstart(event) {\n event.preventDefault();\n }\n };\n /**\n * Remove bindings from images:\n * - on destroying, to remove added EventListeners\n */\n\n Events.on('destroy', function () {\n Images.unbind();\n Binder.destroy();\n });\n return Images;\n }\n\n function Anchors (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n /**\n * Holds detaching status of anchors.\n * Prevents detaching of already detached anchors.\n *\n * @private\n * @type {Boolean}\n */\n\n var detached = false;\n /**\n * Holds preventing status of anchors.\n * If `true` redirection after click will be disabled.\n *\n * @private\n * @type {Boolean}\n */\n\n var prevented = false;\n var Anchors = {\n /**\n * Setups a initial state of anchors component.\n *\n * @returns {Void}\n */\n mount: function mount() {\n /**\n * Holds collection of anchors elements.\n *\n * @private\n * @type {HTMLCollection}\n */\n this._a = Components.Html.wrapper.querySelectorAll('a');\n this.bind();\n },\n\n /**\n * Binds events to anchors inside a track.\n *\n * @return {Void}\n */\n bind: function bind() {\n Binder.on('click', Components.Html.wrapper, this.click);\n },\n\n /**\n * Unbinds events attached to anchors inside a track.\n *\n * @return {Void}\n */\n unbind: function unbind() {\n Binder.off('click', Components.Html.wrapper);\n },\n\n /**\n * Handler for click event. Prevents clicks when glide is in `prevent` status.\n *\n * @param {Object} event\n * @return {Void}\n */\n click: function click(event) {\n if (prevented) {\n event.stopPropagation();\n event.preventDefault();\n }\n },\n\n /**\n * Detaches anchors click event inside glide.\n *\n * @return {self}\n */\n detach: function detach() {\n prevented = true;\n\n if (!detached) {\n for (var i = 0; i < this.items.length; i++) {\n this.items[i].draggable = false;\n }\n\n detached = true;\n }\n\n return this;\n },\n\n /**\n * Attaches anchors click events inside glide.\n *\n * @return {self}\n */\n attach: function attach() {\n prevented = false;\n\n if (detached) {\n for (var i = 0; i < this.items.length; i++) {\n this.items[i].draggable = true;\n }\n\n detached = false;\n }\n\n return this;\n }\n };\n define(Anchors, 'items', {\n /**\n * Gets collection of the arrows HTML elements.\n *\n * @return {HTMLElement[]}\n */\n get: function get() {\n return Anchors._a;\n }\n });\n /**\n * Detach anchors inside slides:\n * - on swiping, so they won't redirect to its `href` attributes\n */\n\n Events.on('swipe.move', function () {\n Anchors.detach();\n });\n /**\n * Attach anchors inside slides:\n * - after swiping and transitions ends, so they can redirect after click again\n */\n\n Events.on('swipe.end', function () {\n Components.Transition.after(function () {\n Anchors.attach();\n });\n });\n /**\n * Unbind anchors inside slides:\n * - on destroying, to bring anchors to its initial state\n */\n\n Events.on('destroy', function () {\n Anchors.attach();\n Anchors.unbind();\n Binder.destroy();\n });\n return Anchors;\n }\n\n var NAV_SELECTOR = '[data-glide-el=\"controls[nav]\"]';\n var CONTROLS_SELECTOR = '[data-glide-el^=\"controls\"]';\n var PREVIOUS_CONTROLS_SELECTOR = \"\".concat(CONTROLS_SELECTOR, \" [data-glide-dir*=\\\"<\\\"]\");\n var NEXT_CONTROLS_SELECTOR = \"\".concat(CONTROLS_SELECTOR, \" [data-glide-dir*=\\\">\\\"]\");\n function Controls (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var capture = supportsPassive$1 ? {\n passive: true\n } : false;\n var Controls = {\n /**\n * Inits arrows. Binds events listeners\n * to the arrows HTML elements.\n *\n * @return {Void}\n */\n mount: function mount() {\n /**\n * Collection of navigation HTML elements.\n *\n * @private\n * @type {HTMLCollection}\n */\n this._n = Components.Html.root.querySelectorAll(NAV_SELECTOR);\n /**\n * Collection of controls HTML elements.\n *\n * @private\n * @type {HTMLCollection}\n */\n\n this._c = Components.Html.root.querySelectorAll(CONTROLS_SELECTOR);\n /**\n * Collection of arrow control HTML elements.\n *\n * @private\n * @type {Object}\n */\n\n this._arrowControls = {\n previous: Components.Html.root.querySelectorAll(PREVIOUS_CONTROLS_SELECTOR),\n next: Components.Html.root.querySelectorAll(NEXT_CONTROLS_SELECTOR)\n };\n this.addBindings();\n },\n\n /**\n * Sets active class to current slide.\n *\n * @return {Void}\n */\n setActive: function setActive() {\n for (var i = 0; i < this._n.length; i++) {\n this.addClass(this._n[i].children);\n }\n },\n\n /**\n * Removes active class to current slide.\n *\n * @return {Void}\n */\n removeActive: function removeActive() {\n for (var i = 0; i < this._n.length; i++) {\n this.removeClass(this._n[i].children);\n }\n },\n\n /**\n * Toggles active class on items inside navigation.\n *\n * @param {HTMLElement} controls\n * @return {Void}\n */\n addClass: function addClass(controls) {\n var settings = Glide.settings;\n var item = controls[Glide.index];\n\n if (!item) {\n return;\n }\n\n if (item) {\n item.classList.add(settings.classes.nav.active);\n siblings(item).forEach(function (sibling) {\n sibling.classList.remove(settings.classes.nav.active);\n });\n }\n },\n\n /**\n * Removes active class from active control.\n *\n * @param {HTMLElement} controls\n * @return {Void}\n */\n removeClass: function removeClass(controls) {\n var item = controls[Glide.index];\n\n if (item) {\n item.classList.remove(Glide.settings.classes.nav.active);\n }\n },\n\n /**\n * Calculates, removes or adds `Glide.settings.classes.disabledArrow` class on the control arrows\n */\n setArrowState: function setArrowState() {\n if (Glide.settings.rewind) {\n return;\n }\n\n var next = Controls._arrowControls.next;\n var previous = Controls._arrowControls.previous;\n this.resetArrowState(next, previous);\n\n if (Glide.index === 0) {\n this.disableArrow(previous);\n }\n\n if (Glide.index === Components.Run.length) {\n this.disableArrow(next);\n }\n },\n\n /**\n * Removes `Glide.settings.classes.disabledArrow` from given NodeList elements\n *\n * @param {NodeList[]} lists\n */\n resetArrowState: function resetArrowState() {\n var settings = Glide.settings;\n\n for (var _len = arguments.length, lists = new Array(_len), _key = 0; _key < _len; _key++) {\n lists[_key] = arguments[_key];\n }\n\n lists.forEach(function (list) {\n toArray(list).forEach(function (element) {\n element.classList.remove(settings.classes.arrow.disabled);\n });\n });\n },\n\n /**\n * Adds `Glide.settings.classes.disabledArrow` to given NodeList elements\n *\n * @param {NodeList[]} lists\n */\n disableArrow: function disableArrow() {\n var settings = Glide.settings;\n\n for (var _len2 = arguments.length, lists = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n lists[_key2] = arguments[_key2];\n }\n\n lists.forEach(function (list) {\n toArray(list).forEach(function (element) {\n element.classList.add(settings.classes.arrow.disabled);\n });\n });\n },\n\n /**\n * Adds handles to the each group of controls.\n *\n * @return {Void}\n */\n addBindings: function addBindings() {\n for (var i = 0; i < this._c.length; i++) {\n this.bind(this._c[i].children);\n }\n },\n\n /**\n * Removes handles from the each group of controls.\n *\n * @return {Void}\n */\n removeBindings: function removeBindings() {\n for (var i = 0; i < this._c.length; i++) {\n this.unbind(this._c[i].children);\n }\n },\n\n /**\n * Binds events to arrows HTML elements.\n *\n * @param {HTMLCollection} elements\n * @return {Void}\n */\n bind: function bind(elements) {\n for (var i = 0; i < elements.length; i++) {\n Binder.on('click', elements[i], this.click);\n Binder.on('touchstart', elements[i], this.click, capture);\n }\n },\n\n /**\n * Unbinds events binded to the arrows HTML elements.\n *\n * @param {HTMLCollection} elements\n * @return {Void}\n */\n unbind: function unbind(elements) {\n for (var i = 0; i < elements.length; i++) {\n Binder.off(['click', 'touchstart'], elements[i]);\n }\n },\n\n /**\n * Handles `click` event on the arrows HTML elements.\n * Moves slider in direction given via the\n * `data-glide-dir` attribute.\n *\n * @param {Object} event\n * @return {void}\n */\n click: function click(event) {\n if (!supportsPassive$1 && event.type === 'touchstart') {\n event.preventDefault();\n }\n\n var direction = event.currentTarget.getAttribute('data-glide-dir');\n Components.Run.make(Components.Direction.resolve(direction));\n }\n };\n define(Controls, 'items', {\n /**\n * Gets collection of the controls HTML elements.\n *\n * @return {HTMLElement[]}\n */\n get: function get() {\n return Controls._c;\n }\n });\n /**\n * Swap active class of current navigation item:\n * - after mounting to set it to initial index\n * - after each move to the new index\n */\n\n Events.on(['mount.after', 'move.after'], function () {\n Controls.setActive();\n });\n /**\n * Add or remove disabled class of arrow elements\n */\n\n Events.on(['mount.after', 'run'], function () {\n Controls.setArrowState();\n });\n /**\n * Remove bindings and HTML Classes:\n * - on destroying, to bring markup to its initial state\n */\n\n Events.on('destroy', function () {\n Controls.removeBindings();\n Controls.removeActive();\n Binder.destroy();\n });\n return Controls;\n }\n\n function Keyboard (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var Keyboard = {\n /**\n * Binds keyboard events on component mount.\n *\n * @return {Void}\n */\n mount: function mount() {\n if (Glide.settings.keyboard) {\n this.bind();\n }\n },\n\n /**\n * Adds keyboard press events.\n *\n * @return {Void}\n */\n bind: function bind() {\n Binder.on('keyup', document, this.press);\n },\n\n /**\n * Removes keyboard press events.\n *\n * @return {Void}\n */\n unbind: function unbind() {\n Binder.off('keyup', document);\n },\n\n /**\n * Handles keyboard's arrows press and moving glide foward and backward.\n *\n * @param {Object} event\n * @return {Void}\n */\n press: function press(event) {\n var perSwipe = Glide.settings.perSwipe;\n\n if (event.code === 'ArrowRight') {\n Components.Run.make(Components.Direction.resolve(\"\".concat(perSwipe, \">\")));\n }\n\n if (event.code === 'ArrowLeft') {\n Components.Run.make(Components.Direction.resolve(\"\".concat(perSwipe, \"<\")));\n }\n }\n };\n /**\n * Remove bindings from keyboard:\n * - on destroying to remove added events\n * - on updating to remove events before remounting\n */\n\n Events.on(['destroy', 'update'], function () {\n Keyboard.unbind();\n });\n /**\n * Remount component\n * - on updating to reflect potential changes in settings\n */\n\n Events.on('update', function () {\n Keyboard.mount();\n });\n /**\n * Destroy binder:\n * - on destroying to remove listeners\n */\n\n Events.on('destroy', function () {\n Binder.destroy();\n });\n return Keyboard;\n }\n\n function Autoplay (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n var Autoplay = {\n /**\n * Initializes autoplaying and events.\n *\n * @return {Void}\n */\n mount: function mount() {\n this.enable();\n this.start();\n\n if (Glide.settings.hoverpause) {\n this.bind();\n }\n },\n\n /**\n * Enables autoplaying\n *\n * @returns {Void}\n */\n enable: function enable() {\n this._e = true;\n },\n\n /**\n * Disables autoplaying.\n *\n * @returns {Void}\n */\n disable: function disable() {\n this._e = false;\n },\n\n /**\n * Starts autoplaying in configured interval.\n *\n * @param {Boolean|Number} force Run autoplaying with passed interval regardless of `autoplay` settings\n * @return {Void}\n */\n start: function start() {\n var _this = this;\n\n if (!this._e) {\n return;\n }\n\n this.enable();\n\n if (Glide.settings.autoplay) {\n if (isUndefined(this._i)) {\n this._i = setInterval(function () {\n _this.stop();\n\n Components.Run.make('>');\n\n _this.start();\n\n Events.emit('autoplay');\n }, this.time);\n }\n }\n },\n\n /**\n * Stops autorunning of the glide.\n *\n * @return {Void}\n */\n stop: function stop() {\n this._i = clearInterval(this._i);\n },\n\n /**\n * Stops autoplaying while mouse is over glide's area.\n *\n * @return {Void}\n */\n bind: function bind() {\n var _this2 = this;\n\n Binder.on('mouseover', Components.Html.root, function () {\n if (_this2._e) {\n _this2.stop();\n }\n });\n Binder.on('mouseout', Components.Html.root, function () {\n if (_this2._e) {\n _this2.start();\n }\n });\n },\n\n /**\n * Unbind mouseover events.\n *\n * @returns {Void}\n */\n unbind: function unbind() {\n Binder.off(['mouseover', 'mouseout'], Components.Html.root);\n }\n };\n define(Autoplay, 'time', {\n /**\n * Gets time period value for the autoplay interval. Prioritizes\n * times in `data-glide-autoplay` attrubutes over options.\n *\n * @return {Number}\n */\n get: function get() {\n var autoplay = Components.Html.slides[Glide.index].getAttribute('data-glide-autoplay');\n\n if (autoplay) {\n return toInt(autoplay);\n }\n\n return toInt(Glide.settings.autoplay);\n }\n });\n /**\n * Stop autoplaying and unbind events:\n * - on destroying, to clear defined interval\n * - on updating via API to reset interval that may changed\n */\n\n Events.on(['destroy', 'update'], function () {\n Autoplay.unbind();\n });\n /**\n * Stop autoplaying:\n * - before each run, to restart autoplaying\n * - on pausing via API\n * - on destroying, to clear defined interval\n * - while starting a swipe\n * - on updating via API to reset interval that may changed\n */\n\n Events.on(['run.before', 'swipe.start', 'update'], function () {\n Autoplay.stop();\n });\n Events.on(['pause', 'destroy'], function () {\n Autoplay.disable();\n Autoplay.stop();\n });\n /**\n * Start autoplaying:\n * - after each run, to restart autoplaying\n * - on playing via API\n * - while ending a swipe\n */\n\n Events.on(['run.after', 'swipe.end'], function () {\n Autoplay.start();\n });\n /**\n * Start autoplaying:\n * - after each run, to restart autoplaying\n * - on playing via API\n * - while ending a swipe\n */\n\n Events.on(['play'], function () {\n Autoplay.enable();\n Autoplay.start();\n });\n /**\n * Remount autoplaying:\n * - on updating via API to reset interval that may changed\n */\n\n Events.on('update', function () {\n Autoplay.mount();\n });\n /**\n * Destroy a binder:\n * - on destroying glide instance to clearup listeners\n */\n\n Events.on('destroy', function () {\n Binder.destroy();\n });\n return Autoplay;\n }\n\n /**\n * Sorts keys of breakpoint object so they will be ordered from lower to bigger.\n *\n * @param {Object} points\n * @returns {Object}\n */\n\n function sortBreakpoints(points) {\n if (isObject(points)) {\n return sortKeys(points);\n } else {\n warn(\"Breakpoints option must be an object\");\n }\n\n return {};\n }\n\n function Breakpoints (Glide, Components, Events) {\n /**\n * Instance of the binder for DOM Events.\n *\n * @type {EventsBinder}\n */\n var Binder = new EventsBinder();\n /**\n * Holds reference to settings.\n *\n * @type {Object}\n */\n\n var settings = Glide.settings;\n /**\n * Holds reference to breakpoints object in settings. Sorts breakpoints\n * from smaller to larger. It is required in order to proper\n * matching currently active breakpoint settings.\n *\n * @type {Object}\n */\n\n var points = sortBreakpoints(settings.breakpoints);\n /**\n * Cache initial settings before overwritting.\n *\n * @type {Object}\n */\n\n var defaults = Object.assign({}, settings);\n var Breakpoints = {\n /**\n * Matches settings for currectly matching media breakpoint.\n *\n * @param {Object} points\n * @returns {Object}\n */\n match: function match(points) {\n if (typeof window.matchMedia !== 'undefined') {\n for (var point in points) {\n if (points.hasOwnProperty(point)) {\n if (window.matchMedia(\"(max-width: \".concat(point, \"px)\")).matches) {\n return points[point];\n }\n }\n }\n }\n\n return defaults;\n }\n };\n /**\n * Overwrite instance settings with currently matching breakpoint settings.\n * This happens right after component initialization.\n */\n\n Object.assign(settings, Breakpoints.match(points));\n /**\n * Update glide with settings of matched brekpoint:\n * - window resize to update slider\n */\n\n Binder.on('resize', window, throttle(function () {\n Glide.settings = mergeOptions(settings, Breakpoints.match(points));\n }, Glide.settings.throttle));\n /**\n * Resort and update default settings:\n * - on reinit via API, so breakpoint matching will be performed with options\n */\n\n Events.on('update', function () {\n points = sortBreakpoints(points);\n defaults = Object.assign({}, settings);\n });\n /**\n * Unbind resize listener:\n * - on destroying, to bring markup to its initial state\n */\n\n Events.on('destroy', function () {\n Binder.off('resize', window);\n });\n return Breakpoints;\n }\n\n var COMPONENTS = {\n // Required\n Html: Html,\n Translate: Translate,\n Transition: Transition,\n Direction: Direction,\n Peek: Peek,\n Sizes: Sizes,\n Gaps: Gaps,\n Move: Move,\n Clones: Clones,\n Resize: Resize,\n Build: Build,\n Run: Run,\n // Optional\n Swipe: Swipe,\n Images: Images,\n Anchors: Anchors,\n Controls: Controls,\n Keyboard: Keyboard,\n Autoplay: Autoplay,\n Breakpoints: Breakpoints\n };\n\n var Glide = /*#__PURE__*/function (_Core) {\n _inherits(Glide, _Core);\n\n var _super = _createSuper(Glide);\n\n function Glide() {\n _classCallCheck(this, Glide);\n\n return _super.apply(this, arguments);\n }\n\n _createClass(Glide, [{\n key: \"mount\",\n value: function mount() {\n var extensions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _get(_getPrototypeOf(Glide.prototype), \"mount\", this).call(this, Object.assign({}, COMPONENTS, extensions));\n }\n }]);\n\n return Glide;\n }(Glide$1);\n\n return Glide;\n\n}));\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Selector = {\n DISMISS : '[data-dismiss=\"alert\"]'\n}\n\nconst Event = {\n CLOSE : `close${EVENT_KEY}`,\n CLOSED : `closed${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n ALERT : 'alert',\n FADE : 'fade',\n SHOW : 'show'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n close(element) {\n let rootElement = this._element\n if (element) {\n rootElement = this._getRootElement(element)\n }\n\n const customEvent = this._triggerCloseEvent(rootElement)\n\n if (customEvent.isDefaultPrevented()) {\n return\n }\n\n this._removeElement(rootElement)\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Private\n\n _getRootElement(element) {\n const selector = Util.getSelectorFromElement(element)\n let parent = false\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n if (!parent) {\n parent = $(element).closest(`.${ClassName.ALERT}`)[0]\n }\n\n return parent\n }\n\n _triggerCloseEvent(element) {\n const closeEvent = $.Event(Event.CLOSE)\n\n $(element).trigger(closeEvent)\n return closeEvent\n }\n\n _removeElement(element) {\n $(element).removeClass(ClassName.SHOW)\n\n if (!$(element).hasClass(ClassName.FADE)) {\n this._destroyElement(element)\n return\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(element)\n\n $(element)\n .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))\n .emulateTransitionEnd(transitionDuration)\n }\n\n _destroyElement(element) {\n $(element)\n .detach()\n .trigger(Event.CLOSED)\n .remove()\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $element = $(this)\n let data = $element.data(DATA_KEY)\n\n if (!data) {\n data = new Alert(this)\n $element.data(DATA_KEY, data)\n }\n\n if (config === 'close') {\n data[config](this)\n }\n })\n }\n\n static _handleDismiss(alertInstance) {\n return function (event) {\n if (event) {\n event.preventDefault()\n }\n\n alertInstance.close(this)\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(\n Event.CLICK_DATA_API,\n Selector.DISMISS,\n Alert._handleDismiss(new Alert())\n)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Alert._jQueryInterface\n$.fn[NAME].Constructor = Alert\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Alert._jQueryInterface\n}\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key\nconst ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval : 5000,\n keyboard : true,\n slide : false,\n pause : 'hover',\n wrap : true,\n touch : true\n}\n\nconst DefaultType = {\n interval : '(number|boolean)',\n keyboard : 'boolean',\n slide : '(boolean|string)',\n pause : '(string|boolean)',\n wrap : 'boolean',\n touch : 'boolean'\n}\n\nconst Direction = {\n NEXT : 'next',\n PREV : 'prev',\n LEFT : 'left',\n RIGHT : 'right'\n}\n\nconst Event = {\n SLIDE : `slide${EVENT_KEY}`,\n SLID : `slid${EVENT_KEY}`,\n KEYDOWN : `keydown${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`,\n TOUCHSTART : `touchstart${EVENT_KEY}`,\n TOUCHMOVE : `touchmove${EVENT_KEY}`,\n TOUCHEND : `touchend${EVENT_KEY}`,\n POINTERDOWN : `pointerdown${EVENT_KEY}`,\n POINTERUP : `pointerup${EVENT_KEY}`,\n DRAG_START : `dragstart${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n CAROUSEL : 'carousel',\n ACTIVE : 'active',\n SLIDE : 'slide',\n RIGHT : 'carousel-item-right',\n LEFT : 'carousel-item-left',\n NEXT : 'carousel-item-next',\n PREV : 'carousel-item-prev',\n ITEM : 'carousel-item',\n POINTER_EVENT : 'pointer-event'\n}\n\nconst Selector = {\n ACTIVE : '.active',\n ACTIVE_ITEM : '.active.carousel-item',\n ITEM : '.carousel-item',\n ITEM_IMG : '.carousel-item img',\n NEXT_PREV : '.carousel-item-next, .carousel-item-prev',\n INDICATORS : '.carousel-indicators',\n DATA_SLIDE : '[data-slide], [data-slide-to]',\n DATA_RIDE : '[data-ride=\"carousel\"]'\n}\n\nconst PointerType = {\n TOUCH : 'touch',\n PEN : 'pen'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel {\n constructor(element, config) {\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._element = element\n this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n next() {\n if (!this._isSliding) {\n this._slide(Direction.NEXT)\n }\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden &&\n ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {\n this.next()\n }\n }\n\n prev() {\n if (!this._isSliding) {\n this._slide(Direction.PREV)\n }\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (this._element.querySelector(Selector.NEXT_PREV)) {\n Util.triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config.interval && !this._isPaused) {\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n $(this._element).one(Event.SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const direction = index > activeIndex\n ? Direction.NEXT\n : Direction.PREV\n\n this._slide(direction, this._items[index])\n }\n\n dispose() {\n $(this._element).off(EVENT_KEY)\n $.removeData(this._element, DATA_KEY)\n\n this._items = null\n this._config = null\n this._element = null\n this._interval = null\n this._isPaused = null\n this._isSliding = null\n this._activeElement = null\n this._indicatorsElement = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n // swipe left\n if (direction > 0) {\n this.prev()\n }\n\n // swipe right\n if (direction < 0) {\n this.next()\n }\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n $(this._element)\n .on(Event.KEYDOWN, (event) => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n $(this._element)\n .on(Event.MOUSEENTER, (event) => this.pause(event))\n .on(Event.MOUSELEAVE, (event) => this.cycle(event))\n }\n\n if (this._config.touch) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n if (!this._touchSupported) {\n return\n }\n\n const start = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchStartX = event.originalEvent.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.originalEvent.touches[0].clientX\n }\n }\n\n const move = (event) => {\n // ensure swiping with one touch and not pinching\n if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {\n this.touchDeltaX = 0\n } else {\n this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX\n }\n }\n\n const end = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchDeltaX = event.originalEvent.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n $(this._element.querySelectorAll(Selector.ITEM_IMG)).on(Event.DRAG_START, (e) => e.preventDefault())\n if (this._pointerEvent) {\n $(this._element).on(Event.POINTERDOWN, (event) => start(event))\n $(this._element).on(Event.POINTERUP, (event) => end(event))\n\n this._element.classList.add(ClassName.POINTER_EVENT)\n } else {\n $(this._element).on(Event.TOUCHSTART, (event) => start(event))\n $(this._element).on(Event.TOUCHMOVE, (event) => move(event))\n $(this._element).on(Event.TOUCHEND, (event) => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n switch (event.which) {\n case ARROW_LEFT_KEYCODE:\n event.preventDefault()\n this.prev()\n break\n case ARROW_RIGHT_KEYCODE:\n event.preventDefault()\n this.next()\n break\n default:\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode\n ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))\n : []\n return this._items.indexOf(element)\n }\n\n _getItemByDirection(direction, activeElement) {\n const isNextDirection = direction === Direction.NEXT\n const isPrevDirection = direction === Direction.PREV\n const activeIndex = this._getItemIndex(activeElement)\n const lastItemIndex = this._items.length - 1\n const isGoingToWrap = isPrevDirection && activeIndex === 0 ||\n isNextDirection && activeIndex === lastItemIndex\n\n if (isGoingToWrap && !this._config.wrap) {\n return activeElement\n }\n\n const delta = direction === Direction.PREV ? -1 : 1\n const itemIndex = (activeIndex + delta) % this._items.length\n\n return itemIndex === -1\n ? this._items[this._items.length - 1] : this._items[itemIndex]\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))\n const slideEvent = $.Event(Event.SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n\n $(this._element).trigger(slideEvent)\n\n return slideEvent\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))\n $(indicators)\n .removeClass(ClassName.ACTIVE)\n\n const nextIndicator = this._indicatorsElement.children[\n this._getItemIndex(element)\n ]\n\n if (nextIndicator) {\n $(nextIndicator).addClass(ClassName.ACTIVE)\n }\n }\n }\n\n _slide(direction, element) {\n const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || activeElement &&\n this._getItemByDirection(direction, activeElement)\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n let directionalClassName\n let orderClassName\n let eventDirectionName\n\n if (direction === Direction.NEXT) {\n directionalClassName = ClassName.LEFT\n orderClassName = ClassName.NEXT\n eventDirectionName = Direction.LEFT\n } else {\n directionalClassName = ClassName.RIGHT\n orderClassName = ClassName.PREV\n eventDirectionName = Direction.RIGHT\n }\n\n if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {\n this._isSliding = false\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.isDefaultPrevented()) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n\n const slidEvent = $.Event(Event.SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n\n if ($(this._element).hasClass(ClassName.SLIDE)) {\n $(nextElement).addClass(orderClassName)\n\n Util.reflow(nextElement)\n\n $(activeElement).addClass(directionalClassName)\n $(nextElement).addClass(directionalClassName)\n\n const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)\n if (nextElementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = nextElementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(activeElement)\n\n $(activeElement)\n .one(Util.TRANSITION_END, () => {\n $(nextElement)\n .removeClass(`${directionalClassName} ${orderClassName}`)\n .addClass(ClassName.ACTIVE)\n\n $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)\n\n this._isSliding = false\n\n setTimeout(() => $(this._element).trigger(slidEvent), 0)\n })\n .emulateTransitionEnd(transitionDuration)\n } else {\n $(activeElement).removeClass(ClassName.ACTIVE)\n $(nextElement).addClass(ClassName.ACTIVE)\n\n this._isSliding = false\n $(this._element).trigger(slidEvent)\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n let _config = {\n ...Default,\n ...$(this).data()\n }\n\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (!data) {\n data = new Carousel(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n })\n }\n\n static _dataApiClickHandler(event) {\n const selector = Util.getSelectorFromElement(this)\n\n if (!selector) {\n return\n }\n\n const target = $(selector)[0]\n\n if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {\n return\n }\n\n const config = {\n ...$(target).data(),\n ...$(this).data()\n }\n const slideIndex = this.getAttribute('data-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel._jQueryInterface.call($(target), config)\n\n if (slideIndex) {\n $(target).data(DATA_KEY).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)\n\n$(window).on(Event.LOAD_DATA_API, () => {\n const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))\n for (let i = 0, len = carousels.length; i < len; i++) {\n const $carousel = $(carousels[i])\n Carousel._jQueryInterface.call($carousel, $carousel.data())\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Carousel._jQueryInterface\n$.fn[NAME].Constructor = Carousel\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Carousel._jQueryInterface\n}\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n toggle : true,\n parent : ''\n}\n\nconst DefaultType = {\n toggle : 'boolean',\n parent : '(string|element)'\n}\n\nconst Event = {\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SHOW : 'show',\n COLLAPSE : 'collapse',\n COLLAPSING : 'collapsing',\n COLLAPSED : 'collapsed'\n}\n\nconst Dimension = {\n WIDTH : 'width',\n HEIGHT : 'height'\n}\n\nconst Selector = {\n ACTIVES : '.show, .collapsing',\n DATA_TOGGLE : '[data-toggle=\"collapse\"]'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse {\n constructor(element, config) {\n this._isTransitioning = false\n this._element = element\n this._config = this._getConfig(config)\n this._triggerArray = [].slice.call(document.querySelectorAll(\n `[data-toggle=\"collapse\"][href=\"#${element.id}\"],` +\n `[data-toggle=\"collapse\"][data-target=\"#${element.id}\"]`\n ))\n\n const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = Util.getSelectorFromElement(elem)\n const filterElement = [].slice.call(document.querySelectorAll(selector))\n .filter((foundElem) => foundElem === element)\n\n if (selector !== null && filterElement.length > 0) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._parent = this._config.parent ? this._getParent() : null\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._element, this._triggerArray)\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle() {\n if ($(this._element).hasClass(ClassName.SHOW)) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning ||\n $(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n let actives\n let activesData\n\n if (this._parent) {\n actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))\n .filter((elem) => {\n if (typeof this._config.parent === 'string') {\n return elem.getAttribute('data-parent') === this._config.parent\n }\n\n return elem.classList.contains(ClassName.COLLAPSE)\n })\n\n if (actives.length === 0) {\n actives = null\n }\n }\n\n if (actives) {\n activesData = $(actives).not(this._selector).data(DATA_KEY)\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = $.Event(Event.SHOW)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n if (actives) {\n Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')\n if (!activesData) {\n $(actives).data(DATA_KEY, null)\n }\n }\n\n const dimension = this._getDimension()\n\n $(this._element)\n .removeClass(ClassName.COLLAPSE)\n .addClass(ClassName.COLLAPSING)\n\n this._element.style[dimension] = 0\n\n if (this._triggerArray.length) {\n $(this._triggerArray)\n .removeClass(ClassName.COLLAPSED)\n .attr('aria-expanded', true)\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .addClass(ClassName.SHOW)\n\n this._element.style[dimension] = ''\n\n this.setTransitioning(false)\n\n $(this._element).trigger(Event.SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning ||\n !$(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n const startEvent = $.Event(Event.HIDE)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n Util.reflow(this._element)\n\n $(this._element)\n .addClass(ClassName.COLLAPSING)\n .removeClass(ClassName.COLLAPSE)\n .removeClass(ClassName.SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n if (triggerArrayLength > 0) {\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const selector = Util.getSelectorFromElement(trigger)\n\n if (selector !== null) {\n const $elem = $([].slice.call(document.querySelectorAll(selector)))\n if (!$elem.hasClass(ClassName.SHOW)) {\n $(trigger).addClass(ClassName.COLLAPSED)\n .attr('aria-expanded', false)\n }\n }\n }\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n this.setTransitioning(false)\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .trigger(Event.HIDDEN)\n }\n\n this._element.style[dimension] = ''\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n }\n\n setTransitioning(isTransitioning) {\n this._isTransitioning = isTransitioning\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._parent = null\n this._element = null\n this._triggerArray = null\n this._isTransitioning = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n const hasWidth = $(this._element).hasClass(Dimension.WIDTH)\n return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT\n }\n\n _getParent() {\n let parent\n\n if (Util.isElement(this._config.parent)) {\n parent = this._config.parent\n\n // It's a jQuery object\n if (typeof this._config.parent.jquery !== 'undefined') {\n parent = this._config.parent[0]\n }\n } else {\n parent = document.querySelector(this._config.parent)\n }\n\n const selector =\n `[data-toggle=\"collapse\"][data-parent=\"${this._config.parent}\"]`\n\n const children = [].slice.call(parent.querySelectorAll(selector))\n $(children).each((i, element) => {\n this._addAriaAndCollapsedClass(\n Collapse._getTargetFromElement(element),\n [element]\n )\n })\n\n return parent\n }\n\n _addAriaAndCollapsedClass(element, triggerArray) {\n const isOpen = $(element).hasClass(ClassName.SHOW)\n\n if (triggerArray.length) {\n $(triggerArray)\n .toggleClass(ClassName.COLLAPSED, !isOpen)\n .attr('aria-expanded', isOpen)\n }\n }\n\n // Static\n\n static _getTargetFromElement(element) {\n const selector = Util.getSelectorFromElement(element)\n return selector ? document.querySelector(selector) : null\n }\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $this = $(this)\n let data = $this.data(DATA_KEY)\n const _config = {\n ...Default,\n ...$this.data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data && _config.toggle && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n if (!data) {\n data = new Collapse(this, _config)\n $this.data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.currentTarget.tagName === 'A') {\n event.preventDefault()\n }\n\n const $trigger = $(this)\n const selector = Util.getSelectorFromElement(this)\n const selectors = [].slice.call(document.querySelectorAll(selector))\n\n $(selectors).each(function () {\n const $target = $(this)\n const data = $target.data(DATA_KEY)\n const config = data ? 'toggle' : $trigger.data()\n Collapse._jQueryInterface.call($target, config)\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Collapse._jQueryInterface\n$.fn[NAME].Constructor = Collapse\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Collapse._jQueryInterface\n}\n\nexport default Collapse\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n\nconst Default = {\n backdrop : true,\n keyboard : true,\n focus : true,\n show : true\n}\n\nconst DefaultType = {\n backdrop : '(boolean|string)',\n keyboard : 'boolean',\n focus : 'boolean',\n show : 'boolean'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n RESIZE : `resize${EVENT_KEY}`,\n CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,\n KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,\n MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,\n MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SCROLLABLE : 'modal-dialog-scrollable',\n SCROLLBAR_MEASURER : 'modal-scrollbar-measure',\n BACKDROP : 'modal-backdrop',\n OPEN : 'modal-open',\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n DIALOG : '.modal-dialog',\n MODAL_BODY : '.modal-body',\n DATA_TOGGLE : '[data-toggle=\"modal\"]',\n DATA_DISMISS : '[data-dismiss=\"modal\"]',\n FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',\n STICKY_CONTENT : '.sticky-top'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal {\n constructor(element, config) {\n this._config = this._getConfig(config)\n this._element = element\n this._dialog = element.querySelector(Selector.DIALOG)\n this._backdrop = null\n this._isShown = false\n this._isBodyOverflowing = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollbarWidth = 0\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n this._isTransitioning = true\n }\n\n const showEvent = $.Event(Event.SHOW, {\n relatedTarget\n })\n\n $(this._element).trigger(showEvent)\n\n if (this._isShown || showEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = true\n\n this._checkScrollbar()\n this._setScrollbar()\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(this._element).on(\n Event.CLICK_DISMISS,\n Selector.DATA_DISMISS,\n (event) => this.hide(event)\n )\n\n $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {\n $(this._element).one(Event.MOUSEUP_DISMISS, (event) => {\n if ($(event.target).is(this._element)) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide(event) {\n if (event) {\n event.preventDefault()\n }\n\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = $.Event(Event.HIDE)\n\n $(this._element).trigger(hideEvent)\n\n if (!this._isShown || hideEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = false\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (transition) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(document).off(Event.FOCUSIN)\n\n $(this._element).removeClass(ClassName.SHOW)\n\n $(this._element).off(Event.CLICK_DISMISS)\n $(this._dialog).off(Event.MOUSEDOWN_DISMISS)\n\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, (event) => this._hideModal(event))\n .emulateTransitionEnd(transitionDuration)\n } else {\n this._hideModal()\n }\n }\n\n dispose() {\n [window, this._element, this._dialog]\n .forEach((htmlElement) => $(htmlElement).off(EVENT_KEY))\n\n /**\n * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`\n * Do not move `document` in `htmlElements` array\n * It will remove `Event.CLICK_DATA_API` event that should remain\n */\n $(document).off(Event.FOCUSIN)\n\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._element = null\n this._dialog = null\n this._backdrop = null\n this._isShown = null\n this._isBodyOverflowing = null\n this._ignoreBackdropClick = null\n this._isTransitioning = null\n this._scrollbarWidth = null\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (!this._element.parentNode ||\n this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.appendChild(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n\n if ($(this._dialog).hasClass(ClassName.SCROLLABLE)) {\n this._dialog.querySelector(Selector.MODAL_BODY).scrollTop = 0\n } else {\n this._element.scrollTop = 0\n }\n\n if (transition) {\n Util.reflow(this._element)\n }\n\n $(this._element).addClass(ClassName.SHOW)\n\n if (this._config.focus) {\n this._enforceFocus()\n }\n\n const shownEvent = $.Event(Event.SHOWN, {\n relatedTarget\n })\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._element.focus()\n }\n this._isTransitioning = false\n $(this._element).trigger(shownEvent)\n }\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)\n\n $(this._dialog)\n .one(Util.TRANSITION_END, transitionComplete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n transitionComplete()\n }\n }\n\n _enforceFocus() {\n $(document)\n .off(Event.FOCUSIN) // Guard against infinite focus loop\n .on(Event.FOCUSIN, (event) => {\n if (document !== event.target &&\n this._element !== event.target &&\n $(this._element).has(event.target).length === 0) {\n this._element.focus()\n }\n })\n }\n\n _setEscapeEvent() {\n if (this._isShown && this._config.keyboard) {\n $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {\n if (event.which === ESCAPE_KEYCODE) {\n event.preventDefault()\n this.hide()\n }\n })\n } else if (!this._isShown) {\n $(this._element).off(Event.KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))\n } else {\n $(window).off(Event.RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._isTransitioning = false\n this._showBackdrop(() => {\n $(document.body).removeClass(ClassName.OPEN)\n this._resetAdjustments()\n this._resetScrollbar()\n $(this._element).trigger(Event.HIDDEN)\n })\n }\n\n _removeBackdrop() {\n if (this._backdrop) {\n $(this._backdrop).remove()\n this._backdrop = null\n }\n }\n\n _showBackdrop(callback) {\n const animate = $(this._element).hasClass(ClassName.FADE)\n ? ClassName.FADE : ''\n\n if (this._isShown && this._config.backdrop) {\n this._backdrop = document.createElement('div')\n this._backdrop.className = ClassName.BACKDROP\n\n if (animate) {\n this._backdrop.classList.add(animate)\n }\n\n $(this._backdrop).appendTo(document.body)\n\n $(this._element).on(Event.CLICK_DISMISS, (event) => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n if (event.target !== event.currentTarget) {\n return\n }\n if (this._config.backdrop === 'static') {\n this._element.focus()\n } else {\n this.hide()\n }\n })\n\n if (animate) {\n Util.reflow(this._backdrop)\n }\n\n $(this._backdrop).addClass(ClassName.SHOW)\n\n if (!callback) {\n return\n }\n\n if (!animate) {\n callback()\n return\n }\n\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callback)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else if (!this._isShown && this._backdrop) {\n $(this._backdrop).removeClass(ClassName.SHOW)\n\n const callbackRemove = () => {\n this._removeBackdrop()\n if (callback) {\n callback()\n }\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callbackRemove)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else {\n callbackRemove()\n }\n } else if (callback) {\n callback()\n }\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // todo (fat): these should probably be refactored out of modal.js\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing =\n this._element.scrollHeight > document.documentElement.clientHeight\n\n if (!this._isBodyOverflowing && isModalOverflowing) {\n this._element.style.paddingLeft = `${this._scrollbarWidth}px`\n }\n\n if (this._isBodyOverflowing && !isModalOverflowing) {\n this._element.style.paddingRight = `${this._scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n _checkScrollbar() {\n const rect = document.body.getBoundingClientRect()\n this._isBodyOverflowing = rect.left + rect.right < window.innerWidth\n this._scrollbarWidth = this._getScrollbarWidth()\n }\n\n _setScrollbar() {\n if (this._isBodyOverflowing) {\n // Note: DOMNode.style.paddingRight returns the actual value or '' if not set\n // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n const stickyContent = [].slice.call(document.querySelectorAll(Selector.STICKY_CONTENT))\n\n // Adjust fixed content padding\n $(fixedContent).each((index, element) => {\n const actualPadding = element.style.paddingRight\n const calculatedPadding = $(element).css('padding-right')\n $(element)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n })\n\n // Adjust sticky content margin\n $(stickyContent).each((index, element) => {\n const actualMargin = element.style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element)\n .data('margin-right', actualMargin)\n .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)\n })\n\n // Adjust body padding\n const actualPadding = document.body.style.paddingRight\n const calculatedPadding = $(document.body).css('padding-right')\n $(document.body)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n }\n\n $(document.body).addClass(ClassName.OPEN)\n }\n\n _resetScrollbar() {\n // Restore fixed content padding\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n $(fixedContent).each((index, element) => {\n const padding = $(element).data('padding-right')\n $(element).removeData('padding-right')\n element.style.paddingRight = padding ? padding : ''\n })\n\n // Restore sticky content\n const elements = [].slice.call(document.querySelectorAll(`${Selector.STICKY_CONTENT}`))\n $(elements).each((index, element) => {\n const margin = $(element).data('margin-right')\n if (typeof margin !== 'undefined') {\n $(element).css('margin-right', margin).removeData('margin-right')\n }\n })\n\n // Restore body padding\n const padding = $(document.body).data('padding-right')\n $(document.body).removeData('padding-right')\n document.body.style.paddingRight = padding ? padding : ''\n }\n\n _getScrollbarWidth() { // thx d.walsh\n const scrollDiv = document.createElement('div')\n scrollDiv.className = ClassName.SCROLLBAR_MEASURER\n document.body.appendChild(scrollDiv)\n const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth\n document.body.removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n // Static\n\n static _jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = {\n ...Default,\n ...$(this).data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data) {\n data = new Modal(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config](relatedTarget)\n } else if (_config.show) {\n data.show(relatedTarget)\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n let target\n const selector = Util.getSelectorFromElement(this)\n\n if (selector) {\n target = document.querySelector(selector)\n }\n\n const config = $(target).data(DATA_KEY)\n ? 'toggle' : {\n ...$(target).data(),\n ...$(this).data()\n }\n\n if (this.tagName === 'A' || this.tagName === 'AREA') {\n event.preventDefault()\n }\n\n const $target = $(target).one(Event.SHOW, (showEvent) => {\n if (showEvent.isDefaultPrevented()) {\n // Only register focus restorer if modal will actually get shown\n return\n }\n\n $target.one(Event.HIDDEN, () => {\n if ($(this).is(':visible')) {\n this.focus()\n }\n })\n })\n\n Modal._jQueryInterface.call($(target), config, this)\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Modal._jQueryInterface\n$.fn[NAME].Constructor = Modal\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Modal._jQueryInterface\n}\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n offset : 10,\n method : 'auto',\n target : ''\n}\n\nconst DefaultType = {\n offset : 'number',\n method : 'string',\n target : '(string|element)'\n}\n\nconst Event = {\n ACTIVATE : `activate${EVENT_KEY}`,\n SCROLL : `scroll${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DROPDOWN_ITEM : 'dropdown-item',\n DROPDOWN_MENU : 'dropdown-menu',\n ACTIVE : 'active'\n}\n\nconst Selector = {\n DATA_SPY : '[data-spy=\"scroll\"]',\n ACTIVE : '.active',\n NAV_LIST_GROUP : '.nav, .list-group',\n NAV_LINKS : '.nav-link',\n NAV_ITEMS : '.nav-item',\n LIST_ITEMS : '.list-group-item',\n DROPDOWN : '.dropdown',\n DROPDOWN_ITEMS : '.dropdown-item',\n DROPDOWN_TOGGLE : '.dropdown-toggle'\n}\n\nconst OffsetMethod = {\n OFFSET : 'offset',\n POSITION : 'position'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy {\n constructor(element, config) {\n this._element = element\n this._scrollElement = element.tagName === 'BODY' ? window : element\n this._config = this._getConfig(config)\n this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +\n `${this._config.target} ${Selector.LIST_ITEMS},` +\n `${this._config.target} ${Selector.DROPDOWN_ITEMS}`\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window\n ? OffsetMethod.OFFSET : OffsetMethod.POSITION\n\n const offsetMethod = this._config.method === 'auto'\n ? autoMethod : this._config.method\n\n const offsetBase = offsetMethod === OffsetMethod.POSITION\n ? this._getScrollTop() : 0\n\n this._offsets = []\n this._targets = []\n\n this._scrollHeight = this._getScrollHeight()\n\n const targets = [].slice.call(document.querySelectorAll(this._selector))\n\n targets\n .map((element) => {\n let target\n const targetSelector = Util.getSelectorFromElement(element)\n\n if (targetSelector) {\n target = document.querySelector(targetSelector)\n }\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n // TODO (fat): remove sketch reliance on jQuery position/offset\n return [\n $(target)[offsetMethod]().top + offsetBase,\n targetSelector\n ]\n }\n }\n return null\n })\n .filter((item) => item)\n .sort((a, b) => a[0] - b[0])\n .forEach((item) => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._scrollElement).off(EVENT_KEY)\n\n this._element = null\n this._scrollElement = null\n this._config = null\n this._selector = null\n this._offsets = null\n this._targets = null\n this._activeTarget = null\n this._scrollHeight = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.target !== 'string') {\n let id = $(config.target).attr('id')\n if (!id) {\n id = Util.getUID(NAME)\n $(config.target).attr('id', id)\n }\n config.target = `#${id}`\n }\n\n Util.typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window\n ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window\n ? window.innerHeight : this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset +\n scrollHeight -\n this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n const offsetLength = this._offsets.length\n for (let i = offsetLength; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' ||\n scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = this._selector\n .split(',')\n .map((selector) => `${selector}[data-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))\n\n if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {\n $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)\n $link.addClass(ClassName.ACTIVE)\n } else {\n // Set triggered link as active\n $link.addClass(ClassName.ACTIVE)\n // Set triggered links parents as active\n // With both